Unable to set/call Completion routine in USB Driver

Discussion in 'Windows Vista Drivers' started by Kabir, Mar 17, 2008.

  1. Kabir

    Kabir Guest

    I am writing a USB driver, while doing bulk transfer, i create the URB (with
    correct parameters), make the IRP_MJ_WRITE pending, set the URB as part of
    the same IRP and send it down to the next driver (usbhub.sys). I have set a
    completion routine for this IRP, which should be called in all the cases.
    The lower driver returns a STATUS_PENDING, but it never calls the completion
    routine, which means, it is unable to complete the IRP.
    I have referred lot of information and seem to be doing the right thing.
    I know this is a generic question, but any pointers?
    Help appreciated.
    Thanks.
     
    Kabir, Mar 17, 2008
    #1
    1. Advertisements

  2. how about posting the code which sets the c.r. and the urb?

    d
     
    Doron Holan [MSFT], Mar 17, 2008
    #2
    1. Advertisements

  3. Kabir

    kabirchugh Guest

    - The Write Dispatch routine schedules a work item, Marks the IRP as
    pending and returns STATUS_PENDING

    - The work item calls this function

    NTSTATUS
    SendFWData (
    PFDO_DATA pFdoData,
    PVOID pTxBuffer,
    ULONG TxBufferSize,
    PIRP Irp
    )
    {
    PMRVL_USB_DEVICE pBus = pFdoData->pUsbDevice;
    UCHAR IrpStackSize;
    PCOMMON_CMD pCmd;
    USHORT urbSize;
    PURB urb;
    ULONG totalLength;
    ULONG stageLength;
    ULONG urbFlags;
    BOOLEAN read;
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    ULONG_PTR virtualAddress;
    PFILE_OBJECT fileObject;
    PIO_STACK_LOCATION irpStack;
    PIO_STACK_LOCATION nextStack;
    PBULKUSB_RW_CONTEXT rwContext;


    CardDebugPrint(INFO, "Entered SendFWData.\n");

    urb = NULL;
    rwContext = NULL;
    totalLength = TxBufferSize;
    irpStack = IoGetCurrentIrpStackLocation(Irp);

    read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;

    rwContext = ExAllocatePool(NonPagedPool,
    sizeof(BULKUSB_RW_CONTEXT));

    if(rwContext == NULL) {

    CardDebugPrint(ERROR, ("Failed to alloc mem for rwContext
    \n"));
    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    goto SendFWDataExit;
    }

    if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE) {

    CardDebugPrint(ERROR, ("Transfer length > circular buffer
    \n"));

    ntStatus = STATUS_INVALID_PARAMETER;

    ExFreePool(rwContext);
    rwContext = NULL;

    goto SendFWDataExit;
    }

    if(totalLength == 0) {

    CardDebugPrint(ERROR, ("Transfer data length = 0\n"));

    ntStatus = STATUS_SUCCESS;

    ExFreePool(rwContext);
    rwContext = NULL;

    goto SendFWDataExit;
    }

    urbFlags = USBD_SHORT_TRANSFER_OK;
    virtualAddress = (ULONG_PTR) pTxBuffer;

    if(read) {

    urbFlags |= USBD_TRANSFER_DIRECTION_IN;
    CardDebugPrint(INFO, ("Read operation\n"));
    }
    else {

    urbFlags = USBD_TRANSFER_DIRECTION_OUT;
    CardDebugPrint(INFO, ("Write operation\n"));
    }

    if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {

    stageLength = BULKUSB_MAX_TRANSFER_SIZE;
    }
    else {

    stageLength = totalLength;
    }

    urbSize = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);

    urb = ExAllocatePool(NonPagedPool, urbSize);

    if(urb == NULL) {

    CardDebugPrint(ERROR, ("Failed to alloc mem for urb\n"));
    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    ExFreePool(rwContext);
    rwContext = NULL;
    return ntStatus;
    }

    // Initialize the URB
    UsbBuildInterruptOrBulkTransferRequest(
    urb, // Urb
    urbSize, // Length of Urb
    pBus->USBPipeBulkOut.PipeHandle, // Pipe Handle
    pTxBuffer, // TransferBuffer
    NULL, // Transfer MDL
    stageLength, // Transfer Size
    urbFlags, // Transfer Flags
    NULL // Link
    );

    //
    // set BULKUSB_RW_CONTEXT parameters.
    //

    rwContext->Urb = urb;
    rwContext->TxBuffer = pTxBuffer;
    rwContext->Length = totalLength - stageLength;
    rwContext->Numxfer = 0;
    rwContext->VirtualAddress = virtualAddress + stageLength;
    rwContext->pFdoData = pFdoData;
    rwContext->pCurrentIrp = Irp; // Pointer to the
    IRP_MJ_WRITE

    CardDebugPrint(INFO, "Sending TxBuffer to Pipe:\n");
    DbgPrintData(pTxBuffer, TxBufferSize, 0);
    CardDebugPrint(INFO, "Tx Buffer size is (%d).\n", TxBufferSize);

    nextStack = IoGetNextIrpStackLocation(Irp);
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextStack->Parameters.Others.Argument1 = (PVOID) urb;
    nextStack->Parameters.DeviceIoControl.IoControlCode =

    IOCTL_INTERNAL_USB_SUBMIT_URB;

    CardDebugPrint(INFO, "Setting BulkUsb_ReadWriteCompletion\n");
    ntStatus = IoSetCompletionRoutineEx(pFdoData->Self,
    Irp,

    (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
    rwContext,
    TRUE,
    TRUE,
    TRUE);

    if(!NT_SUCCESS(ntStatus))
    {
    CardDebugPrint(ERROR, ("IoSetCompletionRoutineEx failed with
    status %X\n", ntStatus));
    goto SendFWDataExit;
    }

    Irp->Cancel = FALSE;

    // Marking the IRP as pending
    /*CardDebugPrint(INFO, "Marking the IRP as pending\n");
    IoMarkIrpPending(Irp);*/

    CardIoIncrement(pFdoData);

    CardDebugPrint(INFO, "Calling the next lower driver\n");

    ntStatus = IoCallDriver(pFdoData->NextLowerDriver,Irp);

    if(ntStatus == STATUS_PENDING)
    {
    CardDebugPrint(INFO, "SendFWData: IoCallDriver Lower driver
    returned STATUS_PENDING\n");
    }

    return ntStatus;

    SendFWDataExit:
    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    CardDebugPrint(INFO, "Leaving SendFWData with status (0x%p).\n",
    ntStatus);

    return ntStatus;
    }

    NTSTATUS
    BulkUsb_ReadWriteCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    /*++

    Routine Description:

    This is the completion routine for reads/writes
    If the irp completes with success, we check if we
    need to recirculate this irp for another stage of
    transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
    if the irp completes in error, free all memory allocs and
    return the status.

    Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    Context - context passed to the completion routine.

    Return Value:

    NT status value

    --*/
    {
    ULONG stageLength;
    NTSTATUS ntStatus;
    //PIO_STACK_LOCATION nextStack;
    PBULKUSB_RW_CONTEXT rwContext;

    UNREFERENCED_PARAMETER(DeviceObject);
    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion - begins\n"));
    //
    // initialize variables
    //
    rwContext = (PBULKUSB_RW_CONTEXT) Context;

    CardIoDecrement(rwContext->pFdoData);

    ntStatus = Irp->IoStatus.Status;

    //
    // successfully performed a stageLength of transfer.
    // check if we need to recirculate the irp.
    //
    if(NT_SUCCESS(ntStatus))
    {
    if(rwContext)
    {
    rwContext->Numxfer +=
    rwContext->Urb-
    if(rwContext->Length) {

    //
    // another stage transfer
    //
    CardDebugPrint(INFO, ("Another stage transfer...\n"));

    if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {

    stageLength = BULKUSB_MAX_TRANSFER_SIZE;
    }
    else {

    stageLength = rwContext->Length;
    }

    // Need to add code for staged tx
    // Don't need this as of now
    }
    else
    {

    //
    // this is the last transfer
    //
    // We are stripping one byte from the Write IRP and then
    // sending on the USB, so adding it back
    Irp->IoStatus.Information = rwContext->Numxfer +
    1;
    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion:
    Success"));
    }
    }
    }
    else
    {
    CardDebugPrint(ERROR, ("BulkUsb_ReadWriteCompletion - failed
    with status = (0x%p)\n",
    ntStatus));
    }

    if(rwContext) {

    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion:: Cleaning
    up memory\n"));
    ExFreePool(rwContext->Urb);
    ExFreePool(rwContext);
    }

    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion - ends\n"));

    return ntStatus;
    }
     
    kabirchugh, Mar 26, 2008
    #3
  4. Kabir

    Kabir Guest

    - The Write Dispatch routine schedules a work item, marks the IRP as pending
    and returns STATUS_PENDING

    - The work item calls this function

    NTSTATUS
    SendFWData (
    PFDO_DATA pFdoData,
    PVOID pTxBuffer,
    ULONG TxBufferSize,
    PIRP Irp
    )
    {
    PMRVL_USB_DEVICE pBus = pFdoData->pUsbDevice;
    UCHAR IrpStackSize;
    PCOMMON_CMD pCmd;
    USHORT urbSize;
    PURB urb;
    ULONG totalLength;
    ULONG stageLength;
    ULONG urbFlags;
    BOOLEAN read;
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    ULONG_PTR virtualAddress;
    PFILE_OBJECT fileObject;
    PIO_STACK_LOCATION irpStack;
    PIO_STACK_LOCATION nextStack;
    PBULKUSB_RW_CONTEXT rwContext;


    CardDebugPrint(INFO, "Entered SendFWData.\n");

    urb = NULL;
    rwContext = NULL;
    totalLength = TxBufferSize;
    irpStack = IoGetCurrentIrpStackLocation(Irp);

    read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;

    rwContext = ExAllocatePool(NonPagedPool,
    sizeof(BULKUSB_RW_CONTEXT));

    if(rwContext == NULL) {

    CardDebugPrint(ERROR, ("Failed to alloc mem for rwContext\n"));
    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    goto SendFWDataExit;
    }

    if(totalLength > BULKUSB_TEST_BOARD_TRANSFER_BUFFER_SIZE) {

    CardDebugPrint(ERROR, ("Transfer length > circular buffer\n"));

    ntStatus = STATUS_INVALID_PARAMETER;

    ExFreePool(rwContext);
    rwContext = NULL;

    goto SendFWDataExit;
    }

    if(totalLength == 0) {

    CardDebugPrint(ERROR, ("Transfer data length = 0\n"));

    ntStatus = STATUS_SUCCESS;

    ExFreePool(rwContext);
    rwContext = NULL;

    goto SendFWDataExit;
    }

    urbFlags = USBD_SHORT_TRANSFER_OK;
    virtualAddress = (ULONG_PTR) pTxBuffer;

    if(read) {

    urbFlags |= USBD_TRANSFER_DIRECTION_IN;
    CardDebugPrint(INFO, ("Read operation\n"));
    }
    else {

    urbFlags = USBD_TRANSFER_DIRECTION_OUT;
    CardDebugPrint(INFO, ("Write operation\n"));
    }

    if(totalLength > BULKUSB_MAX_TRANSFER_SIZE) {

    stageLength = BULKUSB_MAX_TRANSFER_SIZE;
    }
    else {

    stageLength = totalLength;
    }

    urbSize = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);

    urb = ExAllocatePool(NonPagedPool, urbSize);

    if(urb == NULL) {

    CardDebugPrint(ERROR, ("Failed to alloc mem for urb\n"));
    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    ExFreePool(rwContext);
    rwContext = NULL;
    return ntStatus;
    }

    // Initialize the URB
    UsbBuildInterruptOrBulkTransferRequest(
    urb, // Urb
    urbSize, // Length of Urb
    pBus->USBPipeBulkOut.PipeHandle, // Pipe Handle
    pTxBuffer, // TransferBuffer
    NULL, // Transfer MDL
    stageLength, // Transfer Size
    urbFlags, // Transfer Flags
    NULL // Link
    );

    //
    // set BULKUSB_RW_CONTEXT parameters.
    //

    rwContext->Urb = urb;
    rwContext->TxBuffer = pTxBuffer;
    rwContext->Length = totalLength - stageLength;
    rwContext->Numxfer = 0;
    rwContext->VirtualAddress = virtualAddress + stageLength;
    rwContext->pFdoData = pFdoData;
    rwContext->pCurrentIrp = Irp; // Pointer to the IRP_MJ_WRITE

    CardDebugPrint(INFO, "Sending TxBuffer to Pipe:\n");
    DbgPrintData(pTxBuffer, TxBufferSize, 0);
    CardDebugPrint(INFO, "Tx Buffer size is (%d).\n", TxBufferSize);

    nextStack = IoGetNextIrpStackLocation(Irp);
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextStack->Parameters.Others.Argument1 = (PVOID) urb;
    nextStack->Parameters.DeviceIoControl.IoControlCode =
    IOCTL_INTERNAL_USB_SUBMIT_URB;

    CardDebugPrint(INFO, "Setting BulkUsb_ReadWriteCompletion\n");
    ntStatus = IoSetCompletionRoutineEx(pFdoData->Self,
    Irp,

    (PIO_COMPLETION_ROUTINE)BulkUsb_ReadWriteCompletion,
    rwContext,
    TRUE,
    TRUE,
    TRUE);

    if(!NT_SUCCESS(ntStatus))
    {
    CardDebugPrint(ERROR, ("IoSetCompletionRoutineEx failed with status
    %X\n", ntStatus));
    goto SendFWDataExit;
    }

    Irp->Cancel = FALSE;

    // Marking the IRP as pending
    /*CardDebugPrint(INFO, "Marking the IRP as pending\n");
    IoMarkIrpPending(Irp);*/

    CardIoIncrement(pFdoData);

    CardDebugPrint(INFO, "Calling the next lower driver\n");

    ntStatus = IoCallDriver(pFdoData->NextLowerDriver,Irp);

    if(ntStatus == STATUS_PENDING)
    {
    CardDebugPrint(INFO, "SendFWData: IoCallDriver Lower driver returned
    STATUS_PENDING\n");
    }

    return ntStatus;

    SendFWDataExit:
    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    CardDebugPrint(INFO, "Leaving SendFWData with status (0x%p).\n",
    ntStatus);

    return ntStatus;
    }

    NTSTATUS
    BulkUsb_ReadWriteCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    /*++

    Routine Description:

    This is the completion routine for reads/writes
    If the irp completes with success, we check if we
    need to recirculate this irp for another stage of
    transfer. In this case return STATUS_MORE_PROCESSING_REQUIRED.
    if the irp completes in error, free all memory allocs and
    return the status.

    Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    Context - context passed to the completion routine.

    Return Value:

    NT status value

    --*/
    {
    ULONG stageLength;
    NTSTATUS ntStatus;
    //PIO_STACK_LOCATION nextStack;
    PBULKUSB_RW_CONTEXT rwContext;

    UNREFERENCED_PARAMETER(DeviceObject);
    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion - begins\n"));
    //
    // initialize variables
    //
    rwContext = (PBULKUSB_RW_CONTEXT) Context;

    CardIoDecrement(rwContext->pFdoData);

    ntStatus = Irp->IoStatus.Status;

    //
    // successfully performed a stageLength of transfer.
    // check if we need to recirculate the irp.
    //
    if(NT_SUCCESS(ntStatus))
    {
    if(rwContext)
    {
    rwContext->Numxfer +=
    rwContext->Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
    if(rwContext->Length) {

    //
    // another stage transfer
    //
    CardDebugPrint(INFO, ("Another stage transfer...\n"));

    if(rwContext->Length > BULKUSB_MAX_TRANSFER_SIZE) {

    stageLength = BULKUSB_MAX_TRANSFER_SIZE;
    }
    else {

    stageLength = rwContext->Length;
    }

    // Need to add code for staged tx
    // Don't need this as of now
    }
    else
    {

    //
    // this is the last transfer
    //
    // We are stripping one byte from the Write IRP and then
    // sending on the USB, so adding it back
    Irp->IoStatus.Information = rwContext->Numxfer + 1;

    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion:
    Success"));
    }
    }
    }
    else
    {
    CardDebugPrint(ERROR, ("BulkUsb_ReadWriteCompletion - failed with
    status = (0x%p)\n",
    ntStatus));
    }

    if(rwContext) {

    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion:: Cleaning up
    memory\n"));
    ExFreePool(rwContext->Urb);
    ExFreePool(rwContext);
    }

    CardDebugPrint(INFO, ("BulkUsb_ReadWriteCompletion - ends\n"));

    return ntStatus;
    }
     
    Kabir, Mar 26, 2008
    #4
  5. if you want to resend the irp in your completion routine you need to return
    STATUS_MORE_PROCESSING_REQUIRED from the completion routine, otherwise the
    io manager will complete and eventually free the irp once the irp stack has
    been walked.

    d
     
    Doron Holan [MSFT], Mar 26, 2008
    #5
  6. Irp->Cancel = FALSE;

    You can't do that. You can only read it, not modify.

    You MUST then return STATUS_PENDING, not ntStatus.
     
    Alexander Grigoriev, Mar 27, 2008
    #6
    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.