Irp/Urb Completion Routine Is Called Only Once

Discussion in 'Windows Vista Drivers' started by Dennis Burns, Sep 26, 2004.

  1. Dennis Burns

    Dennis Burns Guest

    Any idea why my Completion Routine is not being called, when I send an
    Irp/Urb down the stack? It works on the first attempt, but not the second.
    On the first request, everything works fine, and the expected data is
    returned from my device.
    On the second request, I see the request take place on the USB bus, and I
    see that my device responds correctly. But my Completion Routine is not
    subsequently called.
    I've tried re-using the original Irp and allocating a new Irp, with no
    difference.
    I re-initialize the Urb and attach it to the Irp the same way each time
    before forwarding down the stack.

    Dazed, dazzled and confused,
    Dennis
     
    Dennis Burns, Sep 26, 2004
    #1
    1. Advertisements

  2. Dennis Burns

    Walter Oney Guest

    Are you also calling IoSetCompletionRoutine each time?
     
    Walter Oney, Sep 26, 2004
    #2
    1. Advertisements

  3. Dennis Burns

    Dennis Burns Guest

    Hello Walter,
    Yes, I am.
    As a matter of fact, I am using your USBINT example as a base, but I had to
    change things a little. I don't want the Irp to be active until I get a
    request for data from user mode. In addition, I want the original Irp to
    receive (Interrupt IN) data from the USB operation.
    My DispatchControl function contains this:

    case IOCTL_READEP1_BUFFERED:
    status = GenericCacheControlRequest(pdx->pgx, Irp, &pdx->InterruptIrp);
    if( status == STATUS_PENDING )
    {
    // The expected result
    status = StartInterruptUrb( pdx, Irp->AssociatedIrp.SystemBuffer,
    cbout );
    }
    break;

    Inside StartInterruptUrb I first double-check that a PollingIrp is not
    already active (which it should not be), and then I end up with this, where
    dwBytes was passed as cbout, and pvDest was passed as
    Irp->AssociatedIrp.SystemBuffer.

    NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
    if (!NT_SUCCESS(status))
    {
    pdx->pollpending = 0;
    return status;
    }
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    // Initialize the URB we use for reading the interrupt pipe
    UsbBuildInterruptOrBulkTransferRequest(
    urb,
    sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER),
    pdx->hintpipe,
    pvDest,
    NULL,
    dwBytes,
    USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
    NULL
    );
    IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnInterrupt, pdx, TRUE,
    TRUE, TRUE);
    PIO_STACK_LOCATION LowerStack = IoGetNextIrpStackLocation(Irp);
    LowerStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    LowerStack->Parameters.DeviceIoControl.IoControlCode =
    IOCTL_INTERNAL_USB_SUBMIT_URB;
    LowerStack->Parameters.DeviceIoControl.OutputBufferLength = dwBytes;
    LowerStack->Parameters.Others.Argument1 = urb;

    return IoCallDriver(pdx->LowerDeviceObject, Irp);
    }

    FYI: Before I exit OnInterrupt, I do this:
    IoReuseIrp( Irp, STATUS_SUCCESS );
    but as I said, I've also tried allocating a new Irp, with no success.

    I've verified via Windbg that StartInterruptUrb gets called twice, as
    expected, but OnInterrupt gets called only once. I can see USB traffic with
    my Ellisys USB analyzer in response to both calls of StartInterruptUrb, but
    the second call to OnInterrupt never occurs.
    Thanks for the help,
    Dennis
     
    Dennis Burns, Sep 26, 2004
    #3
  4. Dennis Burns

    Walter Oney Guest

    This will reinitialize all the stack locations, so you want to be sure
    you do it before setting the completion routine, major function, etc.
     
    Walter Oney, Sep 27, 2004
    #4
  5. Dennis Burns

    Dennis Burns Guest

    Hello Walter,
    Yes, I understand that.
    At that point, I'm completely finished with the polling irp until I get
    another request from User mode, via a new IOCTL. Then, in my dispatch
    routine, I first double-check that I can cache the new Irp. If so (always
    the case, as long as my user mode thread is operating properly, and my last
    OnInterrupt routine un-cached it properly). Then, I call StartInterruptUrb,
    which is almost identical to your USBINT version. I've added the pvDest and
    dwBytes parameters. Note that StartInterruptUrb executes twice without
    error, and the first OnInterrupt routine executes without error, but the
    second OnInterrupt call never happens. I do see two USB transfers being sent
    from my device, as expected.
    Is there some context change occuring that I'm not considering?
    Thanks again for you help.
    Dennis

    NTSTATUS StartInterruptUrb(PDEVICE_EXTENSION pdx, PVOID pvDest, DWORD
    dwBytes )
    { // StartInterruptUrb
    // If the interrupt polling IRP is currently running, don't try to start
    // it again.
    KdPrint((DRIVERNAME " - Entering StartInterruptUrb\n"));

    BOOLEAN startirp;
    KIRQL oldirql;
    KeAcquireSpinLock(&pdx->polllock, &oldirql);
    if (pdx->pollpending)
    startirp = FALSE;
    else
    startirp = TRUE, pdx->pollpending = TRUE;
    KeReleaseSpinLock(&pdx->polllock, oldirql);

    if (!startirp)
    return STATUS_DEVICE_BUSY; // already pending

    PIRP Irp = pdx->PollingIrp;
    PURB urb = pdx->PollingUrb;
    ASSERT(Irp && urb);

    // Acquire the remove lock so we can't remove the lower device while the
    IRP
    // is still active.
    NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
    if (!NT_SUCCESS(status))
    {
    pdx->pollpending = 0;
    return status;
    }
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    // Initialize the URB we use for reading the interrupt pipe
    UsbBuildInterruptOrBulkTransferRequest(
    urb,
    sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER),
    pdx->hintpipe,
    pvDest,
    NULL,
    dwBytes,
    USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
    NULL
    );
    IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnInterrupt, pdx,
    TRUE, TRUE, TRUE);
    PIO_STACK_LOCATION LowerStack = IoGetNextIrpStackLocation(Irp);
    LowerStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    LowerStack->Parameters.DeviceIoControl.IoControlCode =
    IOCTL_INTERNAL_USB_SUBMIT_URB;
    LowerStack->Parameters.DeviceIoControl.OutputBufferLength = dwBytes;
    LowerStack->Parameters.Others.Argument1 = urb;

    return IoCallDriver(pdx->LowerDeviceObject, Irp);
    } // StartInterruptUrb
     
    Dennis Burns, Sep 27, 2004
    #5
  6. Dennis Burns

    Dennis Burns Guest

    Hello Walter,
    Here's something else that I tried, with no success.
    Instead of putting IoReuseIrp at the end of the completion routine, I added
    a BOOLEAN called UsedPollingIrp to my device context. Then, near the
    beginning of StartInterruptUrb, I added this code:

    if( pdx->UsedPollingIrp )
    IoReuseIrp( Irp, STATUS_SUCCESS );
    pdx->UsedPollingIrp = true;

    Then I initialize the Polling Irp and Urb.

    This did not affect the problem: still only one OnInterrupt.

    By the way, maybe it's overkill to call IoReuseIrp at all. In your book, you
    simply have:
    Irp->Cancel = FALSE;
    just before the call of IoCallDriver at the end of StartInterruptUrb.
    Is that all that is necessary?

    Still Stalled,
    Dennis
     
    Dennis Burns, Sep 27, 2004
    #6
  7. So here is my guess on your Irp/Urb completion thing: (I will also post to
    the newsgroup).



    The nature of the problem has to do with your MaxPacket on your interrupt
    endpoint and the way in which USB completes transfers.

    USB transfers will complete as a result of one of the following scenarios:

    1.. All of the requested data has been received (your URB request 15
    bytes, and 15 bytes have been returned by the device)
    2.. A short packet is received (MaxPacket is 8 bytes, and the device
    returns 6 in one packet)
    3.. The transfer is cancelled by your driver (or some other driver in the
    stack).
    4.. The device STALLs the endpoint due to some error condition.


    So, since your completion routine isn't being called, then none of those 4
    situations are occurring.

    My best guess is that you requested some value greater than MaxPacket, and
    the device returns exactly some multiple of MaxPacket. Because of this, the
    host doesn't know that the device is finished with its transfer.



    There are three ways I can think of to solve this.

    1.. Have the device send a zero-length packet if the transfer is a
    multiple of MaxPacket and less that some MaxTransfer that you determine
    (which will be the size of your transfer request). This will cause the URB
    to complete since a zero-length packet is also a short packet and satisfies
    scenario 2 above.
    2.. Change your MaxPacket so that the transfers will always end on a short
    packet (this only works if you have fixed packet types on the endpoint
    rather than some arbitrary amount of data)
    3.. Only request MaxPacket at a time and then build the transfer manually.
    This way your transfer will complete after every packet. It would then be
    up to you to reconstruct the transfer based on some knowledge of the packet
    format (i.e. if there was a header that specified the length of the data).


    I hope this helps!



    Randy
     
    Randy Aull \(MSFT\), Sep 29, 2004
    #7
  8. Dennis Burns

    Dennis Burns Guest

    Hello Randy,

    I can't tell you how much I appreciate the help on this.

    Here's the situation, with respect to your comments below.

    My endpoint MaxPacketSize is 64.

    When I set up the URB, I request 1024 bytes, and set the flag to allow short
    packets.

    For my test, my device returns only 12 bytes, so I'm definitely in the
    short-packet scenario. By the way, in both cases, my device is using DATA0.
    From what I read in the USB specification, this should be correct. Once the
    first Irp (per USB's definition) is complete, then we revert back to DATA0.
    Right?

    Note: If the host is expecting DATA1, then this could explain the problem.



    By the way, you did point out an error that would have occurred, if my
    returned byte count was less than requested, but equal to an integral number
    of MaxPacketSize bytes. I knew that rule, just forgot to implement the check
    in my device's interrupt handler. I'll add a zero-length packet in that
    case.



    Best regards,

    Dennis
     
    Dennis Burns, Sep 29, 2004
    #8
  9. Dennis Burns

    Dennis Burns Guest

    Note that this problem was solved.
    The root cause was that my device was not properly toggling the DATA0/DATA1
    sequence when sending it's response to a data IN request.
    Thanks to Walter and Randy for their help.
    Dennis
     
    Dennis Burns, Sep 30, 2004
    #9
    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.