Отправка URB

В этом разделе описываются шаги, необходимые для отправки инициализированного URB в стек драйверов USB для обработки определенного запроса.

Драйвер клиента взаимодействует со своим устройством с помощью запросов I/O control code (IOCTL), которые доставляются на устройство в пакетах запросов ввода-вывода (IRP) типа IRP_MJ_INTERNAL_DEVICE_CONTROL. Для конкретного устройства запроса, например запроса на выборку конфигурации, запрос описывается в блоке запросов USB (URB), связанном с IRP. Процесс связывания URB с IRP и отправки запроса в стек usb-драйверов называется отправкой URB. Чтобы отправить URB, драйвер клиента должен использовать IOCTL_INTERNAL_USB_SUBMIT_URB в качестве кода управления устройством. IOCTL является одним из "внутренних" кодов управления, которые предоставляют интерфейс ввода-вывода, который драйвер клиента использует для управления устройством и портом, к которому подключено устройство. Приложения в пользовательском режиме не имеют доступа к этим внутренним интерфейсам ввода-вывода. Дополнительные коды управления для драйверов режима ядра см. в разделе Списки IOCTL в режиме ядра для драйверов USB-клиента.

Предварительные требования

Перед отправкой запроса в стек драйвера универсальной последовательной шины (USB) драйвер клиента должен выделить структуру URB и форматировать ее в зависимости от типа запроса. Дополнительные сведения см. в разделах Выделение и создание urb и Рекомендации по использованию УРБ.

Инструкции

  1. Выделите IRP для URB, вызвав подпрограмму IoAllocateIrp . Необходимо указать размер стека объекта устройства, получающего IRP. Вы получили указатель на этот объект устройства при предыдущем вызове процедуры IoAttachDeviceToDeviceStack . Размер стека хранится в элементе StackSizeструктуры DEVICE_OBJECT .

  2. Получите указатель на первое расположение стека IRP (IO_STACK_LOCATION), вызвав IoGetNextIrpStackLocation.

  3. Задайте для элемента MajorFunction структуры IO_STACK_LOCATIONзначение IRP_MJ_INTERNAL_DEVICE_CONTROL.

  4. Задайте для элемента Parameters.DeviceIoControl.IoControlCode структуры IO_STACK_LOCATIONзначение IOCTL_INTERNAL_USB_SUBMIT_URB.

  5. Задайте для элемента Parameters.Others.Argument1 структуры IO_STACK_LOCATION адрес инициализированной структуры URB . Чтобы связать IRP с URB, можно вызвать USBD_AssignUrbToIoStackLocation только в том случае, если urB был выделен USBD_UrbAllocate, USBD_SelectConfigUrbAllocateAndBuild или USBD_SelectInterfaceUrbAllocateAndBuild.

  6. Задайте подпрограмму завершения, вызвав IoSetCompletionRoutineEx.

    Если вы отправляете URB асинхронно, передайте указатель на процедуру завершения, реализованную вызывающим объектом, и ее контекст. Вызывающий объект освобождает IRP в процедуре завершения.

    Если вы отправляете IRP синхронно, реализуйте процедуру завершения и передайте указатель на нее в вызове IoSetCompletionRoutineEx. Для вызова также требуется инициализированный объект KEVENT в параметре Context . В подпрограмме завершения задайте для события состояние сигналов.

  7. Вызовите IoCallDriver , чтобы перенаправить заполненное IRP на следующий нижний объект устройства в стеке устройств. Для синхронного вызова после вызова IoCallDriver дождитесь, пока объект события будет вызывать KeWaitForSingleObject , чтобы получить уведомление о событии.

  8. После завершения IRP проверка элемент IoStatus.Status IRP и оцените результат. Если ioStatus.Status имеет STATUS_SUCCESS, запрос выполнен успешно.

Синхронная отправка ПО USB

В следующем примере показано, как отправить URB синхронно.

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

Асинхронная отправка ПО USB

В следующем примере показано, как отправить URB асинхронно.

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

Отправка запросов на USB-устройство