Partager via


Comment envoyer un URB

Cette rubrique décrit les étapes nécessaires pour envoyer un URB initialisé à la pile de pilotes USB afin de traiter une demande particulière.

Un pilote client communique avec son appareil à l’aide de requêtes IOCTL (I/O Control Code) qui sont remises à l’appareil dans des paquets de demandes d’E/S (IRPs) de type IRP_MJ_INTERNAL_DEVICE_CONTROL. Pour une demande spécifique à un appareil, telle qu’une demande de sélection-configuration, la demande est décrite dans un bloc de requête USB (URB) associé à un IRP. Le processus d’association d’une URB à un IRP et d’envoi de la demande à la pile de pilotes USB est appelé envoi d’un URB. Pour envoyer un URB, le pilote client doit utiliser IOCTL_INTERNAL_USB_SUBMIT_URB comme code de contrôle d’appareil. IocTL est l’un des codes de contrôle « internes » qui fournissent une interface d’E/S qu’un pilote client utilise pour gérer son appareil et le port auquel l’appareil est connecté. Les applications en mode utilisateur n’ont pas accès à cette interface d’E/S interne. Pour plus d’informations sur les codes de contrôle pour les pilotes en mode noyau, consultez IOCTL en mode noyau pour les pilotes clients USB.

Prérequis

Avant d’envoyer une requête à la pile de pilotes USB (Universal Serial Bus), le pilote client doit allouer une structure URB et mettre en forme cette structure en fonction du type de requête. Pour plus d’informations, consultez Allocation et génération d’URBs et meilleures pratiques : utilisation d’URBs.

Instructions

  1. Allouez un IRP pour l’URB en appelant la routine IoAllocateIrp . Vous devez fournir la taille de pile de l’objet d’appareil qui reçoit l’IRP. Vous avez reçu un pointeur vers cet objet d’appareil lors d’un appel précédent à la routine IoAttachDeviceToDeviceStack . La taille de la pile est stockée dans le membre StackSize de la structure DEVICE_OBJECT .

  2. Obtenez un pointeur vers le premier emplacement de pile de l’IRP (IO_STACK_LOCATION) en appelant IoGetNextIrpStackLocation.

  3. Définissez le membre MajorFunction de la structure IO_STACK_LOCATION sur IRP_MJ_INTERNAL_DEVICE_CONTROL.

  4. Définissez le membre Parameters.DeviceIoControl.IoControlCode de la structure IO_STACK_LOCATION sur IOCTL_INTERNAL_USB_SUBMIT_URB.

  5. Définissez le membre Parameters.Others.Argument1 de la structure IO_STACK_LOCATION sur l’adresse de la structure URB initialisée. Pour associer l’IRP à l’URB, vous pouvez également appeler USBD_AssignUrbToIoStackLocation uniquement si l’URB a été alloué par USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild ou USBD_SelectInterfaceUrbAllocateAndBuild.

  6. Définissez une routine d’achèvement en appelant IoSetCompletionRoutineEx.

    Si vous envoyez l’URB de manière asynchrone, passez un pointeur vers la routine d’achèvement implémentée par l’appelant et son contexte. L’appelant libère l’IRP dans sa routine d’achèvement.

    Si vous envoyez l’IRP de manière synchrone, implémentez une routine d’achèvement et passez un pointeur vers cette routine dans l’appel à IoSetCompletionRoutineEx. L’appel nécessite également un objet KEVENT initialisé dans le paramètre Context . Dans votre routine d’achèvement, définissez l’événement sur l’état signalé.

  7. Appelez IoCallDriver pour transférer l’IRP rempli vers l’objet d’appareil inférieur suivant dans la pile de l’appareil. Pour un appel synchrone, après avoir appelé IoCallDriver, attendez l’objet d’événement en appelant KeWaitForSingleObject pour obtenir la notification d’événement.

  8. Une fois l’IRP terminée, case activée le membre IoStatus.Status d’IRP et évaluez le résultat. Si IoStatus.Status est STATUS_SUCCESS, la demande a réussi.

Envoi synchrone USB

L’exemple suivant montre comment envoyer un URB de manière synchrone.

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

Envoi asynchrone USB

L’exemple suivant montre comment envoyer un URB de manière asynchrone.

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

Envoi de requêtes à un périphérique USB