[USBSTOR]
[reactos.git] / drivers / usb / usbstor / scsi.c
index 8b97c51..68b7aac 100644 (file)
@@ -31,7 +31,7 @@ USBSTOR_BuildCBW(
     Control->Signature = CBW_SIGNATURE;
     Control->Tag = Tag;
     Control->DataTransferLength = DataTransferLength;
-    Control->Flags = 0x80;
+    Control->Flags = (CommandBlock[0] != SCSIOP_WRITE) ? 0x80 : 0x00;
     Control->LUN = (LUN & MAX_LUN);
     Control->CommandBlockLength = CommandBlockLength;
 
@@ -46,192 +46,481 @@ USBSTOR_BuildCBW(
     return STATUS_SUCCESS;
 }
 
+PIRP_CONTEXT
+USBSTOR_AllocateIrpContext()
+{
+    PIRP_CONTEXT Context;
+
+    //
+    // allocate irp context
+    //
+    Context = (PIRP_CONTEXT)AllocateItem(NonPagedPool, sizeof(IRP_CONTEXT));
+    if (!Context)
+    {
+        //
+        // no memory
+        //
+        return NULL;
+    }
+
+    //
+    // allocate cbw block
+    //
+    Context->cbw = (PCBW)AllocateItem(NonPagedPool, 512);
+    if (!Context->cbw)
+    {
+        //
+        // no memory
+        //
+        FreeItem(Context);
+        return NULL;
+    }
+
+    //
+    // done
+    //
+    return Context;
+
+}
+
+//
+// driver verifier
+//
+IO_COMPLETION_ROUTINE USBSTOR_CSWCompletionRoutine;
+
 NTSTATUS
-USBSTOR_SendCBW(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN UCHAR CommandBlockLength,
-    IN PUCHAR CommandBlock,
-    IN ULONG DataTransferLength,
-    OUT PCBW *OutControl)
+NTAPI
+USBSTOR_CSWCompletionRoutine(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp, 
+    PVOID Ctx)
 {
-    PCBW Control;
+    PIRP_CONTEXT Context;
+    PIO_STACK_LOCATION IoStack;
+    PSCSI_REQUEST_BLOCK Request;
+    PCDB pCDB;
+    PREAD_CAPACITY_DATA_EX CapacityDataEx;
+    PREAD_CAPACITY_DATA CapacityData;
+    PUFI_CAPACITY_RESPONSE Response;
+    PERRORHANDLER_WORKITEM_DATA ErrorHandlerWorkItemData;
     NTSTATUS Status;
     PURB Urb;
-    PPDO_DEVICE_EXTENSION PDODeviceExtension;
-    PFDO_DEVICE_EXTENSION FDODeviceExtension;
 
-    //
-    // get PDO device extension
-    //
-    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    DPRINT("USBSTOR_CSWCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx);
 
     //
-    // get FDO device extension
+    // access context
     //
-    FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
+    Context = (PIRP_CONTEXT)Ctx;
 
     //
-    // first allocate CBW
+    // is there a mdl
     //
-    Control = (PCBW)AllocateItem(NonPagedPool, 512);
-    if (!Control)
+    if (Context->TransferBufferMDL)
     {
         //
-        // no memory
+        // is there an irp associated
         //
-        return STATUS_INSUFFICIENT_RESOURCES;
+        if (Context->Irp)
+        {
+            //
+            // did we allocate the mdl
+            //
+            if (Context->TransferBufferMDL != Context->Irp->MdlAddress)
+            {
+                //
+                // free mdl
+                //
+                IoFreeMdl(Context->TransferBufferMDL);
+            }
+        }
+        else
+        {
+            //
+            // free mdl
+            //
+            IoFreeMdl(Context->TransferBufferMDL);
+        }
     }
 
-    //
-    // first allocate CBW
-    //
-    Status = USBSTOR_BuildCBW(0xDEADDEAD, DataTransferLength, PDODeviceExtension->LUN, CommandBlockLength, CommandBlock, Control);
-    if (!NT_SUCCESS(Status))
+    if (Context->Irp)
     {
         //
-        // failed to build CBW
+        // get current stack location
         //
-        return Status;
+        IoStack = IoGetCurrentIrpStackLocation(Context->Irp);
+
+        //
+        // get request block
+        //
+        Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
+        ASSERT(Request);
+
+        Status = Irp->IoStatus.Status;
+
+        Urb = &Context->Urb;
+
+        //
+        // get SCSI command data block
+        //
+        pCDB = (PCDB)Request->Cdb;
+
+        //
+        // check status
+        //
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Status %x\n", Status);
+            DPRINT1("UrbStatus %x\n", Urb->UrbHeader.Status);
+
+            //
+            // Check for errors that can be handled
+            // FIXME: Verify all usb errors that can be recovered via pipe reset/port reset/controller reset
+            //
+            if ((Urb->UrbHeader.Status & USB_RECOVERABLE_ERRORS) == Urb->UrbHeader.Status)
+            {
+                DPRINT1("Attempting Error Recovery\n");
+                //
+                // free the allocated irp
+                //
+                IoFreeIrp(Irp);
+
+                //
+                // Allocate Work Item Data
+                //
+                ErrorHandlerWorkItemData = ExAllocatePoolWithTag(NonPagedPool, sizeof(ERRORHANDLER_WORKITEM_DATA), USB_STOR_TAG);
+                if (!ErrorHandlerWorkItemData)
+                {
+                    DPRINT1("Failed to allocate memory\n");
+                    Status = STATUS_INSUFFICIENT_RESOURCES;
+                }
+                else
+                {
+                    //
+                    // Initialize and queue the work item to handle the error
+                    //
+                    ExInitializeWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem,
+                                        ErrorHandlerWorkItemRoutine,
+                                        ErrorHandlerWorkItemData);
+    
+                    ErrorHandlerWorkItemData->DeviceObject = Context->FDODeviceExtension->FunctionalDeviceObject;
+                    ErrorHandlerWorkItemData->Context = Context;
+                    DPRINT1("Queuing WorkItemROutine\n");
+                    ExQueueWorkItem(&ErrorHandlerWorkItemData->WorkQueueItem, DelayedWorkQueue);
+
+                    return STATUS_MORE_PROCESSING_REQUIRED;
+                }
+            }
+        }
+
+        Request->SrbStatus = SRB_STATUS_SUCCESS;
+
+        //
+        // read capacity needs special work
+        //
+        if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY)
+        {
+            //
+            // get output buffer
+            //
+            Response = (PUFI_CAPACITY_RESPONSE)Context->TransferData;
+
+            //
+            // store in pdo
+            //
+            Context->PDODeviceExtension->BlockLength = NTOHL(Response->BlockLength);
+            Context->PDODeviceExtension->LastLogicBlockAddress = NTOHL(Response->LastLogicalBlockAddress);
+
+            if (Request->DataTransferLength == sizeof(READ_CAPACITY_DATA_EX))
+            {
+                //
+                // get input buffer
+                //
+                CapacityDataEx = (PREAD_CAPACITY_DATA_EX)Request->DataBuffer;
+
+                //
+                // set result
+                //
+                CapacityDataEx->BytesPerBlock = Response->BlockLength;
+                CapacityDataEx->LogicalBlockAddress.QuadPart = Response->LastLogicalBlockAddress;
+                Irp->IoStatus.Information = sizeof(READ_CAPACITY_DATA_EX);
+            }
+            else
+            {
+                //
+                // get input buffer
+                //
+                CapacityData = (PREAD_CAPACITY_DATA)Request->DataBuffer;
+
+                //
+                // set result
+                //
+                CapacityData->BytesPerBlock = Response->BlockLength;
+                CapacityData->LogicalBlockAddress = Response->LastLogicalBlockAddress;
+                Irp->IoStatus.Information = sizeof(READ_CAPACITY_DATA);
+            }
+
+            //
+            // free response
+            //
+            FreeItem(Context->TransferData);
+        }
     }
 
     //
-    // now build the urb
+    // free cbw
     //
-    Urb = (PURB)AllocateItem(NonPagedPool, sizeof(URB));
-    if (!Urb)
+    FreeItem(Context->cbw);
+
+
+    if (Context->Irp)
     {
         //
-        // failed to allocate urb
+        // FIXME: check status
         //
-        FreeItem(Control);
-        return STATUS_INSUFFICIENT_RESOURCES;
+        Context->Irp->IoStatus.Status = Irp->IoStatus.Status;
+        Context->Irp->IoStatus.Information = Context->TransferDataLength;
+
+        //
+        // terminate current request
+        //
+        USBSTOR_QueueTerminateRequest(Context->PDODeviceExtension->LowerDeviceObject, Context->Irp);
+
+        //
+        // complete request
+        //
+        IoCompleteRequest(Context->Irp, IO_NO_INCREMENT);
+
+        //
+        // start next request
+        //
+        USBSTOR_QueueNextRequest(Context->PDODeviceExtension->LowerDeviceObject);
+    }
+
+    if (Context->Event)
+    {
+        //
+        // signal event
+        //
+        KeSetEvent(Context->Event, 0, FALSE);
     }
 
     //
-    // now initialize the urb
+    // free our allocated irp
+    //
+    IoFreeIrp(Irp);
+
+    //
+    // free context
+    //
+    FreeItem(Context);
+
     //
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(URB);
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
-    Urb->UrbBulkOrInterruptTransfer.PipeHandle = FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
-    Urb->UrbBulkOrInterruptTransfer.TransferBuffer = (PVOID)Control;
-    Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(CBW);
-    Urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT | USBD_SHORT_TRANSFER_OK;
+    // done
+    //
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+//
+// driver verifier
+//
+IO_COMPLETION_ROUTINE USBSTOR_DataCompletionRoutine;
+
+NTSTATUS
+NTAPI
+USBSTOR_DataCompletionRoutine(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp, 
+    PVOID Ctx)
+{
+    PIRP_CONTEXT Context;
+    PIO_STACK_LOCATION IoStack;
+
+    DPRINT("USBSTOR_DataCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx);
 
     //
-    // now send urb
+    // access context
     //
-    Status = USBSTOR_SyncUrbRequest(FDODeviceExtension->LowerDeviceObject, Urb);
+    Context = (PIRP_CONTEXT)Ctx;
 
     //
-    // free urb
+    // get next stack location
     //
-    FreeItem(Urb);
+
+    IoStack = IoGetNextIrpStackLocation(Irp);
 
     //
-    // store cbw
+    // now initialize the urb for sending the csw
     //
-    *OutControl = Control;
+    UsbBuildInterruptOrBulkTransferRequest(&Context->Urb,
+                                           sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
+                                           Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle,
+                                           Context->csw,
+                                           NULL,
+                                           512, //FIXME
+                                           USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
+                                           NULL);
 
     //
-    // return operation status
+    // initialize stack location
     //
-    return Status;
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+    IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb;
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+
+    //
+    // setup completion routine
+    //
+    IoSetCompletionRoutine(Irp, USBSTOR_CSWCompletionRoutine, Context, TRUE, TRUE, TRUE);
+
+    //
+    // call driver
+    //
+    IoCallDriver(Context->FDODeviceExtension->LowerDeviceObject, Irp);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
+//
+// driver verifier
+//
+IO_COMPLETION_ROUTINE USBSTOR_CBWCompletionRoutine;
+
 NTSTATUS
-USBSTOR_SendData(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN ULONG DataTransferLength,
-    IN PVOID DataTransfer)
+NTAPI
+USBSTOR_CBWCompletionRoutine(
+    PDEVICE_OBJECT DeviceObject,
+    PIRP Irp, 
+    PVOID Ctx)
 {
-    PMDL TransferBufferMDL;
-    PURB Urb;
-    NTSTATUS Status;
-    PPDO_DEVICE_EXTENSION PDODeviceExtension;
-    PFDO_DEVICE_EXTENSION FDODeviceExtension;
+    PIRP_CONTEXT Context;
+    PIO_STACK_LOCATION IoStack;
+    UCHAR Code;
+    USBD_PIPE_HANDLE PipeHandle;
+
+    DPRINT("USBSTOR_CBWCompletionRoutine Irp %p Ctx %p\n", Irp, Ctx);
 
     //
-    // get PDO device extension
+    // access context
     //
-    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+    Context = (PIRP_CONTEXT)Ctx;
 
     //
-    // get FDO device extension
+    // get next stack location
     //
-    FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
+    IoStack = IoGetNextIrpStackLocation(Irp);
 
     //
-    // allocate mdl for buffer, buffer must be allocated from NonPagedPool
+    // is there data to be submitted
     //
-    TransferBufferMDL = IoAllocateMdl(DataTransfer, DataTransferLength, FALSE, FALSE, NULL);
-    if (!TransferBufferMDL)
+    if (Context->TransferDataLength)
     {
         //
-        // failed to allocate MDL
+        // get command code
         //
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+        Code = Context->cbw->CommandBlock[0];
+
+        if (Code == SCSIOP_WRITE)
+        {
+            //
+            // write request use bulk out pipe
+            // 
+            PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkOutPipeIndex].PipeHandle;
+        }
+        else
+        {
+            //
+            // default bulk in pipe
+            //
+            PipeHandle = Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle;
+        }
 
-    //
-    // build mdl for nonpaged pool
-    //
-    MmBuildMdlForNonPagedPool(TransferBufferMDL);
+        //
+        // now initialize the urb for sending data
+        //
+        UsbBuildInterruptOrBulkTransferRequest(&Context->Urb,
+                                               sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
+                                               PipeHandle,
+                                               NULL,
+                                               Context->TransferBufferMDL,
+                                               Context->TransferDataLength,
+                                               ((Code == SCSIOP_WRITE) ? USBD_TRANSFER_DIRECTION_OUT : (USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK)),
+                                               NULL);
 
-    //
-    // now build the urb
-    //
-    Urb = (PURB)AllocateItem(NonPagedPool, sizeof(URB));
-    if (!Urb)
-    {
         //
-        // failed to allocate urb
+        // setup completion routine
         //
-        IoFreeMdl(TransferBufferMDL);
-        return STATUS_INSUFFICIENT_RESOURCES;
+        IoSetCompletionRoutine(Irp, USBSTOR_DataCompletionRoutine, Context, TRUE, TRUE, TRUE);
     }
+    else
+    {
+        //
+        // now initialize the urb for sending the csw
+        //
 
-    //
-    // now initialize the urb
-    //
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(URB);
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
-    Urb->UrbBulkOrInterruptTransfer.PipeHandle = FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle;
-    Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = TransferBufferMDL;
-    Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DataTransferLength;
-    Urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
+        UsbBuildInterruptOrBulkTransferRequest(&Context->Urb,
+                                               sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
+                                               Context->FDODeviceExtension->InterfaceInformation->Pipes[Context->FDODeviceExtension->BulkInPipeIndex].PipeHandle,
+                                               Context->csw,
+                                               NULL,
+                                               512, //FIXME
+                                               USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK,
+                                               NULL);
 
-    //
-    // now send urb
-    //
-    Status = USBSTOR_SyncUrbRequest(FDODeviceExtension->LowerDeviceObject, Urb);
+        //
+        // setup completion routine
+        //
+        IoSetCompletionRoutine(Irp, USBSTOR_CSWCompletionRoutine, Context, TRUE, TRUE, TRUE);
+    }
 
     //
-    // free urb
+    // initialize stack location
     //
-    FreeItem(Urb);
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+    IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb;
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
 
     //
-    // free mdl
+    // call driver
     //
-    IoFreeMdl(TransferBufferMDL);
+    IoCallDriver(Context->FDODeviceExtension->LowerDeviceObject, Irp);
 
-    //
-    // done
-    //
-    return Status;
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
 NTSTATUS
-USBSTOR_SendCSW(
+USBSTOR_SendRequest(
     IN PDEVICE_OBJECT DeviceObject,
-    IN PVOID Data,
-    IN ULONG DataLength,
-    OUT PCSW OutCSW)
+    IN PIRP OriginalRequest,
+    IN OPTIONAL PKEVENT Event,
+    IN UCHAR CommandLength,
+    IN PUCHAR Command,
+    IN ULONG TransferDataLength,
+    IN PUCHAR TransferData)
 {
-    NTSTATUS Status;
+    PIRP_CONTEXT Context;
     PPDO_DEVICE_EXTENSION PDODeviceExtension;
     PFDO_DEVICE_EXTENSION FDODeviceExtension;
-    PURB Urb;
+    PIRP Irp;
+    PIO_STACK_LOCATION IoStack;
+    PUCHAR MdlVirtualAddress;
+
+    //
+    // first allocate irp context
+    //
+    Context = USBSTOR_AllocateIrpContext();
+    if (!Context)
+    {
+        //
+        // no memory
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
     //
     // get PDO device extension
@@ -244,49 +533,177 @@ USBSTOR_SendCSW(
     FDODeviceExtension = (PFDO_DEVICE_EXTENSION)PDODeviceExtension->LowerDeviceObject->DeviceExtension;
 
     //
-    // now build the urb
+    // now build the cbw
     //
-    Urb = (PURB)AllocateItem(NonPagedPool, sizeof(URB));
-    if (!Urb)
+    USBSTOR_BuildCBW(0xDEADDEAD, // FIXME tag
+                     TransferDataLength,
+                     PDODeviceExtension->LUN,
+                     CommandLength,
+                     Command,
+                     Context->cbw);
+
+    //
+    // now initialize the urb
+    //
+    UsbBuildInterruptOrBulkTransferRequest(&Context->Urb,
+                                           sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
+                                           FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkOutPipeIndex].PipeHandle,
+                                           Context->cbw,
+                                           NULL,
+                                           sizeof(CBW),
+                                           USBD_TRANSFER_DIRECTION_OUT,
+                                           NULL);
+
+    //
+    // initialize rest of context
+    //
+    Context->Irp = OriginalRequest;
+    Context->TransferData = TransferData;
+    Context->TransferDataLength = TransferDataLength;
+    Context->FDODeviceExtension = FDODeviceExtension;
+    Context->PDODeviceExtension = PDODeviceExtension;
+    Context->Event = Event;
+
+    //
+    // is there transfer data
+    //
+    if (Context->TransferDataLength)
     {
         //
-        // failed to allocate urb
+        // check if the original request already does have an mdl associated
         //
+        if (OriginalRequest)
+        {
+            if ((OriginalRequest->MdlAddress != NULL) &&
+                (Context->TransferData == NULL || Command[0] == SCSIOP_READ || Command[0] == SCSIOP_WRITE))
+            {
+                //
+                // Sanity check that the Mdl does describe the TransferData for read/write
+                //
+                if (CommandLength == UFI_READ_WRITE_CMD_LEN)
+                {
+                    MdlVirtualAddress = MmGetMdlVirtualAddress(OriginalRequest->MdlAddress);
+
+                    //
+                    // is there an offset
+                    //
+                    if (MdlVirtualAddress != Context->TransferData)
+                    {
+                        //
+                        // lets build an mdl
+                        //
+                        Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, MmGetMdlByteCount(OriginalRequest->MdlAddress), FALSE, FALSE, NULL);
+                        if (!Context->TransferBufferMDL)
+                        {
+                            //
+                            // failed to allocate MDL
+                            //
+                            return STATUS_INSUFFICIENT_RESOURCES;
+                        }
+
+                        //
+                        // now build the partial mdl
+                        //
+                        IoBuildPartialMdl(OriginalRequest->MdlAddress, Context->TransferBufferMDL, Context->TransferData, Context->TransferDataLength);
+                    }
+                }
+
+                if (!Context->TransferBufferMDL)
+                {
+                    //
+                    // I/O paging request
+                    //
+                    Context->TransferBufferMDL = OriginalRequest->MdlAddress;
+                }
+            }
+            else
+            {
+                //
+                // allocate mdl for buffer, buffer must be allocated from NonPagedPool
+                //
+                Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, Context->TransferDataLength, FALSE, FALSE, NULL);
+                if (!Context->TransferBufferMDL)
+                {
+                    //
+                    // failed to allocate MDL
+                    //
+                    return STATUS_INSUFFICIENT_RESOURCES;
+                }
+
+                //
+                // build mdl for nonpaged pool
+                //
+                MmBuildMdlForNonPagedPool(Context->TransferBufferMDL);
+            }
+        }
+        else
+        {
+            //
+            // allocate mdl for buffer, buffer must be allocated from NonPagedPool
+            //
+            Context->TransferBufferMDL = IoAllocateMdl(Context->TransferData, Context->TransferDataLength, FALSE, FALSE, NULL);
+            if (!Context->TransferBufferMDL)
+            {
+                //
+                // failed to allocate MDL
+                //
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            //
+            // build mdl for nonpaged pool
+            //
+            MmBuildMdlForNonPagedPool(Context->TransferBufferMDL);
+        }
+    }
+
+    //
+    // now allocate the request
+    //
+    Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+    if (!Irp)
+    {
+        FreeItem(Context->cbw);
+        FreeItem(Context);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
     //
-    // now initialize the urb
+    // get next stack location
     //
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(URB);
-    Urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
-    Urb->UrbBulkOrInterruptTransfer.PipeHandle = FDODeviceExtension->InterfaceInformation->Pipes[FDODeviceExtension->BulkInPipeIndex].PipeHandle;
-    Urb->UrbBulkOrInterruptTransfer.TransferBuffer = Data;
-    Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DataLength;
-    Urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;
+    IoStack = IoGetNextIrpStackLocation(Irp);
 
     //
-    // now send urb
+    // initialize stack location
     //
-    Status = USBSTOR_SyncUrbRequest(FDODeviceExtension->LowerDeviceObject, Urb);
+    IoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+    IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
+    IoStack->Parameters.Others.Argument1 = (PVOID)&Context->Urb;
+    IoStack->Parameters.DeviceIoControl.InputBufferLength = Context->Urb.UrbHeader.Length;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
 
-    if (NT_SUCCESS(Status))
+    //
+    // setup completion routine
+    //
+    IoSetCompletionRoutine(Irp, USBSTOR_CBWCompletionRoutine, Context, TRUE, TRUE, TRUE);
+
+    if (OriginalRequest)
     {
         //
-        // copy csw status
+        // mark orignal irp as pending
         //
-        RtlCopyMemory(OutCSW, Data, sizeof(CSW));
+        IoMarkIrpPending(OriginalRequest);
     }
 
     //
-    // free urb
+    // call driver
     //
-    FreeItem(Urb);
+    IoCallDriver(FDODeviceExtension->LowerDeviceObject, Irp);
 
     //
     // done
     //
-    return Status;
+    return STATUS_PENDING;
 }
 
 NTSTATUS
@@ -294,21 +711,16 @@ USBSTOR_SendInquiryCmd(
     IN PDEVICE_OBJECT DeviceObject)
 {
     UFI_INQUIRY_CMD Cmd;
-    CSW CSW;
     NTSTATUS Status;
-    PUFI_INQUIRY_RESPONSE Response;
+    KEVENT Event;
     PPDO_DEVICE_EXTENSION PDODeviceExtension;
-    PCBW OutControl;
+    PUFI_INQUIRY_RESPONSE Response;
 
-    //
-    // get PDO device extension
-    //
-    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
 
     //
     // allocate inquiry response
     //
-    Response = (PUFI_INQUIRY_RESPONSE)AllocateItem(NonPagedPool, sizeof(UFI_INQUIRY_RESPONSE));
+    Response = AllocateItem(NonPagedPool, PAGE_SIZE);
     if (!Response)
     {
         //
@@ -317,6 +729,11 @@ USBSTOR_SendInquiryCmd(
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    //
+    // get PDO device extension
+    //
+    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
     //
     // initialize inquiry cmd
     //
@@ -326,34 +743,19 @@ USBSTOR_SendInquiryCmd(
     Cmd.AllocationLength = sizeof(UFI_INQUIRY_RESPONSE);
 
     //
-    // now send inquiry cmd
+    // initialize event
     //
-    Status = USBSTOR_SendCBW(DeviceObject, UFI_INQUIRY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_INQUIRY_RESPONSE), &OutControl);
-    if (!NT_SUCCESS(Status))
-    {
-        //
-        // failed to send CBW
-        //
-        DPRINT1("USBSTOR_SendInquiryCmd> USBSTOR_SendCBW failed with %x\n", Status);
-        FreeItem(Response);
-        ASSERT(FALSE);
-        return Status;
-    }
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
 
     //
-    // now send inquiry response
+    // now send the request
     //
-    Status = USBSTOR_SendData(DeviceObject, sizeof(UFI_INQUIRY_RESPONSE), Response);
-    if (!NT_SUCCESS(Status))
-    {
-        //
-        // failed to send CBW
-        //
-        DPRINT1("USBSTOR_SendInquiryCmd> USBSTOR_SendData failed with %x\n", Status);
-        FreeItem(Response);
-        ASSERT(FALSE);
-        return Status;
-    }
+    Status = USBSTOR_SendRequest(DeviceObject, NULL, &Event, UFI_INQUIRY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_INQUIRY_RESPONSE), (PUCHAR)Response);
+
+    //
+    // wait for the action to complete
+    //
+    KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
 
     DPRINT1("Response %p\n", Response);
     DPRINT1("DeviceType %x\n", Response->DeviceType);
@@ -370,34 +772,11 @@ USBSTOR_SendInquiryCmd(
 
     DPRINT1("Revision %c%c%c%c\n", Response->Revision[0], Response->Revision[1], Response->Revision[2], Response->Revision[3]);
 
-    //
-    // send csw
-    //
-    Status = USBSTOR_SendCSW(DeviceObject, OutControl, 512, &CSW);
-
-    DPRINT1("------------------------\n");
-    DPRINT1("CSW %p\n", &CSW);
-    DPRINT1("Signature %x\n", CSW.Signature);
-    DPRINT1("Tag %x\n", CSW.Tag);
-    DPRINT1("DataResidue %x\n", CSW.DataResidue);
-    DPRINT1("Status %x\n", CSW.Status);
-
-    //
-    // free item
-    //
-    FreeItem(OutControl);
-
     //
     // store inquiry data
     //
     PDODeviceExtension->InquiryData = (PVOID)Response;
 
-    //
-    // FIXME: handle error
-    //
-    ASSERT(CSW.Status == 0);
-
-
     //
     // done
     //
@@ -407,15 +786,11 @@ USBSTOR_SendInquiryCmd(
 NTSTATUS
 USBSTOR_SendCapacityCmd(
     IN PDEVICE_OBJECT DeviceObject,
-    OUT PREAD_CAPACITY_DATA_EX CapacityDataEx,
-    OUT PREAD_CAPACITY_DATA CapacityData)
+    IN PIRP Irp)
 {
     UFI_CAPACITY_CMD Cmd;
-    CSW CSW;
-    NTSTATUS Status;
     PUFI_CAPACITY_RESPONSE Response;
     PPDO_DEVICE_EXTENSION PDODeviceExtension;
-    PCBW OutControl;
 
     //
     // get PDO device extension
@@ -442,101 +817,63 @@ USBSTOR_SendCapacityCmd(
     Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN);
 
     //
-    // now send capacity cmd
-    //
-    Status = USBSTOR_SendCBW(DeviceObject, UFI_CAPACITY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_CAPACITY_RESPONSE), &OutControl);
-    if (!NT_SUCCESS(Status))
-    {
-        //
-        // failed to send CBW
-        //
-        DPRINT1("USBSTOR_SendCapacityCmd> USBSTOR_SendCBW failed with %x\n", Status);
-        FreeItem(Response);
-        ASSERT(FALSE);
-        return Status;
-    }
-
-    //
-    // now send inquiry response
+    // send request, response will be freed in completion routine
     //
-    Status = USBSTOR_SendData(DeviceObject, sizeof(UFI_CAPACITY_RESPONSE), Response);
-    if (!NT_SUCCESS(Status))
-    {
-        //
-        // failed to send CBW
-        //
-        DPRINT1("USBSTOR_SendCapacityCmd> USBSTOR_SendData failed with %x\n", Status);
-        FreeItem(Response);
-        ASSERT(FALSE);
-        return Status;
-    }
+    return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_INQUIRY_CMD_LEN, (PUCHAR)&Cmd, sizeof(UFI_CAPACITY_RESPONSE), (PUCHAR)Response);
+}
 
-    DPRINT1("LastLogicalBlockAddress %lu\n", NTOHL(Response->LastLogicalBlockAddress));
-    DPRINT1("BlockLength %lu\n", NTOHL(Response->BlockLength));
-    DPRINT1("Medium Length %lu\n", NTOHL(Response->BlockLength) * NTOHL(Response->LastLogicalBlockAddress));
+NTSTATUS
+USBSTOR_SendModeSenseCmd(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+#if 0
+    UFI_SENSE_CMD Cmd;
+    NTSTATUS Status;
+    PVOID Response;
+    PCBW OutControl;
+    PCDB pCDB;
+    PUFI_MODE_PARAMETER_HEADER Header;
+#endif
+    PPDO_DEVICE_EXTENSION PDODeviceExtension;
+    PIO_STACK_LOCATION IoStack;
+    PSCSI_REQUEST_BLOCK Request;
 
     //
-    // store response
+    // get PDO device extension
     //
-    if (CapacityDataEx)
-    {
-        CapacityDataEx->LogicalBlockAddress.QuadPart = Response->LastLogicalBlockAddress;
-        CapacityDataEx->BytesPerBlock = Response->BlockLength;
-    }
-    else
-    {
-        CapacityData->LogicalBlockAddress = Response->LastLogicalBlockAddress;
-        CapacityData->BytesPerBlock = Response->BlockLength;
-    }
+    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
 
     //
-    // send csw
+    // sanity check
     //
-    Status = USBSTOR_SendCSW(DeviceObject, OutControl, 512, &CSW);
-
-    DPRINT1("------------------------\n");
-    DPRINT1("CSW %p\n", &CSW);
-    DPRINT1("Signature %x\n", CSW.Signature);
-    DPRINT1("Tag %x\n", CSW.Tag);
-    DPRINT1("DataResidue %x\n", CSW.DataResidue);
-    DPRINT1("Status %x\n", CSW.Status);
+    ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
 
     //
-    // FIXME: handle error
+    // get current stack location
     //
-    ASSERT(CSW.Status == 0);
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
 
     //
-    // free item
+    // get request block
     //
-    FreeItem(OutControl);
+    Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
 
-    //
-    // free response
-    //
-    FreeItem(Response);
+    RtlZeroMemory(Request->DataBuffer, Request->DataTransferLength);
+    Request->SrbStatus = SRB_STATUS_SUCCESS;
+    Irp->IoStatus.Information = Request->DataTransferLength;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
     //
-    // done
+    // start next request
     //
-    return Status;
-}
+    USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
 
-NTSTATUS
-USBSTOR_SendModeSenseCmd(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN OUT PSCSI_REQUEST_BLOCK Request,
-    OUT PULONG TransferBufferLength)
-{
-    UFI_SENSE_CMD Cmd;
-    CSW CSW;
-    NTSTATUS Status;
-    PVOID Response;
-    PPDO_DEVICE_EXTENSION PDODeviceExtension;
-    PCBW OutControl;
-    PCDB pCDB;
-    PUFI_MODE_PARAMETER_HEADER Header;
+    return STATUS_SUCCESS;
 
+#if 0
     //
     // get SCSI command data block
     //
@@ -626,7 +963,7 @@ USBSTOR_SendModeSenseCmd(
     // MODE_PARAMETER_BLOCK
     //
     // 
-       DbgBreakPoint();
+    UNIMPLEMENTED
 
     //
     // send csw
@@ -670,5 +1007,225 @@ USBSTOR_SendModeSenseCmd(
     // done
     //
     return Status;
+#endif
 }
 
+NTSTATUS
+USBSTOR_SendReadWriteCmd(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    UFI_READ_WRITE_CMD Cmd;
+    PPDO_DEVICE_EXTENSION PDODeviceExtension;
+    PCDB pCDB;
+    ULONG BlockCount;
+    PIO_STACK_LOCATION IoStack;
+    PSCSI_REQUEST_BLOCK Request;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // get request block
+    //
+    Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
+
+    //
+    // get SCSI command data block
+    //
+    pCDB = (PCDB)Request->Cdb;
+
+    //
+    // get PDO device extension
+    //
+    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // informal debug print
+    //
+    DPRINT("USBSTOR_SendReadWriteCmd DataTransferLength %lu, BlockLength %lu\n", Request->DataTransferLength, PDODeviceExtension->BlockLength);
+
+    //
+    // sanity check
+    //
+    ASSERT(PDODeviceExtension->BlockLength);
+
+    //
+    // block count
+    //
+    BlockCount = Request->DataTransferLength / PDODeviceExtension->BlockLength;
+
+    //
+    // initialize read cmd
+    //
+    RtlZeroMemory(&Cmd, sizeof(UFI_READ_WRITE_CMD));
+    Cmd.Code = pCDB->AsByte[0];
+    Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN);
+    Cmd.ContiguousLogicBlocksByte0 = pCDB->CDB10.TransferBlocksMsb;
+    Cmd.ContiguousLogicBlocksByte1 = pCDB->CDB10.TransferBlocksLsb;
+    Cmd.LogicalBlockByte0 = pCDB->CDB10.LogicalBlockByte0;
+    Cmd.LogicalBlockByte1 = pCDB->CDB10.LogicalBlockByte1;
+    Cmd.LogicalBlockByte2 = pCDB->CDB10.LogicalBlockByte2;
+    Cmd.LogicalBlockByte3 = pCDB->CDB10.LogicalBlockByte3;
+
+    DPRINT("USBSTOR_SendReadWriteCmd BlockAddress %x%x%x%x BlockCount %lu BlockLength %lu\n", Cmd.LogicalBlockByte0, Cmd.LogicalBlockByte1, Cmd.LogicalBlockByte2, Cmd.LogicalBlockByte3, BlockCount, PDODeviceExtension->BlockLength);
+
+    //
+    // send request
+    //
+    return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_READ_WRITE_CMD_LEN, (PUCHAR)&Cmd, Request->DataTransferLength, (PUCHAR)Request->DataBuffer);
+}
+
+NTSTATUS
+USBSTOR_SendTestUnitCmd(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN OUT PIRP Irp)
+{
+    UFI_TEST_UNIT_CMD Cmd;
+    PPDO_DEVICE_EXTENSION PDODeviceExtension;
+    PIO_STACK_LOCATION IoStack;
+    PSCSI_REQUEST_BLOCK Request;
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // get request block
+    //
+    Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
+
+    //
+    // no transfer length
+    //
+    ASSERT(Request->DataTransferLength == 0);
+
+    //
+    // get PDO device extension
+    //
+    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // initialize test unit cmd
+    //
+    RtlZeroMemory(&Cmd, sizeof(UFI_TEST_UNIT_CMD));
+    Cmd.Code = SCSIOP_TEST_UNIT_READY;
+    Cmd.LUN = (PDODeviceExtension->LUN & MAX_LUN);
+
+    //
+    // send the request
+    //
+    return USBSTOR_SendRequest(DeviceObject, Irp, NULL, UFI_TEST_UNIT_CMD_LEN, (PUCHAR)&Cmd, 0, NULL);
+}
+
+
+NTSTATUS
+USBSTOR_HandleExecuteSCSI(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PIRP Irp)
+{
+    PCDB pCDB;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStack;
+    PSCSI_REQUEST_BLOCK Request;
+    PPDO_DEVICE_EXTENSION PDODeviceExtension;
+
+    //
+    // get PDO device extension
+    //
+    PDODeviceExtension = (PPDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    //
+    // sanity check
+    //
+    ASSERT(PDODeviceExtension->Common.IsFDO == FALSE);
+
+    //
+    // get current stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(Irp);
+
+    //
+    // get request block
+    //
+    Request = (PSCSI_REQUEST_BLOCK)IoStack->Parameters.Others.Argument1;
+
+    //
+    // get SCSI command data block
+    //
+    pCDB = (PCDB)Request->Cdb;
+
+    DPRINT("USBSTOR_HandleExecuteSCSI Operation Code %x\n", pCDB->AsByte[0]);
+
+    if (pCDB->AsByte[0] == SCSIOP_READ_CAPACITY)
+    {
+        //
+        // sanity checks
+        //
+        ASSERT(Request->DataBuffer);
+
+        DPRINT("SCSIOP_READ_CAPACITY Length %\n", Request->DataTransferLength);
+        Status = USBSTOR_SendCapacityCmd(DeviceObject, Irp);
+    }
+    else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE)
+    {
+        DPRINT1("SCSIOP_MODE_SENSE DataTransferLength %lu\n", Request->DataTransferLength);
+        ASSERT(pCDB->MODE_SENSE.AllocationLength == Request->DataTransferLength);
+        ASSERT(Request->DataBuffer);
+
+        //
+        // send mode sense command
+        //
+        Status = USBSTOR_SendModeSenseCmd(DeviceObject, Irp);
+    }
+    else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_READ ||  pCDB->MODE_SENSE.OperationCode == SCSIOP_WRITE)
+    {
+        DPRINT("SCSIOP_READ / SCSIOP_WRITE DataTransferLength %lu\n", Request->DataTransferLength);
+
+        //
+        // send read / write command
+        //
+        Status = USBSTOR_SendReadWriteCmd(DeviceObject, Irp);
+    }
+    else if (pCDB->AsByte[0] == SCSIOP_MEDIUM_REMOVAL)
+    {
+        DPRINT("SCSIOP_MEDIUM_REMOVAL\n");
+
+        //
+        // just complete the request
+        //
+        Request->SrbStatus = SRB_STATUS_SUCCESS;
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+        Irp->IoStatus.Information = Request->DataTransferLength;
+        USBSTOR_QueueTerminateRequest(PDODeviceExtension->LowerDeviceObject, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        //
+        // start next request
+        //
+        USBSTOR_QueueNextRequest(PDODeviceExtension->LowerDeviceObject);
+
+        return STATUS_SUCCESS;
+    }
+    else if (pCDB->MODE_SENSE.OperationCode == SCSIOP_TEST_UNIT_READY)
+    {
+        DPRINT("SCSIOP_TEST_UNIT_READY\n");
+
+        //
+        // send test unit command
+        //
+        Status = USBSTOR_SendTestUnitCmd(DeviceObject, Irp);
+    }
+    else
+    {
+        DPRINT1("UNIMPLEMENTED Operation Code %x\n", pCDB->AsByte[0]);
+        Request->SrbStatus = SRB_STATUS_ERROR;
+        Status = STATUS_NOT_SUPPORTED;
+        DbgBreakPoint();
+    }
+
+    return Status;
+}