Bulk USB transfer fails to trigger IRP completion routine

Discussion in 'Windows Vista Drivers' started by Linda, Feb 26, 2007.

  1. Linda

    Linda Guest

    Hello,

    I'm hoping someone out there can help me solve this perplexing
    problem.

    Here's the design:
    When the driver receives an IRP_MJ_CREATE irp, it completes it and
    queues a work item which allocates an IRP (unassociated with any I/O
    Manager irp) to read data from the usb device using bulk transfers
    with URB flags set to USBD_SHORT_TRANSFER_OK |
    USBD_TRANSFER_DIRECTION_IN. The completion routine reuses that irp
    using the technique described in Walter Oney's WDM book.

    Here's the problem:
    Unfotunately, the irp's completion routine is not always triggered
    even, though the device has sent data (verified using a CATC protocol
    analyzer).

    Here is the sequence of events.
    1. Driver allocates irp to request 4096 bytes
    2. Device sends 1535 bytes
    3. Completion routine reuses irp to request 4096 bytes
    4. Device sends 1 byte
    5. Completion routine reuses irp to request 4096 bytes
    6. Devices sends 24 bytes
    7. Completion routine fails to trigger!
    8. Device sends 24 bytes
    9. Completion routine processes the 24 bytes sent in step 8 and reuses
    the irp.

    What happened to the 24 bytes sent in step 6? The messages contain
    sequence numbers, and the driver receives message sequence 0 (which
    was the 1535 and 1 byte transfers), and message sequence 2 (the second
    24 byte transfer). Message sequence 1 disappeared into the great
    unknown.

    If I modify the device to send message 0 in 2 transfers of 1532 and 4
    bytes, the completion routine gets triggered properly.

    Alternatively, if I modify the driver to, instead of reusing the irp,
    allocate a new irp when the TransferBufferLength == 1, the subsequent
    completion routine gets triggered properly.

    So apparently, the single-byte transfer is the culprit. But why?

    Any help would be greatly appreciated!

    Here is the relevant code, with error-handling and some bookkeeping
    removed:

    VOID WorkerStartUsbReader (PDEVICE_OBJECT pFdo_, PVOID pContext_ )
    { // WorkerStartUsbReader
    PIRP pIrp = NULL;
    PURB pUrb = NULL;
    ULONG readLen = 0;

    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) pFdo_->DeviceExtension;
    PUSB_READER pUsbRdr = &pdx->UsbReader;
    PIO_WORKITEM pWorkItem = (PIO_WORKITEM) pContext_;

    KIRQL oldIrql;
    NTSTATUS status = STATUS_SUCCESS;

    // Spin lock gets released in __finally clause
    KeAcquireSpinLock ( &pUsbRdr->kSpinLock, &oldIrql );
    __try
    {
    // Determine amount of data to be requested, making sure it's a
    multiple
    // of the pipe's packet size to avoid stalling the endpoint
    readLen = pUsbRdr->ulCapacity - pUsbRdr->ulLength;
    if ( readLen > pdx->MaximumTransferSize )
    readLen = pdx->MaximumTransferSize;
    readLen -= ( readLen % pdx->MaxPacketSize );

    // Minimum request size is a single packet to avoid stalling the
    endpoint
    if ( readLen < pdx->MaxPacketSize )
    {
    status = STATUS_BUFFER_TOO_SMALL;
    __leave;
    }

    // Allocate an IRP to send to the USB driver.
    pIrp = IoAllocateIrp ( pdx->LowerDeviceObject->StackSize+1,
    FALSE );

    // Allocate our URB
    pUrb = (PURB) ExAllocatePool ( NonPagedPool, sizeof(URB) );

    RtlZeroMemory ( pUrb, sizeof(URB) );
    UsbBuildInterruptOrBulkTransferRequest (
    pUrb,
    sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER),
    pdx->hInPipe,
    pUsbRdr->pucWriteCursor, // Nonpaged memory allocated by
    driver
    NULL,
    readLen,
    USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN,
    NULL );

    // Driver-allocated IRPs must set the default status to
    STATUS_NOT_SUPPORTED
    pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;

    // Set up the first (our) stack location in the Irp
    PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation ( pIrp );
    stack->DeviceObject = pUsbRdr->pFdo;

    // Set up next stack location
    IoSetNextIrpStackLocation( pIrp );
    stack = IoGetNextIrpStackLocation ( pIrp );
    stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    stack->Parameters.DeviceIoControl.IoControlCode =
    IOCTL_INTERNAL_USB_SUBMIT_URB;
    stack->Parameters.Others.Argument1 = (PVOID) pUrb;

    IoSetCompletionRoutine ( pIrp, (PIO_COMPLETION_ROUTINE)
    OnCompleteUsbReaderIrp,
    (PVOID) pUrb, TRUE, TRUE, TRUE );
    }
    __finally
    {
    KeReleaseSpinLock ( &pUsbRdr->kSpinLock, oldIrql );

    // Send Irp on its way.
    if ( NT_SUCCESS( status ) && pIrp != NULL )
    IoCallDriver(pdx->LowerDeviceObject, pIrp);

    // Release the remove lock we acquired in StartUsbReader()
    IoReleaseRemoveLock ( &pdx->RemoveLock, pWorkItem );
    IoFreeWorkItem(pWorkItem);
    }

    return;
    } // WorkerStartUsbReader

    ///////////////////////////////////////////////////////////////////////////////
    #pragma LOCKEDCODE

    NTSTATUS OnCompleteUsbReaderIrp (PDEVICE_OBJECT pFdo_, PIRP pIrp_,
    PURB pUrb_)
    { // OnCompleteUsbReaderIrp

    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) pFdo_->DeviceExtension;
    PUSB_READER pUsbRdr = &pdx->UsbReader;

    NTSTATUS status;
    ULONG seglen = 0; // length of next read request
    KIRQL oldIrql;
    BOOLEAN bReuseIrp = true;

    USBD_STATUS urbstatus = URB_STATUS(pUrb_);
    if (!USBD_SUCCESS(urbstatus))
    bReuseIrp = false;

    if ( !NT_SUCCESS ( status ) )
    bReuseIrp = false;

    ///////////////////////////////////////////////////////////////////////////////////////
    // This kludge prevents missing data following single-byte transfer
    if ( pUrb_->UrbBulkOrInterruptTransfer.TransferBufferLength == 1 )
    bReuseIrp = false;
    ///////////////////////////////////////////////////////////////////////////////////////

    // If Irp completed without error, process the data
    if ( NT_SUCCESS(status) )
    seglen = ProcessData( pUsbRdr, pUrb_ );

    // Minimum request is a single packet.
    if ( seglen < pdx->MaxPacketSize )
    bReuseIrp = false;

    if ( bReuseIrp )
    {
    ASSERT ( seglen <= pdx->MaximumTransferSize );
    ASSERT ( seglen % pdx->MaxPacketSize == 0 );
    ASSERT ( seglen > 0 );

    // Reinitialize the URB so we can request more data
    RtlZeroMemory ( pUrb_,
    sizeof(URB) );
    UsbBuildInterruptOrBulkTransferRequest
    (

    pUrb_,

    sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER),
    pdx-
    NULL,

    seglen,
    USBD_SHORT_TRANSFER_OK |
    USBD_TRANSFER_DIRECTION_IN,

    NULL );

    // Reuse the
    irp
    UCHAR flags = pIrp_->AllocationFlags;
    CCHAR nstack = pIrp_-
    IoInitializeIrp (pIrp_,IoSizeOfIrp(nstack),nstack);
    pIrp_->AllocationFlags =
    flags;
    pIrp_->IoStatus.Status = STATUS_NOT_SUPPORTED;

    // Set up the first (our) stack location in the
    Irp
    PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation
    ( pIrp_ );
    stack->DeviceObject =
    pFdo_;

    // Set up next (ngpsusb's) stack
    location

    IoSetNextIrpStackLocation( pIrp_ );
    stack = IoGetNextIrpStackLocation
    ( pIrp_ );
    stack->MajorFunction =
    IRP_MJ_INTERNAL_DEVICE_CONTROL;
    stack->Parameters.DeviceIoControl.IoControlCode =
    IOCTL_INTERNAL_USB_SUBMIT_URB;
    stack->Parameters.Others.Argument1 = (PVOID)
    pUrb_;

    IoSetCompletionRoutine ( pIrp_, (PIO_COMPLETION_ROUTINE)
    OnCompleteUsbReaderIrp,
    (PVOID) pUrb_, TRUE, TRUE,
    TRUE );

    // Pass the recycled IRP down and ignore the return code from
    IoCallDriver.
    IoCallDriver ( pdx->LowerDeviceObject, pIrp_ );

    }
    else
    {
    // Free the Irp and Urb.
    // A new Irp will be created when other code queues the
    WorkerStartUsbReader work item.
    IoReleaseRemoveLock ( &pdx->RemoveLock, pIrp_ );
    IoFreeIrp ( pIrp_ );
    ExFreePool ( pUrb_ );
    }

    return STATUS_MORE_PROCESSING_REQUIRED;
    } // OnCompleteUsbReaderIrp
     
    Linda, Feb 26, 2007
    #1
    1. Advertisements

  2. What is the endpoint's reported maximum packet size?
     
    chris.aseltine, Feb 26, 2007
    #2
    1. Advertisements

  3. Linda

    Linda Guest

    The endpoint's maximum packet size is 64 bytes.
     
    Linda, Feb 26, 2007
    #3
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.