Übermitteln einer URB

In diesem Thema werden die Schritte beschrieben, die erforderlich sind, um eine initialisierte URB an den USB-Treiberstapel zu übermitteln, um eine bestimmte Anforderung zu verarbeiten.

Ein Clienttreiber kommuniziert mit seinem Gerät mithilfe von IOCTL-Anforderungen (I/O Control Code), die in I/O-Anforderungspaketen (IRPs) vom Typ IRP_MJ_INTERNAL_DEVICE_CONTROL an das Gerät übermittelt werden. Für eine gerätespezifische Anforderung, z. B. eine Select-Configuration-Anforderung, wird die Anforderung in einem USB-Anforderungsblock (URB) beschrieben, der einem IRP zugeordnet ist. Das Zuordnen einer URB zu einem IRP und das Senden der Anforderung an den USB-Treiberstapel wird als Übermitteln einer URB bezeichnet. Um eine URB zu übermitteln, muss der Clienttreiber IOCTL_INTERNAL_USB_SUBMIT_URB als Gerätesteuerungscode verwenden. Die IOCTL ist einer der "internen" Kontrollcodes, die eine E/A-Schnittstelle bereitstellen, über die ein Clienttreiber sein Gerät und den Port verwaltet, mit dem das Gerät verbunden ist. Benutzermodusanwendungen haben keinen Zugriff auf diese interne E/A-Schnittstelle. Weitere Steuercodes für Kernelmodustreiber finden Sie unter Kernelmodus-IOCTLs für USB-Clienttreiber.

Voraussetzungen

Vor dem Senden einer Anforderung an den USB-Treiberstapel (Universal Serial Bus) muss der Clienttreiber je nach Anforderungstyp eine URB-Struktur und ein Format dieser Struktur zuordnen. Weitere Informationen finden Sie unter Zuordnen und Erstellen von URBs und bewährte Methoden: Verwenden von URBs.

Anweisungen

  1. Ordnen Sie einen IRP für die URB zu, indem Sie die IoAllocateIrp-Routine aufrufen. Sie müssen die Stapelgröße des Geräteobjekts angeben, das den IRP empfängt. Sie haben in einem vorherigen Aufruf der IoAttachDeviceToDeviceStack-Routine einen Zeiger auf dieses Geräteobjekt erhalten. Die Stapelgröße wird im StackSize-Element der DEVICE_OBJECT-Struktur gespeichert.

  2. Rufen Sie einen Zeiger auf den ersten Stapelspeicherort (IO_STACK_LOCATION) des IRP ab, indem Sie IoGetNextIrpStackLocation aufrufen.

  3. Legen Sie den MajorFunction-Member der IO_STACK_LOCATION-Struktur auf IRP_MJ_INTERNAL_DEVICE_CONTROL fest.

  4. Legen Sie den Parameter.DeviceIoControl.IoControlCode-Member der IO_STACK_LOCATION-Struktur auf IOCTL_INTERNAL_USB_SUBMIT_URB fest.

  5. Legen Sie das Parameters.Others.Argument1-Element der IO_STACK_LOCATION-Struktur auf die Adresse der initialisierten URB-Struktur fest. Um den IRP dem URB zuzuordnen, können Sie alternativ USBD_AssignUrbToIoStackLocation nur aufrufen, wenn die URB von USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild oder USBD_SelectInterfaceUrbAllocateAndBuild zugeordnet wurde.

  6. Legen Sie eine VervollständigungsroutineEx durch Aufrufen von IoSetCompletionRoutineEx fest.

    Wenn Sie die URB asynchron übermitteln, übergeben Sie einen Zeiger auf die vom Aufrufer implementierte Vervollständigungsroutine und deren Kontext. Der Aufrufer gibt das IRP in seiner Vervollständigungsroutine frei.

    Wenn Sie das IRP synchron übermitteln, implementieren Sie eine Vervollständigungsroutine, und übergeben Sie einen Zeiger auf diese Routine im Aufruf von IoSetCompletionRoutineEx. Der Aufruf erfordert auch ein initialisiertes KEVENT-Objekt im Context-Parameter . Legen Sie in Ihrer Abschlussroutine das Ereignis auf den signalierten Zustand fest.

  7. Rufen Sie IoCallDriver auf, um den aufgefüllten IRP an das nächstniedrene Geräteobjekt im Gerätestapel weiterzuleiten. Warten Sie bei einem synchronen Aufruf nach dem Aufruf von IoCallDriver auf das Ereignisobjekt, indem Sie KeWaitForSingleObject aufrufen, um die Ereignisbenachrichtigung abzurufen.

  8. Überprüfen Sie nach Abschluss des IRP das IoStatus.Status-Element von IRP, und bewerten Sie das Ergebnis. Wenn ioStatus.Status STATUS_SUCCESS ist, war die Anforderung erfolgreich.

Synchrone USB-Übermittlung

Das folgende Beispiel zeigt, wie Sie eine URB synchron übermitteln.

// The SubmitUrbSync routine submits an URB synchronously.
//
// Parameters:
//      DeviceExtension: Pointer to the caller's device extension. The
//                       device extension must have a pointer to
//                       the next lower device object in the device stacks.  
//
//      Irp: Pointer to an IRP allocated by the caller.
//
//      Urb: Pointer to an URB that is allocated by  USBD_UrbAllocate,
//           USBD_IsochUrbAllocate, USBD_SelectConfigUrbAllocateAndBuild,
//           or USBD_SelectInterfaceUrbAllocateAndBuild.

//      CompletionRoutine: Completion routine.
//
// Return Value:
//
//      NTSTATUS  

NTSTATUS SubmitUrbSync( PDEVICE_EXTENSION DeviceExtension,
                       PIRP Irp,
                       PURB Urb,  
                       PIO_COMPLETION_ROUTINE SyncCompletionRoutine)  

{

    NTSTATUS  ntStatus;  
    KEVENT    kEvent;

    PIO_STACK_LOCATION nextStack;

    // Get the next stack location.
    nextStack = IoGetNextIrpStackLocation(Irp);  

    // Set the major code.
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;  

    // Set the IOCTL code for URB submission.
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;  

    // Attach the URB to this IRP.
    // The URB must be allocated by USBD_UrbAllocate, USBD_IsochUrbAllocate,
    // USBD_SelectConfigUrbAllocateAndBuild, or USBD_SelectInterfaceUrbAllocateAndBuild.
    USBD_AssignUrbToIoStackLocation (DeviceExtension->UsbdHandle, nextStack, Urb);

    KeInitializeEvent(&kEvent, NotificationEvent, FALSE);

    ntStatus = IoSetCompletionRoutineEx ( DeviceExtension->NextDeviceObject,  
        Irp,  
        SyncCompletionRoutine,  
        (PVOID) &kEvent,  
        TRUE,
        TRUE,
        TRUE);

    if (!NT_SUCCESS(ntStatus))
    {
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "IoSetCompletionRoutineEx failed. \n" ));
        goto Exit;
    }

    ntStatus = IoCallDriver(DeviceExtension->NextDeviceObject, Irp);  

    if (ntStatus == STATUS_PENDING)
    {
        KeWaitForSingleObject ( &kEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL);
    }

    ntStatus = Irp->IoStatus.Status;

Exit:

    if (!NT_SUCCESS(ntStatus))
    {
        // We hit a failure condition,
        // We will free the IRP

        IoFreeIrp(Irp);
        Irp = NULL;
    }


    return ntStatus;
}

// The SyncCompletionRoutine routine is the completion routine
// for the synchronous URB submit request.
//
// Parameters:
//
//      DeviceObject: Pointer to the device object.
//      Irp:          Pointer to an I/O Request Packet.
//      CompletionContext: Context for the completion routine.
//
// Return Value:
//
//      NTSTATUS  

NTSTATUS SyncCompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                                PIRP           Irp,
                                PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Request completed. \n" ));


    return STATUS_MORE_PROCESSING_REQUIRED;
}

Asynchrone USB-Übermittlung

Im folgenden Beispiel wird gezeigt, wie Eine URB asynchron übermittelt wird.

// The SubmitUrbASync routine submits an URB asynchronously.
//
// Parameters:
//
// Parameters:
//      DeviceExtension: Pointer to the caller's device extension. The
//                       device extension must have a pointer to
//                       the next lower device object in the device stacks.  
//
//      Irp: Pointer to an IRP allocated by the caller.
//
//      Urb: Pointer to an URB that is allocated by  USBD_UrbAllocate,
//           USBD_IsochUrbAllocate, USBD_SelectConfigUrbAllocateAndBuild,
//           or USBD_SelectInterfaceUrbAllocateAndBuild.

//      CompletionRoutine: Completion routine.
//
//      CompletionContext: Context for the completion routine.
//
//
// Return Value:
//
//      NTSTATUS

NTSTATUS SubmitUrbASync ( PDEVICE_EXTENSION DeviceExtension,
                         PIRP Irp,
                         PURB Urb,  
                         PIO_COMPLETION_ROUTINE CompletionRoutine,  
                         PVOID CompletionContext)  
{
    // Completion routine is required if the URB is submitted asynchronously.
    // The caller's completion routine releases the IRP when it completes.


    NTSTATUS ntStatus = -1;  

    PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);  

    // Attach the URB to this IRP.
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;  

    // Attach the URB to this IRP.
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;  

    // Attach the URB to this IRP.
    (void) USBD_AssignUrbToIoStackLocation (DeviceExtension->UsbdHandle, nextStack, Urb);  

    // Caller's completion routine will free the irp when it completes.
    ntStatus = IoSetCompletionRoutineEx ( DeviceExtension->NextDeviceObject,
        Irp,  
        CompletionRoutine,  
        CompletionContext,  
        TRUE,
        TRUE,
        TRUE);

    if (!NT_SUCCESS(ntStatus))
    {
        goto Exit;
    }

    (void) IoCallDriver(DeviceExtension->NextDeviceObject, Irp);

Exit:
    if (!NT_SUCCESS(ntStatus))
    {
        // We hit a failure condition,
        // We will free the IRP

        IoFreeIrp(Irp);
        Irp = NULL;
    }

    return ntStatus;
}

Senden von Anforderungen an ein USB-Gerät