[USBOHCI]
[reactos.git] / drivers / usb / usbohci / usb_request.cpp
index 38fdfd5..48fc41d 100644 (file)
@@ -36,8 +36,8 @@ public:
     }
 
     // IUSBRequest interface functions
-    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 NTSTATUS InitializeWithSetupPacket(IN PDMAMEMORYMANAGER DmaManager, IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket, IN UCHAR DeviceAddress, IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, IN USB_DEVICE_SPEED DeviceSpeed, IN OUT ULONG TransferBufferLength, IN OUT PMDL TransferBuffer);
+    virtual NTSTATUS InitializeWithIrp(IN PDMAMEMORYMANAGER DmaManager, IN OUT PIRP Irp, IN USB_DEVICE_SPEED DeviceSpeed);
     virtual BOOLEAN IsRequestComplete();
     virtual ULONG GetTransferType();
     virtual NTSTATUS GetEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutEndpointDescriptor);
@@ -45,6 +45,9 @@ public:
     virtual BOOLEAN IsRequestInitialized();
     virtual BOOLEAN IsQueueHeadComplete(struct _QUEUE_HEAD * QueueHead);
     virtual VOID CompletionCallback(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
+    virtual VOID FreeEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
+    virtual UCHAR GetInterval();
+
 
     // local functions
     ULONG InternalGetTransferType();
@@ -53,11 +56,16 @@ public:
     NTSTATUS BuildSetupPacket();
     NTSTATUS BuildSetupPacketFromURB();
     NTSTATUS BuildControlTransferDescriptor(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
+    NTSTATUS BuildBulkInterruptEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
+    NTSTATUS BuildIsochronousEndpoint(POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor);
     NTSTATUS CreateGeneralTransferDescriptor(POHCI_GENERAL_TD* OutDescriptor, ULONG BufferSize);
     VOID FreeDescriptor(POHCI_GENERAL_TD Descriptor);
     NTSTATUS AllocateEndpointDescriptor(OUT POHCI_ENDPOINT_DESCRIPTOR *OutDescriptor);
+    NTSTATUS CreateIsochronousTransferDescriptor(OUT POHCI_ISO_TD *OutDescriptor, ULONG FrameCount);
     UCHAR GetEndpointAddress();
     USHORT GetMaxPacketSize();
+    VOID CheckError(struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor);
+
 
     // constructor / destructor
     CUSBRequest(IUnknown *OuterUnknown){}
@@ -128,6 +136,15 @@ protected:
     NTSTATUS m_NtStatusCode;
     ULONG m_UrbStatusCode;
 
+    //
+    // device speed
+    //
+    USB_DEVICE_SPEED m_DeviceSpeed;
+
+    //
+    // store urb
+    //
+    PURB m_Urb;
 };
 
 //----------------------------------------------------------------------------------------
@@ -147,6 +164,7 @@ CUSBRequest::InitializeWithSetupPacket(
     IN PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket,
     IN UCHAR DeviceAddress,
     IN OPTIONAL PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor,
+    IN USB_DEVICE_SPEED DeviceSpeed,
     IN OUT ULONG TransferBufferLength,
     IN OUT PMDL TransferBuffer)
 {
@@ -166,6 +184,7 @@ CUSBRequest::InitializeWithSetupPacket(
     m_DeviceAddress = DeviceAddress;
     m_EndpointDescriptor = EndpointDescriptor;
     m_TotalBytesTransferred = 0;
+    m_DeviceSpeed = DeviceSpeed;
 
     //
     // Set Length Completed to 0
@@ -198,10 +217,10 @@ CUSBRequest::InitializeWithSetupPacket(
 NTSTATUS
 CUSBRequest::InitializeWithIrp(
     IN PDMAMEMORYMANAGER DmaManager,
-    IN OUT PIRP Irp)
+    IN OUT PIRP Irp,
+    IN USB_DEVICE_SPEED DeviceSpeed)
 {
     PIO_STACK_LOCATION IoStack;
-    PURB Urb;
 
     //
     // sanity checks
@@ -227,18 +246,98 @@ CUSBRequest::InitializeWithIrp(
     //
     // get urb
     //
-    Urb = (PURB)IoStack->Parameters.Others.Argument1;
+    m_Urb = (PURB)IoStack->Parameters.Others.Argument1;
 
     //
     // store irp
     //
     m_Irp = Irp;
 
+    //
+    // store speed
+    //
+    m_DeviceSpeed = DeviceSpeed;
+
     //
     // check function type
     //
-    switch (Urb->UrbHeader.Function)
+    switch (m_Urb->UrbHeader.Function)
     {
+        case URB_FUNCTION_ISOCH_TRANSFER:
+        {
+            //
+            // there must be at least one packet
+            //
+            ASSERT(m_Urb->UrbIsochronousTransfer.NumberOfPackets);
+
+            //
+            // is there data to be transferred
+            //
+            if (m_Urb->UrbIsochronousTransfer.TransferBufferLength)
+            {
+                //
+                // Check if there is a MDL
+                //
+                if (!m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
+                {
+                    //
+                    // sanity check
+                    //
+                    PC_ASSERT(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer);
+
+                    //
+                    // Create one using TransferBuffer
+                    //
+                    DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
+                    m_TransferBufferMDL = IoAllocateMdl(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer,
+                                                        m_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);
+                }
+                else
+                {
+                    //
+                    // use provided mdl
+                    //
+                    m_TransferBufferMDL = m_Urb->UrbIsochronousTransfer.TransferBufferMDL;
+                }
+            }
+            //
+            // save buffer length
+            //
+            m_TransferBufferLength = m_Urb->UrbIsochronousTransfer.TransferBufferLength;
+
+            //
+            // Set Length Completed to 0
+            //
+            m_TransferBufferLengthCompleted = 0;
+
+            //
+            // get endpoint descriptor
+            //
+            m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)m_Urb->UrbIsochronousTransfer.PipeHandle;
+
+            //
+            // completed initialization
+            //
+            break;
+        }
         //
         // luckily those request have the same structure layout
         //
@@ -249,24 +348,24 @@ CUSBRequest::InitializeWithIrp(
             //
             // bulk interrupt transfer
             //
-            if (Urb->UrbBulkOrInterruptTransfer.TransferBufferLength)
+            if (m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength)
             {
                 //
                 // Check if there is a MDL
                 //
-                if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
+                if (!m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
                 {
                     //
                     // sanity check
                     //
-                    PC_ASSERT(Urb->UrbBulkOrInterruptTransfer.TransferBuffer);
+                    PC_ASSERT(m_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,
+                    DPRINT("Creating Mdl from Urb Buffer %p Length %lu\n", m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer, m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength);
+                    m_TransferBufferMDL = IoAllocateMdl(m_Urb->UrbBulkOrInterruptTransfer.TransferBuffer,
+                                                        m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength,
                                                         FALSE,
                                                         FALSE,
                                                         NULL);
@@ -291,13 +390,13 @@ CUSBRequest::InitializeWithIrp(
                 }
                 else
                 {
-                    m_TransferBufferMDL = Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
+                    m_TransferBufferMDL = m_Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL;
                 }
 
                 //
                 // save buffer length
                 //
-                m_TransferBufferLength = Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
+                m_TransferBufferLength = m_Urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
 
                 //
                 // Set Length Completed to 0
@@ -307,13 +406,13 @@ CUSBRequest::InitializeWithIrp(
                 //
                 // get endpoint descriptor
                 //
-                m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbBulkOrInterruptTransfer.PipeHandle;
+                m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)m_Urb->UrbBulkOrInterruptTransfer.PipeHandle;
 
             }
             break;
         }
         default:
-            DPRINT1("URB Function: not supported %x\n", Urb->UrbHeader.Function);
+            DPRINT1("URB Function: not supported %x\n", m_Urb->UrbHeader.Function);
             PC_ASSERT(FALSE);
     }
 
@@ -375,6 +474,18 @@ CUSBRequest::GetMaxPacketSize()
     return m_EndpointDescriptor->wMaxPacketSize;
 }
 
+UCHAR
+CUSBRequest::GetInterval()
+{
+    ASSERT(m_EndpointDescriptor);
+    ASSERT((m_EndpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT);
+
+    //
+    // return interrupt interval
+    //
+    return m_EndpointDescriptor->bInterval;
+}
+
 UCHAR
 CUSBRequest::GetEndpointAddress()
 {
@@ -509,6 +620,244 @@ CUSBRequest::FreeDescriptor(
     m_DmaManager->Release(Descriptor, sizeof(OHCI_GENERAL_TD));
 
 }
+
+//----------------------------------------------------------------------------------------
+NTSTATUS
+CUSBRequest::CreateIsochronousTransferDescriptor(
+    POHCI_ISO_TD* OutDescriptor, 
+    ULONG FrameCount)
+{
+    POHCI_ISO_TD Descriptor;
+    PHYSICAL_ADDRESS DescriptorAddress;
+    NTSTATUS Status;
+
+    //
+    // allocate transfer descriptor
+    //
+    Status = m_DmaManager->Allocate(sizeof(OHCI_ISO_TD), (PVOID*)&Descriptor, &DescriptorAddress);
+    if (!NT_SUCCESS(Status))
+    {
+         //
+         // no memory
+         //
+         return Status;
+    }
+
+    //
+    // initialize descriptor, hardware part
+    //
+    Descriptor->Flags = OHCI_ITD_SET_FRAME_COUNT(FrameCount) | OHCI_ITD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);// |  OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED);
+    Descriptor->BufferPhysical = 0;
+    Descriptor->NextPhysicalDescriptor = 0;
+    Descriptor->LastPhysicalByteAddress = 0;
+
+    //
+    // software part
+    //
+    Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
+    Descriptor->NextLogicalDescriptor = 0;
+
+    //
+    // store result
+    //
+    *OutDescriptor = Descriptor;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CUSBRequest::BuildIsochronousEndpoint(
+    POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
+{
+    POHCI_ISO_TD FirstDescriptor = NULL, PreviousDescriptor = NULL, CurrentDescriptor = NULL;
+    POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
+    ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset, Page;
+    NTSTATUS Status;
+    PVOID Buffer;
+    PIO_STACK_LOCATION IoStack;
+    PURB Urb;
+    PHYSICAL_ADDRESS Address;
+
+    //
+    // get current irp stack location
+    //
+    IoStack = IoGetCurrentIrpStackLocation(m_Irp);
+
+    //
+    // sanity check
+    //
+    PC_ASSERT(IoStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL);
+    PC_ASSERT(IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB);
+    PC_ASSERT(IoStack->Parameters.Others.Argument1 != 0);
+
+    //
+    // get urb
+    //
+    Urb = (PURB)IoStack->Parameters.Others.Argument1;
+    ASSERT(Urb);
+
+    //
+    // allocate endpoint descriptor
+    //
+    Status = AllocateEndpointDescriptor(&EndpointDescriptor);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to create setup descriptor
+        //
+        ASSERT(FALSE);
+        return Status;
+    }
+
+    //
+    // get buffer
+    //
+    Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
+    ASSERT(Buffer);
+
+    //
+    // FIXME: support requests which spans serveral pages
+    //
+    ASSERT(ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(m_TransferBufferMDL), MmGetMdlByteCount(m_TransferBufferMDL)) <= 2);
+
+    Status = m_DmaManager->Allocate(m_TransferBufferLength, &Buffer, &Address);
+    ASSERT(Status == STATUS_SUCCESS);
+
+
+    while(Index < Urb->UrbIsochronousTransfer.NumberOfPackets)
+    {
+        //
+        // get number of packets remaining
+        //
+        NumberOfPackets = min(Urb->UrbIsochronousTransfer.NumberOfPackets - Index, OHCI_ITD_NOFFSET);
+        //
+        // allocate iso descriptor
+        //
+        Status = CreateIsochronousTransferDescriptor(&CurrentDescriptor, NumberOfPackets);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // FIXME: cleanup
+            // failed to allocate descriptor
+            //
+            ASSERT(FALSE);
+            return Status;
+        }
+
+        //
+        // get physical page
+        //
+        Page = MmGetPhysicalAddress(Buffer).LowPart;
+
+        //
+        // get page offset
+        //
+        PageOffset = BYTE_OFFSET(Page);
+
+        //
+        // initialize descriptor
+        //
+        CurrentDescriptor->BufferPhysical = Page - PageOffset;
+
+        for(SubIndex = 0; SubIndex < NumberOfPackets; SubIndex++)
+        {
+            //
+            // store buffer offset
+            //
+            CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset + PageOffset;
+            DPRINT1("Index %lu PacketOffset %lu FinalOffset %lu\n", SubIndex+Index, Urb->UrbIsochronousTransfer.IsoPacket[Index+SubIndex].Offset, CurrentDescriptor->Offset[SubIndex]);
+        }
+
+        //
+        // increment packet offset
+        //
+        Index += NumberOfPackets;
+
+        //
+        // check if this is the last descriptor
+        //
+        if (Index == Urb->UrbIsochronousTransfer.NumberOfPackets)
+        {
+            //
+            // end of transfer
+            //
+            CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + m_TransferBufferLength - 1;
+        }
+        else
+        {
+            //
+            // use start address of next packet - 1
+            //
+            CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset - 1;
+        }
+
+        //
+        // is there a previous descriptor
+        //
+        if (PreviousDescriptor)
+        {
+            //
+            // link descriptors
+            //
+            PreviousDescriptor->NextLogicalDescriptor = CurrentDescriptor;
+            PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
+        }
+        else
+        {
+            //
+            // first descriptor
+            //
+            FirstDescriptor = CurrentDescriptor;
+        }
+
+        //
+        // store as previous descriptor
+        //
+        PreviousDescriptor = CurrentDescriptor;
+        DPRINT1("Current Descriptor %p Logical %lx StartAddress %x EndAddress %x\n", CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress);
+
+    //
+    // fire interrupt as soon transfer is finished
+    //
+    CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
+    }
+
+    //
+    // clear interrupt mask for last transfer descriptor
+    //
+    CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK;
+
+    //
+    // fire interrupt as soon transfer is finished
+    //
+    CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
+
+    //
+    // set isochronous type
+    //
+    EndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
+
+    //
+    // now link descriptor to endpoint
+    //
+    EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart;
+    EndpointDescriptor->TailPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
+    EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor;
+
+    //
+    // store result
+    //
+    *OutEndpointDescriptor = EndpointDescriptor;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
 //----------------------------------------------------------------------------------------
 NTSTATUS
 CUSBRequest::CreateGeneralTransferDescriptor(
@@ -606,31 +955,282 @@ CUSBRequest::AllocateEndpointDescriptor(
     //
     // append device address and endpoint number
     //
-    Descriptor->Flags |= OHCI_ENDPOINT_SET_DEVICE_ADDRESS(m_DeviceAddress);
+    Descriptor->Flags |= OHCI_ENDPOINT_SET_DEVICE_ADDRESS(GetDeviceAddress());
     Descriptor->Flags |= OHCI_ENDPOINT_SET_ENDPOINT_NUMBER(GetEndpointAddress());
     Descriptor->Flags |= OHCI_ENDPOINT_SET_MAX_PACKET_SIZE(GetMaxPacketSize());
 
-    //
-    // FIXME: detect type
-    //
-    Descriptor->Flags |= OHCI_ENDPOINT_FULL_SPEED;
-
-    Descriptor->HeadPhysicalDescriptor = 0;
-    Descriptor->NextPhysicalEndpoint = 0;
-    Descriptor->TailPhysicalDescriptor = 0;
-    Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
+    DPRINT("Flags %x DeviceAddress %x EndpointAddress %x PacketSize %x\n", Descriptor->Flags, GetDeviceAddress(), GetEndpointAddress(), GetMaxPacketSize());
 
     //
-    // store result
-    //
-    *OutDescriptor = Descriptor;
-
+    // is there an endpoint descriptor
     //
-    // done
+    if (m_EndpointDescriptor)
+    {
+        //
+        // check direction
+        //
+        if (USB_ENDPOINT_DIRECTION_OUT(m_EndpointDescriptor->bEndpointAddress))
+        {
+            //
+            // direction out
+            //
+            Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_OUT;
+        }
+        else
+        {
+            //
+            // direction in
+            //
+            Descriptor->Flags |= OHCI_ENDPOINT_DIRECTION_IN;
+        }
+
+    }
+
+    //
+    // set type
+    //
+    if (m_DeviceSpeed == UsbFullSpeed)
+    {
+        //
+        // device is full speed
+        //
+        Descriptor->Flags |= OHCI_ENDPOINT_FULL_SPEED;
+    }
+    else if (m_DeviceSpeed == UsbLowSpeed)
+    {
+        //
+        // device is full speed
+        //
+        Descriptor->Flags |= OHCI_ENDPOINT_LOW_SPEED;
+    }
+    else
+    {
+        //
+        // error
+        //
+        ASSERT(FALSE);
+    }
+
+    Descriptor->HeadPhysicalDescriptor = 0;
+    Descriptor->NextPhysicalEndpoint = 0;
+    Descriptor->TailPhysicalDescriptor = 0;
+    Descriptor->PhysicalAddress.QuadPart = DescriptorAddress.QuadPart;
+
+    //
+    // store result
+    //
+    *OutDescriptor = Descriptor;
+
+    //
+    // done
     //
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+CUSBRequest::BuildBulkInterruptEndpoint(
+    POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
+{
+    POHCI_GENERAL_TD FirstDescriptor, PreviousDescriptor = NULL, CurrentDescriptor, LastDescriptor;
+    POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
+    ULONG BufferSize, CurrentSize, Direction, MaxLengthInPage;
+    NTSTATUS Status;
+    PVOID Buffer;
+
+    //
+    // allocate endpoint descriptor
+    //
+    Status = AllocateEndpointDescriptor(&EndpointDescriptor);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to create setup descriptor
+        //
+        return Status;
+    }
+
+    //
+    // allocate transfer descriptor for last descriptor
+    //
+    Status = CreateGeneralTransferDescriptor(&LastDescriptor, 0);
+    if (!NT_SUCCESS(Status))
+    {
+        //
+        // failed to create transfer descriptor
+        //
+        m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
+        return Status;
+    }
+
+    //
+    // get buffer size
+    //
+    BufferSize = m_TransferBufferLength;
+    ASSERT(BufferSize);
+    ASSERT(m_TransferBufferMDL);
+
+    //
+    // get buffer
+    //
+    Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
+    ASSERT(Buffer);
+
+    if (InternalGetPidDirection())
+    {
+        //
+        // input direction
+        //
+        Direction = OHCI_TD_DIRECTION_PID_IN;
+    }
+    else
+    {
+        //
+        // output direction
+        //
+        Direction = OHCI_TD_DIRECTION_PID_OUT;
+    }
+
+    do
+    {
+        //
+        // get current buffersize
+        //
+        CurrentSize = min(8192, BufferSize);
+
+        //
+        // get page offset
+        //
+        MaxLengthInPage = PAGE_SIZE - BYTE_OFFSET(Buffer);
+
+        //
+        // get minimum from current page size
+        //
+        CurrentSize = min(CurrentSize, MaxLengthInPage);
+        ASSERT(CurrentSize);
+
+        //
+        // allocate transfer descriptor
+        //
+        Status = CreateGeneralTransferDescriptor(&CurrentDescriptor, 0);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // failed to create transfer descriptor
+            // TODO: cleanup
+            //
+            ASSERT(FALSE);
+            m_DmaManager->Release(EndpointDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
+            FreeDescriptor(LastDescriptor);
+            return Status;
+        }
+
+        //
+        // initialize descriptor
+        //
+        CurrentDescriptor->Flags = Direction | OHCI_TD_BUFFER_ROUNDING | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY;
+
+        //
+        // store physical address of buffer
+        //
+        CurrentDescriptor->BufferPhysical = MmGetPhysicalAddress(Buffer).LowPart;
+        CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + CurrentSize - 1; 
+
+#if 0
+        if (m_Urb != NULL)
+        {
+            if (m_Urb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK)
+            {
+                //
+                // indicate short packet support
+                //
+                CurrentDescriptor->Flags |= OHCI_TD_BUFFER_ROUNDING;
+            }
+        }
+#endif
+
+        //
+        // is there a previous descriptor
+        //
+        if (PreviousDescriptor)
+        {
+            //
+            // link descriptors
+            //
+            PreviousDescriptor->NextLogicalDescriptor = (PVOID)CurrentDescriptor;
+            PreviousDescriptor->NextPhysicalDescriptor = CurrentDescriptor->PhysicalAddress.LowPart;
+        }
+        else
+        {
+            //
+            // it is the first descriptor
+            //
+            FirstDescriptor = CurrentDescriptor;
+        }
+
+        DPRINT("PreviousDescriptor %p CurrentDescriptor %p Logical %x  Buffer Logical %p Physical %x Last Physical %x CurrentSize %lu\n", PreviousDescriptor, CurrentDescriptor, CurrentDescriptor->PhysicalAddress.LowPart, CurrentDescriptor->BufferLogical, CurrentDescriptor->BufferPhysical, CurrentDescriptor->LastPhysicalByteAddress, CurrentSize);
+
+        //
+        //  set previous descriptor
+        //
+        PreviousDescriptor = CurrentDescriptor;
+
+        //
+        // subtract buffer size
+        //
+        BufferSize -= CurrentSize;
+
+        //
+        // increment buffer offset
+        //
+        Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize);
+
+    }while(BufferSize);
+
+    //
+    // first descriptor has no carry bit
+    //
+    FirstDescriptor->Flags &= ~OHCI_TD_TOGGLE_CARRY;
+
+    //
+    // fixme: toggle
+    //
+    FirstDescriptor->Flags |= OHCI_TD_TOGGLE_0;
+
+    //
+    // clear interrupt mask for last transfer descriptor
+    //
+    CurrentDescriptor->Flags &= ~OHCI_TD_INTERRUPT_MASK;
+
+    //
+    // fire interrupt as soon transfer is finished
+    //
+    CurrentDescriptor->Flags |= OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_IMMEDIATE);
+
+    //
+    // link last data descriptor to last descriptor
+    //
+    CurrentDescriptor->NextLogicalDescriptor = LastDescriptor;
+    CurrentDescriptor->NextPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
+
+    //
+    // now link descriptor to endpoint
+    //
+    EndpointDescriptor->HeadPhysicalDescriptor = FirstDescriptor->PhysicalAddress.LowPart;
+    EndpointDescriptor->TailPhysicalDescriptor = LastDescriptor->PhysicalAddress.LowPart;
+    EndpointDescriptor->HeadLogicalDescriptor = FirstDescriptor;
+
+    //
+    // store result
+    //
+    *OutEndpointDescriptor = EndpointDescriptor;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
+
 NTSTATUS
 CUSBRequest::BuildControlTransferDescriptor(
     POHCI_ENDPOINT_DESCRIPTOR * OutEndpointDescriptor)
@@ -639,8 +1239,6 @@ CUSBRequest::BuildControlTransferDescriptor(
     POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
     NTSTATUS Status;
 
-    DPRINT1("CUSBRequest::BuildControlTransferDescriptor\n");
-
     //
     // allocate endpoint descriptor
     //
@@ -695,7 +1293,6 @@ CUSBRequest::BuildControlTransferDescriptor(
         return Status;
     }
 
-
     if (m_TransferBufferLength)
     {
         //
@@ -722,7 +1319,37 @@ CUSBRequest::BuildControlTransferDescriptor(
         //
         // initialize data descriptor
         //
-        DataDescriptor->Flags = OHCI_TD_BUFFER_ROUNDING| OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY | OHCI_TD_DIRECTION_PID_IN;
+        DataDescriptor->Flags = OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE) | OHCI_TD_TOGGLE_CARRY | OHCI_TD_TOGGLE_1;
+
+        if (m_EndpointDescriptor)
+        {
+            if (USB_ENDPOINT_DIRECTION_OUT(m_EndpointDescriptor->bEndpointAddress))
+            {
+                //
+                // direction out
+                //
+                DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_OUT;
+            }
+            else
+            {
+                //
+                // direction in
+                //
+                DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN;
+            }
+        }
+        else
+        {
+            //
+            // no end point address provided - assume its an in direction
+            //
+            DataDescriptor->Flags |= OHCI_TD_DIRECTION_PID_IN;
+        }
+
+        //
+        // use short packets
+        //
+        DataDescriptor->Flags |= OHCI_TD_BUFFER_ROUNDING;
 
         //
         // store physical address of buffer
@@ -734,7 +1361,7 @@ CUSBRequest::BuildControlTransferDescriptor(
     //
     // initialize setup descriptor
     //
-    SetupDescriptor->Flags = OHCI_TD_DIRECTION_PID_SETUP | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);
+    SetupDescriptor->Flags = OHCI_TD_BUFFER_ROUNDING | OHCI_TD_DIRECTION_PID_SETUP | OHCI_TD_SET_CONDITION_CODE(OHCI_TD_CONDITION_NOT_ACCESSED) | OHCI_TD_TOGGLE_0 | OHCI_TD_SET_DELAY_INTERRUPT(OHCI_TD_INTERRUPT_NONE);
 
     if (m_SetupPacket)
     {
@@ -849,16 +1476,11 @@ CUSBRequest::GetEndpointDescriptor(
             Status = BuildControlTransferDescriptor((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor);
             break;
         case USB_ENDPOINT_TYPE_BULK:
-            DPRINT1("USB_ENDPOINT_TYPE_BULK not implemented\n");
-            Status = STATUS_NOT_IMPLEMENTED; //BuildBulkTransferQueueHead(OutDescriptor);
-            break;
         case USB_ENDPOINT_TYPE_INTERRUPT:
-            DPRINT1("USB_ENDPOINT_TYPE_INTERRUPT not implemented\n");
-            Status = STATUS_NOT_IMPLEMENTED;
+            Status = BuildBulkInterruptEndpoint(OutDescriptor);
             break;
         case USB_ENDPOINT_TYPE_ISOCHRONOUS:
-            DPRINT1("USB_ENDPOINT_TYPE_ISOCHRONOUS not implemented\n");
-            Status = STATUS_NOT_IMPLEMENTED;
+            Status = BuildIsochronousEndpoint((POHCI_ENDPOINT_DESCRIPTOR*)OutDescriptor);
             break;
         default:
             PC_ASSERT(FALSE);
@@ -919,14 +1541,113 @@ CUSBRequest::GetResultStatus(
 
 }
 
-
 VOID
-CUSBRequest::CompletionCallback(
+CUSBRequest::FreeEndpointDescriptor(
     struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
 {
     POHCI_GENERAL_TD TransferDescriptor, NextTransferDescriptor;
+    POHCI_ISO_TD IsoTransferDescriptor, IsoNextTransferDescriptor;
+    ULONG Index, PacketCount;
+
+    DPRINT("CUSBRequest::FreeEndpointDescriptor EndpointDescriptor %p Logical %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart);
+
+    if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT)
+    {
+        //
+        // get first iso transfer descriptor
+        //
+        IsoTransferDescriptor = (POHCI_ISO_TD)OutDescriptor->HeadLogicalDescriptor;
+
+        //
+        // release endpoint descriptor
+        //
+        m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
+
+        while(IsoTransferDescriptor)
+        {
+            //
+            // get next
+            //
+            IsoNextTransferDescriptor = IsoTransferDescriptor->NextLogicalDescriptor;
+
+            //
+            // get packet count
+            //
+            PacketCount = OHCI_ITD_GET_FRAME_COUNT(IsoTransferDescriptor->Flags);
+
+            DPRINT1("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x PacketCount %lu\n", IsoTransferDescriptor, IsoTransferDescriptor->PhysicalAddress.LowPart, IsoTransferDescriptor->BufferPhysical, IsoTransferDescriptor->LastPhysicalByteAddress, PacketCount);
+
+            for(Index = 0; Index < PacketCount; Index++)
+            {
+                DPRINT1("PSW Index %lu Value %x\n", Index, IsoTransferDescriptor->Offset[Index]);
+            }
+
+            //
+            // release descriptor
+            //
+            m_DmaManager->Release(IsoTransferDescriptor, sizeof(OHCI_ISO_TD));
+
+            //
+            // move to next
+            //
+            IsoTransferDescriptor = IsoNextTransferDescriptor;
+        }
+    }
+    else
+    {
+        //
+        // get first general transfer descriptor
+        //
+        TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor;
+
+        //
+        // release endpoint descriptor
+        //
+        m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
+
+        while(TransferDescriptor)
+        {
+            //
+            // get next
+            //
+            NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor;
+
+            //
+            // is there a buffer associated
+            //
+            if (TransferDescriptor->BufferSize)
+            {
+                //
+                // release buffer
+                //
+                m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize);
+            }
+
+            DPRINT("CUSBRequest::FreeEndpointDescriptor Descriptor %p Logical %x Buffer Physical %x EndAddress %x\n", TransferDescriptor, TransferDescriptor->PhysicalAddress.LowPart, TransferDescriptor->BufferPhysical, TransferDescriptor->LastPhysicalByteAddress);
+
+            //
+            // release descriptor
+            //
+            m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD));
+
+            //
+            // move to next
+            //
+            TransferDescriptor = NextTransferDescriptor;
+        }
+    }
+
+}
+
+VOID
+CUSBRequest::CheckError(
+    struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
+{
+    POHCI_GENERAL_TD TransferDescriptor;
+    ULONG ConditionCode;
+    PURB Urb;
+    PIO_STACK_LOCATION IoStack;
 
-    DPRINT1("CUSBRequest::CompletionCallback\n");
 
     //
     // set status code
@@ -934,56 +1655,183 @@ CUSBRequest::CompletionCallback(
     m_NtStatusCode = STATUS_SUCCESS;
     m_UrbStatusCode = USBD_STATUS_SUCCESS;
 
-    ASSERT(!m_Irp);
 
-    //
-    // FIXME: cleanup descriptors
-    //
+    if (OutDescriptor->Flags & OHCI_ENDPOINT_ISOCHRONOUS_FORMAT)
+    {
+        //
+        // FIXME: handle isochronous support
+        //
+        ASSERT(FALSE);
+    }
+    else
+    {
+        //
+        // get first general transfer descriptor
+        //
+        TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor;
+
+        while(TransferDescriptor)
+        {
+             //
+             // the descriptor must have been processed
+             //
+            ASSERT(OHCI_TD_GET_CONDITION_CODE(TransferDescriptor->Flags) != OHCI_TD_CONDITION_NOT_ACCESSED);
+
+            //
+            // get condition code
+            //
+            ConditionCode = OHCI_TD_GET_CONDITION_CODE(TransferDescriptor->Flags);
+            if (ConditionCode != OHCI_TD_CONDITION_NO_ERROR)
+            {
+                //
+                // FIXME status code
+                //
+                m_NtStatusCode = STATUS_UNSUCCESSFUL;
+
+                switch(ConditionCode)
+                {
+                    case OHCI_TD_CONDITION_CRC_ERROR:
+                        DPRINT1("OHCI_TD_CONDITION_CRC_ERROR detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_CRC;
+                        break;
+                    case OHCI_TD_CONDITION_BIT_STUFFING:
+                        DPRINT1("OHCI_TD_CONDITION_BIT_STUFFING detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_BTSTUFF;
+                        break;
+                    case OHCI_TD_CONDITION_TOGGLE_MISMATCH:
+                        DPRINT1("OHCI_TD_CONDITION_TOGGLE_MISMATCH detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_DATA_TOGGLE_MISMATCH;
+                        break;
+                    case OHCI_TD_CONDITION_STALL:
+                        DPRINT1("OHCI_TD_CONDITION_STALL detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_STALL_PID;
+                        break;
+                    case OHCI_TD_CONDITION_NO_RESPONSE:
+                        DPRINT1("OHCI_TD_CONDITION_NO_RESPONSE detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_DEV_NOT_RESPONDING;
+                        break;
+                    case OHCI_TD_CONDITION_PID_CHECK_FAILURE:
+                        DPRINT1("OHCI_TD_CONDITION_PID_CHECK_FAILURE detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_PID_CHECK_FAILURE;
+                        break;
+                    case OHCI_TD_CONDITION_UNEXPECTED_PID:
+                        DPRINT1("OHCI_TD_CONDITION_UNEXPECTED_PID detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_UNEXPECTED_PID;
+                        break;
+                    case OHCI_TD_CONDITION_DATA_OVERRUN:
+                        DPRINT1("OHCI_TD_CONDITION_DATA_OVERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_DATA_OVERRUN;
+                        break;
+                    case OHCI_TD_CONDITION_DATA_UNDERRUN:
+                        if (m_Irp)
+                        {
+                            //
+                            // get current irp stack location
+                            //
+                            IoStack = IoGetCurrentIrpStackLocation(m_Irp);
+
+                            //
+                            // get urb
+                            //
+                            Urb = (PURB)IoStack->Parameters.Others.Argument1;
+
+                            if(Urb->UrbBulkOrInterruptTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK)
+                            {
+                                //
+                                // short packets are ok
+                                //
+                                ASSERT(Urb->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER);
+                                m_NtStatusCode = STATUS_SUCCESS;
+                                break;
+                            }
+                        }
+                        DPRINT1("OHCI_TD_CONDITION_DATA_UNDERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_DATA_UNDERRUN;
+                        break;
+                    case OHCI_TD_CONDITION_BUFFER_OVERRUN:
+                        DPRINT1("OHCI_TD_CONDITION_BUFFER_OVERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_BUFFER_OVERRUN;
+                        break;
+                    case OHCI_TD_CONDITION_BUFFER_UNDERRUN:
+                        DPRINT1("OHCI_TD_CONDITION_BUFFER_UNDERRUN detected in TransferDescriptor TransferDescriptor %p\n", TransferDescriptor);
+                        m_UrbStatusCode = USBD_STATUS_BUFFER_UNDERRUN;
+                        break;
+                }
+            }
+
+            //
+            // get next
+            //
+            TransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor;
+        }
+    }
+}
+
+VOID
+CUSBRequest::CompletionCallback(
+    struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
+{
+    PIO_STACK_LOCATION IoStack;
+    PURB Urb;
+
+    DPRINT("CUSBRequest::CompletionCallback Descriptor %p PhysicalAddress %x\n", OutDescriptor, OutDescriptor->PhysicalAddress.LowPart);
 
     //
-    // get first general transfer descriptor
+    // check for errors
     //
-    TransferDescriptor = (POHCI_GENERAL_TD)OutDescriptor->HeadLogicalDescriptor;
+    CheckError(OutDescriptor);
 
-    while(TransferDescriptor)
+    if (m_Irp)
     {
         //
-        // get next
+        // set irp completion status
+        //
+        m_Irp->IoStatus.Status = m_NtStatusCode;
+
+        //
+        // get current irp stack location
+        //
+        IoStack = IoGetCurrentIrpStackLocation(m_Irp);
+
+        //
+        // get urb
         //
-        NextTransferDescriptor = (POHCI_GENERAL_TD)TransferDescriptor->NextLogicalDescriptor;
+        Urb = (PURB)IoStack->Parameters.Others.Argument1;
 
         //
-        // is there a buffer associated
+        // store urb status
         //
-        if (TransferDescriptor->BufferSize)
+        Urb->UrbHeader.Status = m_UrbStatusCode;
+
+        //
+        // Check if the MDL was created
+        //
+        if (!Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL)
         {
             //
-            // release buffer
+            // Free Mdl
             //
-            m_DmaManager->Release(TransferDescriptor->BufferLogical, TransferDescriptor->BufferSize);
+            IoFreeMdl(m_TransferBufferMDL);
         }
 
         //
-        // release descriptor
+        // FIXME: support status and calculate length
         //
-        m_DmaManager->Release(TransferDescriptor, sizeof(OHCI_GENERAL_TD));
 
         //
-        // move to next
+        // FIXME: check if the transfer was split
+        // if yes dont complete irp yet
         //
-        TransferDescriptor = NextTransferDescriptor;
+        IoCompleteRequest(m_Irp, IO_NO_INCREMENT);
+    }
+    else
+    {
+        //
+        // signal completion event
+        //
+        PC_ASSERT(m_CompletionEvent);
+        KeSetEvent(m_CompletionEvent, 0, FALSE);
     }
-
-    //
-    // release endpoint descriptor
-    //
-    m_DmaManager->Release(OutDescriptor, sizeof(OHCI_ENDPOINT_DESCRIPTOR));
-
-    //
-    // signal completion event
-    //
-    PC_ASSERT(m_CompletionEvent);
-    KeSetEvent(m_CompletionEvent, 0, FALSE);
 }