[USBEHCI_NEW]
[reactos.git] / drivers / usb / usbehci_new / usb_request.cpp
index a02b7e7..38618d2 100644 (file)
@@ -36,7 +36,7 @@ public:
     }
 
     // IUSBRequest interface functions
-    virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer);
+    virtual NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN UCHAR DeviceAddress, IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer);
     virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, IN OUT PIRP Irp);
     virtual VOID CompletionCallback(IN NTSTATUS NtStatusCode, IN ULONG UrbStatusCode, IN struct _QUEUE_HEAD *QueueHead);
     virtual VOID CancelCallback(IN NTSTATUS NtStatusCode, IN struct _QUEUE_HEAD *QueueHead);
@@ -47,16 +47,21 @@ public:
     virtual BOOLEAN IsRequestInitialized();
     virtual BOOLEAN ShouldReleaseRequestAfterCompletion();
     virtual VOID FreeQueueHead(struct _QUEUE_HEAD * QueueHead);
+    virtual VOID GetTransferBuffer(OUT PMDL * OutMDL, OUT PULONG TransferLength);
+    virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
+
 
     // local functions
     ULONG InternalGetTransferType();
+    UCHAR InternalGetPidDirection();
     NTSTATUS BuildControlTransferQueueHead(PQUEUE_HEAD * OutHead);
     NTSTATUS BuildBulkTransferQueueHead(PQUEUE_HEAD * OutHead);
     NTSTATUS CreateDescriptor(PQUEUE_TRANSFER_DESCRIPTOR *OutDescriptor);
     NTSTATUS CreateQueueHead(PQUEUE_HEAD *OutQueueHead);
-    ULONG GetDeviceAddress();
+    UCHAR GetDeviceAddress();
     NTSTATUS BuildSetupPacket();
     NTSTATUS BuildSetupPacketFromURB();
+    ULONG InternalCalculateTransferLength();
 
     // constructor / destructor
     CUSBRequest(IUnknown *OuterUnknown){}
@@ -80,6 +85,16 @@ protected:
     //
     ULONG m_TransferBufferLength;
 
+    //
+    // current transfer length
+    //
+    ULONG m_TransferBufferLengthCompleted;
+
+    //
+    // Total Transfer Length
+    //
+    ULONG m_TotalBytesTransferred;
+
     //
     // transfer buffer MDL
     //
@@ -95,6 +110,16 @@ protected:
     //
     PKEVENT m_CompletionEvent;
 
+    //
+    // device address for callers who initialized it with device address
+    //
+    UCHAR m_DeviceAddress;
+
+    //
+    // store end point address
+    //
+    PUSB_ENDPOINT_DESCRIPTOR m_EndpointDescriptor;
+
     //
     // DMA queue head
     //
@@ -134,6 +159,8 @@ NTSTATUS
 CUSBRequest::InitializeWithSetupPacket(
     IN PDMAMEMORYMANAGER DmaManager,
     IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket,
+    IN UCHAR DeviceAddress,
+    IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor,
     IN OUT ULONG TransferBufferLength,
     IN OUT PMDL TransferBuffer)
 {
@@ -150,6 +177,14 @@ CUSBRequest::InitializeWithSetupPacket(
     m_SetupPacket = SetupPacket;
     m_TransferBufferLength = TransferBufferLength;
     m_TransferBufferMDL = TransferBuffer;
+    m_DeviceAddress = DeviceAddress;
+    m_EndpointDescriptor = EndpointDescriptor;
+    m_TotalBytesTransferred = 0;
+
+    //
+    // Set Length Completed to 0
+    //
+    m_TransferBufferLengthCompleted = 0;
 
     //
     // allocate completion event
@@ -188,6 +223,9 @@ CUSBRequest::InitializeWithIrp(
     PC_ASSERT(DmaManager);
     PC_ASSERT(Irp);
 
+    m_DmaManager = DmaManager;
+    m_TotalBytesTransferred = 0;
+
     //
     // get current irp stack location
     //
@@ -223,20 +261,68 @@ CUSBRequest::InitializeWithIrp(
         case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
         {
             //
-            // bulk interrupt transfer
+            // bulk interrupt transfer
             //
             if (Urb->UrbBulkOrInterruptTransfer.TransferBufferLength)
             {
                 //
-                // FIXME: it must have an MDL
+                // Check if there is a MDL
                 //
-                PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL);
+                if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
+                {
+                    //
+                    // sanity check
+                    //
+                    PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer);
+
+                    //
+                    // Create one using TransferBuffer
+                    //
+                    DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
+                    m_TransferBufferMDL = IoAllocateMdl(Urb->UrbBulkOrInterruptTransfer.TransferBuffer,
+                                                        Urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
+                                                        FALSE,
+                                                        FALSE,
+                                                        NULL);
+
+                    if (!m_TransferBufferMDL)
+                    {
+                        //
+                        // failed to allocate mdl
+                        //
+                        return STATUS_INSUFFICIENT_RESOURCES;
+                    }
+
+                    //
+                    // build mdl for non paged pool
+                    // FIXME: Does hub driver already do this when passing MDL?
+                    //
+                    MmBuildMdlForNonPagedPool(m_TransferBufferMDL);
+
+                    //
+                    // Keep that ehci created the MDL and needs to free it.
+                    //
+                }
+                else
+                {
+                    m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
+                }
 
                 //
-                // get mdl buffer
+                // save buffer length
                 //
-                m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
                 m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+
+                //
+                // Set Length Completed to 0
+                //
+                m_TransferBufferLengthCompleted = 0;
+
+                //
+                // get endpoint descriptor
+                //
+                m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
+
             }
             break;
         }
@@ -294,6 +380,17 @@ CUSBRequest::CompletionCallback(
         //
         Urb->UrbHeader.Status = UrbStatusCode;
 
+        //
+        // Check if the MDL was created
+        //
+        if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
+        {
+            //
+            // Free Mdl
+            //
+            IoFreeMdl(m_TransferBufferMDL);
+        }
+
         //
         // check if the request was successfull
         //
@@ -304,6 +401,15 @@ CUSBRequest::CompletionCallback(
             //
             Urb->UrbHeader.Length = 0;
         }
+        else
+        {
+            //
+            // calculate transfer length
+            //
+            Urb->UrbBulkOrInterruptTransfer.TransferBufferLength = InternalCalculateTransferLength();
+        }
+
+        DPRINT("Request %p Completing Irp %p NtStatusCode %x UrbStatusCode %x Transferred Length %lu\n", this, m_Irp, NtStatusCode, UrbStatusCode, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
 
         //
         // FIXME: check if the transfer was split
@@ -358,6 +464,7 @@ CUSBRequest::CancelCallback(
         //
         // store urb status
         //
+        DPRINT1("Request Cancelled\n");
         Urb->UrbHeader.Status = USBD_STATUS_CANCELED;
         Urb->UrbHeader.Length = 0;
 
@@ -440,7 +547,18 @@ CUSBRequest::IsRequestComplete()
     //
     // FIXME: check if request was split
     //
-    UNIMPLEMENTED
+
+    //
+    // Check if the transfer was completed, only valid for Bulk Transfers
+    //
+    if ((m_TransferBufferLengthCompleted < m_TransferBufferLength)
+        && (GetTransferType() == USB_ENDPOINT_TYPE_BULK))
+    {
+        //
+        // Transfer not completed
+        //
+        return FALSE;
+    }
     return TRUE;
 }
 //----------------------------------------------------------------------------------------
@@ -457,9 +575,6 @@ CUSBRequest::GetTransferType()
 ULONG
 CUSBRequest::InternalGetTransferType()
 {
-    PIO_STACK_LOCATION IoStack;
-    PURB Urb;
-    PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
     ULONG TransferType;
 
     //
@@ -467,38 +582,12 @@ CUSBRequest::InternalGetTransferType()
     //
     if (m_Irp)
     {
-        //
-        // get stack location
-        //
-        IoStack = IoGetCurrentIrpStackLocation(m_Irp);
+        ASSERT(m_EndpointDescriptor);
 
         //
-        // get urb
+        // end point is defined in the low byte of bmAttributes
         //
-        Urb = (PURB)IoStack->Parameters.Others.Argument1;
-
-        //
-        // check if there is a handle
-        //
-        if (Urb->UrbBulkOrInterruptTransfer.PipeHandle)
-        {
-            //
-            // cast to end point
-            //
-            EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
-
-            //
-            // end point is defined in the low byte of bmAttributes
-            //
-            TransferType = (EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK);
-        }
-        else
-        {
-            //
-            // no pipe handle, assume it is a control transfer
-            //
-            TransferType = USB_ENDPOINT_TYPE_CONTROL;
-        }
+        TransferType = (m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK);
     }
     else
     {
@@ -514,6 +603,18 @@ CUSBRequest::InternalGetTransferType()
     return TransferType;
 }
 
+UCHAR
+CUSBRequest::InternalGetPidDirection()
+{
+    ASSERT(m_Irp);
+    ASSERT(m_EndpointDescriptor);
+
+    //
+    // end point is defined in the low byte of bEndpointAddress
+    //
+    return (m_EndpointDescriptor->bEndpointAddress & USB_ENDPOINT_DIRECTION_MASK) >> 7;
+}
+
 //----------------------------------------------------------------------------------------
 NTSTATUS
 CUSBRequest::BuildControlTransferQueueHead(
@@ -535,6 +636,11 @@ CUSBRequest::BuildControlTransferQueueHead(
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
+    //
+    // sanity check
+    //
+    PC_ASSERT(QueueHead);
+
     //
     // create setup packet
     //
@@ -574,9 +680,15 @@ CUSBRequest::BuildControlTransferQueueHead(
     // now initialize the queue head
     //
     QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
-    
-       //if (PipeHandle)
-    //    QueueHead->EndPointCharacteristics.EndPointNumber = ((PUSB_ENDPOINT_DESCRIPTOR)PipeHandle)->bEndpointAddress & 0x0F;
+
+    if (m_EndpointDescriptor)
+    {
+        //
+        // set endpoint address and max packet length
+        //
+        QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->bEndpointAddress & 0x0F;
+        QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->wMaxPacketSize;
+    }
 
     QueueHead->Token.Bits.DataToggle = TRUE;
 
@@ -595,6 +707,12 @@ CUSBRequest::BuildControlTransferQueueHead(
         m_TransferDescriptors[1]->Token.Bits.PIDCode = PID_CODE_IN_TOKEN;
         m_TransferDescriptors[1]->Token.Bits.TotalBytesToTransfer = m_TransferBufferLength;
 
+        //
+        // FIXME: check if the request spawns over a page -> fill other members
+        //
+        PC_ASSERT(m_TransferBufferLength <= PAGE_SIZE);
+        m_TransferDescriptors[1]->BufferPointer[0] = MmGetPhysicalAddress(MmGetMdlVirtualAddress(m_TransferBufferMDL)).LowPart;
+
         //
         // setup out descriptor
         //
@@ -640,12 +758,6 @@ CUSBRequest::BuildControlTransferQueueHead(
         m_TransferDescriptors[1]->Token.Bits.InterruptOnComplete = TRUE;
     }
 
-    //
-    // Control Transfers have only one in or out buffer if one at all. Put in the QueueHead the
-    // same as BulkTransfers. USBQueue will use the Mdl to fill in the BufferPointers
-    //
-    QueueHead->Mdl = m_TransferBufferMDL;
-
     //
     // link setup packet into buffer - Physical Address!!!
     //
@@ -672,8 +784,295 @@ NTSTATUS
 CUSBRequest::BuildBulkTransferQueueHead(
     PQUEUE_HEAD * OutHead)
 {
-    UNIMPLEMENTED
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PQUEUE_HEAD QueueHead;
+    ULONG TransferDescriptorCount, Index;
+    ULONG BytesAvailable, BufferIndex;
+    PVOID Base;
+    ULONG PageOffset, CurrentTransferBufferLength;
+
+    //
+    // Allocate the queue head
+    //
+    Status = CreateQueueHead(&QueueHead);
+
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to allocate queue heads
+        //
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    //
+    // sanity checks
+    //
+    PC_ASSERT(QueueHead);
+    PC_ASSERT(m_TransferBufferLength);
+
+    //
+    // Max default of 3 descriptors
+    //
+    TransferDescriptorCount = 3;
+
+    //
+    // get virtual base of mdl
+    //
+    Base = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
+
+    //
+    // Increase the size of last transfer, 0 in case this is the first
+    //
+    Base = (PVOID)((ULONG_PTR)Base + m_TransferBufferLengthCompleted);
+
+    PC_ASSERT(m_EndpointDescriptor);
+    PC_ASSERT(Base);
+
+    //
+    // Get the offset from page size
+    //
+    PageOffset = BYTE_OFFSET(Base);
+
+    //
+    // PageOffset should only be > 0 if this is the  first transfer for this requests
+    //
+    if ((PageOffset != 0) && (m_TransferBufferLengthCompleted != 0))
+    {
+        ASSERT(FALSE);
+    }
+
+    //
+    // Calculate the size of this transfer
+    //
+    if ((PageOffset != 0) && ((m_TransferBufferLength - m_TransferBufferLengthCompleted) >= (PAGE_SIZE * 4) + PageOffset))
+    {
+        CurrentTransferBufferLength = (PAGE_SIZE * 4) + PageOffset;
+    }
+    else if ((m_TransferBufferLength - m_TransferBufferLengthCompleted) >= PAGE_SIZE * 5)
+    {
+        CurrentTransferBufferLength = PAGE_SIZE * 5;
+    }
+    else
+        CurrentTransferBufferLength = (m_TransferBufferLength - m_TransferBufferLengthCompleted);
+
+    //
+    // Add current transfer length to transfer length completed
+    //
+    m_TransferBufferLengthCompleted += CurrentTransferBufferLength;
+    BytesAvailable = CurrentTransferBufferLength;
+    DPRINT("CurrentTransferBufferLength %x, m_TransferBufferLengthCompleted %x\n", CurrentTransferBufferLength, m_TransferBufferLengthCompleted);
+
+    DPRINT("EndPointAddress %x\n", m_EndpointDescriptor->bEndpointAddress);
+    DPRINT("EndPointDirection %x\n", USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
+
+    DPRINT("Request %p Base Address %p TransferBytesLength %lu MDL %p\n", this, Base, BytesAvailable, m_TransferBufferMDL);
+    DPRINT("InternalGetPidDirection() %d EndPointAddress %x\n", InternalGetPidDirection(), m_EndpointDescriptor->bEndpointAddress & 0x0F);
+    DPRINT("Irp %p QueueHead %p\n", m_Irp, QueueHead);
+
+    //PC_ASSERT(InternalGetPidDirection() == USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress));
+
+    //
+    // Allocated transfer descriptors
+    //
+    for (Index = 0; Index < TransferDescriptorCount; Index++)
+    {
+        Status = CreateDescriptor(&m_TransferDescriptors[Index]);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // Failed to allocate transfer descriptors
+            //
+
+            //
+            // Free QueueHead
+            //
+            FreeQueueHead(QueueHead);
+
+            //
+            // Free Descriptors
+            // FIXME: Implement FreeDescriptors
+            //
+            return Status;
+        }
+
+        //
+        // sanity check
+        //
+        PC_ASSERT(BytesAvailable);
+
+        //
+        // now setup transfer buffers
+        //
+        for(BufferIndex = 0; BufferIndex < 5; BufferIndex++)
+        {
+            //
+            // If this is the first buffer of the first descriptor and there is a PageOffset
+            //
+            if ((BufferIndex == 0) && (PageOffset != 0) && (Index == 0))
+            {
+                //
+                // use physical address
+                //
+                m_TransferDescriptors[Index]->BufferPointer[0] = MmGetPhysicalAddress(Base).LowPart;
+
+                //
+                // move to next page
+                //
+                Base = (PVOID)ROUND_TO_PAGES(Base);
+
+                //
+                // increment transfer bytes
+                //
+                if (CurrentTransferBufferLength > PAGE_SIZE - PageOffset)
+                    m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer = PAGE_SIZE - PageOffset;
+                else
+                    m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer = CurrentTransferBufferLength;
+
+                //
+                // decrement available byte count
+                //
+                BytesAvailable -= m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer;
+
+                DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
+                    BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
+            }
+            else
+            {
+                //
+                // the following pages always start on byte zero of each page
+                //
+                PC_ASSERT(((ULONG_PTR)Base & (PAGE_SIZE-1)) == 0);
+
+                if (BytesAvailable >= PAGE_SIZE)
+                {
+                    //
+                    // store address
+                    //
+                    m_TransferDescriptors[Index]->BufferPointer[BufferIndex] = MmGetPhysicalAddress(Base).LowPart;
+
+                    //
+                    // move to next page
+                    //
+                    Base = (PVOID)((ULONG_PTR)Base + PAGE_SIZE);
+
+                    //
+                    // increment transfer descriptor bytes
+                    //
+                    m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer += PAGE_SIZE;
+
+                    //
+                    // decrement available byte count
+                    //
+                    BytesAvailable -= PAGE_SIZE;
+
+                    DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
+                            BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
+                }
+                else
+                {
+                    PC_ASSERT(BytesAvailable);
+
+                    //
+                    // store address
+                    //
+                    m_TransferDescriptors[Index]->BufferPointer[BufferIndex] = MmGetPhysicalAddress(Base).LowPart;
+
+                    //
+                    // increment transfer descriptor bytes
+                    //
+                    m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer += BytesAvailable;
+
+                    //
+                    // decrement available byte count
+                    //
+                    BytesAvailable -= BytesAvailable;
+
+                    //
+                    // done as this is the last partial or full page
+                    //
+                    DPRINT("TransferDescriptor %p BufferPointer %p BufferIndex %lu TotalBytes %lu Remaining %lu\n", m_TransferDescriptors[Index], m_TransferDescriptors[Index]->BufferPointer[BufferIndex],
+                            BufferIndex, m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer, BytesAvailable);
+
+                    break;
+                }
+            }
+
+            //
+            // Check if all bytes have been consumed
+            //
+            if (BytesAvailable == 0)
+                break;
+        }
+
+        //
+        // store transfer bytes of descriptor
+        //
+        m_TransferDescriptors[Index]->TotalBytesToTransfer = m_TransferDescriptors[Index]->Token.Bits.TotalBytesToTransfer;
+
+        //
+        // Go ahead and link descriptors
+        //
+        if (Index > 0)
+        {
+            m_TransferDescriptors[Index - 1]->NextPointer = m_TransferDescriptors[Index]->PhysicalAddr;
+        }
+
+        //
+        // setup direction
+        //
+        m_TransferDescriptors[Index]->Token.Bits.PIDCode = InternalGetPidDirection();
+
+        //
+        // FIXME: performance penality?
+        //
+        m_TransferDescriptors[Index]->Token.Bits.InterruptOnComplete = TRUE;
+
+        //
+        // FIXME need dead queue transfer descriptor?
+        //
+
+        //
+        // Check if all bytes have been consumed
+        //
+        if (BytesAvailable == 0)
+            break;
+    }
+
+    //
+    // all bytes should have been consumed
+    //
+    PC_ASSERT(BytesAvailable == 0);
+
+    //
+    // Initialize the QueueHead
+    //
+    QueueHead->EndPointCharacteristics.DeviceAddress = GetDeviceAddress();
+
+    if (m_EndpointDescriptor)
+    {
+        //
+        // Set endpoint address and max packet length
+        //
+        QueueHead->EndPointCharacteristics.EndPointNumber = m_EndpointDescriptor->bEndpointAddress & 0x0F;
+        QueueHead->EndPointCharacteristics.MaximumPacketLength = m_EndpointDescriptor->wMaxPacketSize;
+    }
+
+    QueueHead->Token.Bits.DataToggle = TRUE;
+
+    //
+    // link descriptor with queue head
+    //
+    QueueHead->NextPointer = m_TransferDescriptors[0]->PhysicalAddr;
+
+    //
+    // store result
+    //
+    *OutHead = QueueHead;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
 }
 
 //----------------------------------------------------------------------------------------
@@ -731,6 +1130,7 @@ CUSBRequest::CreateQueueHead(
     // allocate queue head
     //
     Status = m_DmaManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysicalAddress);
+
     if (!NT_SUCCESS(Status))
     {
         //
@@ -776,6 +1176,11 @@ CUSBRequest::CreateQueueHead(
     //
     QueueHead->PhysicalAddr = QueueHeadPhysicalAddress.LowPart;
 
+    //
+    // output queue head
+    //
+    *OutQueueHead = QueueHead;
+
     //
     // done
     //
@@ -783,7 +1188,7 @@ CUSBRequest::CreateQueueHead(
 }
 
 //----------------------------------------------------------------------------------------
-ULONG
+UCHAR
 CUSBRequest::GetDeviceAddress()
 {
     PIO_STACK_LOCATION IoStack;
@@ -796,9 +1201,9 @@ CUSBRequest::GetDeviceAddress()
     if (!m_Irp)
     {
         //
-        // no irp is provided
-        // assume it is for device address 0
-        return 0;
+        // used provided address
+        //
+        return m_DeviceAddress;
     }
 
     //
@@ -838,12 +1243,11 @@ NTSTATUS
 CUSBRequest::BuildSetupPacket()
 {
     NTSTATUS Status;
-    PHYSICAL_ADDRESS PhysicalAddress;
 
     //
     // allocate common buffer setup packet
     //
-    Status = m_DmaManager->Allocate(sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), (PVOID*)&m_DescriptorPacket, &PhysicalAddress);
+    Status = m_DmaManager->Allocate(sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), (PVOID*)&m_DescriptorPacket, &m_DescriptorSetupPacket);
     if (!NT_SUCCESS(Status))
     {
         //
@@ -858,7 +1262,6 @@ CUSBRequest::BuildSetupPacket()
         // copy setup packet
         //
         RtlCopyMemory(m_DescriptorPacket, m_SetupPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
-        m_DescriptorSetupPacket = PhysicalAddress;
     }
     else
     {
@@ -1102,10 +1505,12 @@ VOID
 CUSBRequest::FreeQueueHead(
     IN struct _QUEUE_HEAD * QueueHead)
 {
+    LONG DescriptorCount;
+
     //
     // FIXME: support chained queue heads
     //
-    PC_ASSERT(QueueHead == m_QueueHead);
+    //PC_ASSERT(QueueHead == m_QueueHead);
 
     //
     // release queue head
@@ -1120,35 +1525,141 @@ CUSBRequest::FreeQueueHead(
     //
     // release transfer descriptors
     //
+    for (DescriptorCount = 0; DescriptorCount < 3; DescriptorCount++)
+    {
+        if (m_TransferDescriptors[DescriptorCount])
+        {
+            //
+            // Calculate Total Bytes Transferred
+            // FIXME: Is this the correct method of determine bytes transferred?
+            //
+            if (USB_ENDPOINT_TYPE_BULK == GetTransferType())
+            {
+                //
+                // sanity check
+                //
+                ASSERT(m_EndpointDescriptor);
+
+                if (USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress))
+                {
+                    DPRINT("m_TotalBytesTransferred %x, %x - %x\n",
+                        m_TotalBytesTransferred,
+                        m_TransferDescriptors[DescriptorCount]->TotalBytesToTransfer,
+                        m_TransferDescriptors[DescriptorCount]->Token.Bits.TotalBytesToTransfer);
+
+                    m_TotalBytesTransferred +=
+                        m_TransferDescriptors[DescriptorCount]->TotalBytesToTransfer -
+                        m_TransferDescriptors[DescriptorCount]->Token.Bits.TotalBytesToTransfer;
+                }
+            }
+
+            //
+            // release transfer descriptors
+            //
+            m_DmaManager->Release(m_TransferDescriptors[DescriptorCount], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
+            m_TransferDescriptors[DescriptorCount] = 0;
+        }
+    }
+
+    if (m_DescriptorPacket)
+    {
+        //
+        // release packet descriptor
+        //
+        m_DmaManager->Release(m_DescriptorPacket, sizeof(USB_DEFAULT_PIPE_SETUP_PACKET));
+        m_DescriptorPacket = 0;
+    }
+}
+
+//-----------------------------------------------------------------------------------------
+BOOLEAN
+CUSBRequest::IsQueueHeadComplete(
+    struct _QUEUE_HEAD * QueueHead)
+{
+    ULONG Index;
+
+    //
+    // first check - is the queue head currently active
+    //
+    if (QueueHead->Token.Bits.Active)
+    {
+        //
+        // queue head is active (currently processed)
+        //
+        return FALSE;
+    }
 
-    if (m_TransferDescriptors[0])
+    //
+    // FIXME: support chained queue heads
+    //
+    for(Index = 0; Index < 3; Index++)
     {
         //
-        // release transfer descriptors
+        // check transfer descriptors for completion
         //
-        m_DmaManager->Release(m_TransferDescriptors[0], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
-        m_TransferDescriptors[0] = 0;
+        if (m_TransferDescriptors[Index])
+        {
+            //
+            // check for serious error
+            //
+            //PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Halted == 0);
+
+            //
+            // the transfer descriptor should be in the same state as the queue head
+            //
+            //PC_ASSERT(m_TransferDescriptors[Index]->Token.Bits.Active == 0);
+        }
     }
 
-    if (m_TransferDescriptors[1])
+    return TRUE;
+}
+
+//-----------------------------------------------------------------------------------------
+VOID
+CUSBRequest::GetTransferBuffer(
+    OUT PMDL * OutMDL,
+    OUT PULONG TransferLength)
+{
+    // sanity checks
+    PC_ASSERT(OutMDL);
+    PC_ASSERT(TransferLength);
+
+    *OutMDL = m_TransferBufferMDL;
+    *TransferLength = m_TransferBufferLength;
+}
+//-----------------------------------------------------------------------------------------
+ULONG
+CUSBRequest::InternalCalculateTransferLength()
+{
+    if (!m_Irp)
     {
         //
-        // release transfer descriptors
+        // FIXME: get length for control request
         //
-        m_DmaManager->Release(m_TransferDescriptors[1], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
-        m_TransferDescriptors[1] = 0;
+        return m_TransferBufferLength;
     }
 
-    if (m_TransferDescriptors[2])
+    //
+    // sanity check
+    //
+    ASSERT(m_EndpointDescriptor);
+
+    if (USB_ENDPOINT_DIRECTION_IN(m_EndpointDescriptor->bEndpointAddress))
     {
         //
-        // release transfer descriptors
+        // bulk in request
+        // HACK: Properly determine transfer length
         //
-        m_DmaManager->Release(m_TransferDescriptors[2], sizeof(QUEUE_TRANSFER_DESCRIPTOR));
-        m_TransferDescriptors[2] = 0;
+        return m_TransferBufferLength;//m_TotalBytesTransferred;
     }
+
+    //
+    // bulk out transfer
+    //
+    return m_TransferBufferLength;
 }
 
+//-----------------------------------------------------------------------------------------
 NTSTATUS
 InternalCreateUSBRequest(
     PUSBREQUEST *OutRequest)