[USBOHCI]
authorJohannes Anderwald <johannes.anderwald@reactos.org>
Fri, 27 May 2011 13:04:55 +0000 (13:04 +0000)
committerJohannes Anderwald <johannes.anderwald@reactos.org>
Fri, 27 May 2011 13:04:55 +0000 (13:04 +0000)
- Implement function to retrieve isochronous head endpoint descriptor
- Implement function to retrieve current frame number
- Set isochronous descriptor flag for head endpoint
- Implement retrieve device status for devices
- Implement retrieve class status from device
- Partly implement isochronous descriptor handling in usb queue
- Start implementing isochronous transfers in ISUBRequest
- Code currently not tested as the Virtual Machine with XP + ReactOS usbohci driver brings down the host system when starting the iso transfers. Ironically it crashes in MS usbohci driver

svn path=/branches/usb-bringup/; revision=51957

drivers/usb/usbohci/hardware.cpp
drivers/usb/usbohci/hardware.h
drivers/usb/usbohci/hub_controller.cpp
drivers/usb/usbohci/interfaces.h
drivers/usb/usbohci/usb_queue.cpp
drivers/usb/usbohci/usb_request.cpp

index 1d53c8f..e4650c1 100644 (file)
@@ -62,8 +62,10 @@ public:
     NTSTATUS GetBulkHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
     NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
     NTSTATUS GetInterruptEndpointDescriptors(struct _OHCI_ENDPOINT_DESCRIPTOR *** OutDescriptor);
+    NTSTATUS GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor);
     VOID HeadEndpointDescriptorModified(ULONG HeadType);
 
+
     NTSTATUS GetDMA(OUT struct IDMAMemoryManager **m_DmaManager);
     NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
 
@@ -80,6 +82,7 @@ public:
 
     KIRQL AcquireDeviceLock(void);
     VOID ReleaseDeviceLock(KIRQL OldLevel);
+    virtual VOID GetCurrentFrameNumber(PULONG FrameNumber);
     // local
     BOOLEAN InterruptService();
     NTSTATUS InitializeController();
@@ -698,6 +701,17 @@ CUSBHardwareDevice::GetInterruptEndpointDescriptors(
     return STATUS_SUCCESS;
 }
 
+NTSTATUS
+CUSBHardwareDevice::GetIsochronousHeadEndpointDescriptor(
+    struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor)
+{
+    //
+    // get descriptor
+    //
+    *OutDescriptor = m_IsoEndpointDescriptor;
+    return STATUS_SUCCESS;
+}
+
 VOID
 CUSBHardwareDevice::HeadEndpointDescriptorModified(
     ULONG Type)
@@ -848,6 +862,11 @@ CUSBHardwareDevice::InitializeController()
     //
     m_InterruptEndpoints[0]->NextPhysicalEndpoint = m_IsoEndpointDescriptor->PhysicalAddress.LowPart;
 
+    //
+    // set iso endpoint type
+    //
+    m_IsoEndpointDescriptor->Flags |= OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
+
     //
     // done
     //
@@ -1183,6 +1202,15 @@ CUSBHardwareDevice::AcquireDeviceLock(void)
     return OldLevel;
 }
 
+VOID
+CUSBHardwareDevice::GetCurrentFrameNumber(
+    PULONG FrameNumber)
+{
+    //
+    // store frame number
+    //
+    *FrameNumber = m_HCCA->CurrentFrameNumber;
+}
 
 VOID
 CUSBHardwareDevice::ReleaseDeviceLock(
index 864b96d..cf4fb54 100644 (file)
@@ -231,6 +231,8 @@ typedef struct _OHCI_ENDPOINT_DESCRIPTOR
 #define OHCI_ENDPOINT_FULL_SPEED                0x00000000
 #define OHCI_ENDPOINT_DIRECTION_OUT             0x00000800
 #define OHCI_ENDPOINT_DIRECTION_IN              0x00001000
+#define OHCI_ENDPOINT_GENERAL_FORMAT            0x00000000
+#define OHCI_ENDPOINT_ISOCHRONOUS_FORMAT        0x00008000
 
 //
 // Maximum port count set by OHCI
@@ -293,3 +295,35 @@ typedef struct
 #define OHCI_TD_CONDITION_BUFFER_OVERRUN    0x0c
 #define OHCI_TD_CONDITION_BUFFER_UNDERRUN   0x0d
 #define OHCI_TD_CONDITION_NOT_ACCESSED      0x0f
+
+// --------------------------------
+//  Isochronous transfer descriptor structure (section 4.3.2)
+// --------------------------------
+
+#define OHCI_ITD_NOFFSET 8
+
+typedef struct _OHCI_ISO_TD_
+{
+
+    // Hardware part 32 byte
+    ULONG Flags;
+    ULONG BufferPhysical;                       // Physical page number of byte 0
+    ULONG NextPhysicalDescriptor;               // Next isochronous transfer descriptor
+    ULONG LastPhysicalByteAddress;              // Physical buffer end
+    ULONG Offset[OHCI_ITD_NOFFSET];             // Buffer offsets
+
+    // Software part
+    PHYSICAL_ADDRESS PhysicalAddress;             // Physical address of this descriptor
+    struct _OHCI_ISO_TD_ * NextLogicalDescriptor; // Logical pointer next descriptor
+}OHCI_ISO_TD, *POHCI_ISO_TD;
+
+#define OHCI_ITD_GET_STARTING_FRAME(x)          ((x) & 0x0000ffff)
+#define OHCI_ITD_SET_STARTING_FRAME(x)          ((x) & 0xffff)
+#define OHCI_ITD_GET_DELAY_INTERRUPT(x)         (((x) >> 21) & 7)
+#define OHCI_ITD_SET_DELAY_INTERRUPT(x)         ((x) << 21)
+#define OHCI_ITD_NO_INTERRUPT                   0x00e00000
+#define OHCI_ITD_GET_FRAME_COUNT(x)             ((((x) >> 24) & 7) + 1)
+#define OHCI_ITD_SET_FRAME_COUNT(x)             (((x) - 1) << 24)
+#define OHCI_ITD_GET_CONDITION_CODE(x)          ((x) >> 28)
+#define OHCI_ITD_NO_CONDITION_CODE              0xf0000000
+
index 1f6bc55..24e08a8 100644 (file)
@@ -1119,7 +1119,10 @@ CHubController::HandleGetStatusFromDevice(
     IN OUT PIRP Irp, 
     PURB Urb)
 {
-    PUSHORT Status;
+    PUSHORT DeviceStatus;
+    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
+    NTSTATUS Status;
+    PUSBDEVICE UsbDevice;
 
     //
     // sanity checks
@@ -1127,22 +1130,54 @@ CHubController::HandleGetStatusFromDevice(
     PC_ASSERT(Urb->UrbControlGetStatusRequest.Index == 0);
     PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBufferLength >= sizeof(USHORT));
     PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBuffer);
-    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle == NULL); 
 
     //
     // get status buffer
     //
-    Status = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer;
+    DeviceStatus = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer;
+
+
+    if (Urb->UrbHeader.UsbdDeviceHandle == NULL)
+    {
+        //
+        // FIXME need more flags ?
+        //
+        *DeviceStatus = USB_PORT_STATUS_CONNECT;
+        return STATUS_SUCCESS;
+    }
+
+    //
+    // check if this is a valid usb device handle
+    //
+    ASSERT(ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)));
 
     //
-    // FIXME need more flags ?
+    // get device
     //
-    *Status = USB_PORT_STATUS_CONNECT;
+    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);
+
+
+     //
+     // generate setup packet
+     //
+     CtrlSetup.bRequest = USB_REQUEST_GET_STATUS;
+     CtrlSetup.wValue.LowByte = 0;
+     CtrlSetup.wValue.HiByte = 0;
+     CtrlSetup.wIndex.W = Urb->UrbControlGetStatusRequest.Index; 
+     CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength;
+     CtrlSetup.bmRequestType.B = 0x80;
+
+    //
+    // submit setup packet
+    //
+    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
+    ASSERT(Status == STATUS_SUCCESS);
+    DPRINT1("CHubController::HandleGetStatusFromDevice Status %x Length %lu DeviceStatus %x\n", Status, Urb->UrbControlDescriptorRequest.TransferBufferLength, *DeviceStatus);
 
     //
     // done
     //
-    return STATUS_SUCCESS;
+    return Status;
 }
 
 //-----------------------------------------------------------------------------------------
@@ -1155,6 +1190,8 @@ CHubController::HandleClassDevice(
     PUSB_HUB_DESCRIPTOR UsbHubDescriptor;
     ULONG PortCount, Dummy2;
     USHORT Dummy1;
+    PUSBDEVICE UsbDevice;
+    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
 
     DPRINT("CHubController::HandleClassDevice Request %x Class %x\n", Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value >> 8);
 
@@ -1163,6 +1200,36 @@ CHubController::HandleClassDevice(
     //
     switch(Urb->UrbControlVendorClassRequest.Request)
     {
+        case USB_REQUEST_GET_STATUS:
+        {
+            //
+            // check if this is a valid usb device handle
+            //
+            ASSERT(ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)));
+
+            //
+            // get device
+            //
+            UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);
+
+
+            //
+            // generate setup packet
+            //
+            CtrlSetup.bRequest = USB_REQUEST_GET_STATUS;
+            CtrlSetup.wValue.LowByte = Urb->UrbControlVendorClassRequest.Index;
+            CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
+            CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
+            CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength;
+            CtrlSetup.bmRequestType.B = 0xA0;
+
+            //
+            // submit setup packet
+            //
+            Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
+            ASSERT(Status == STATUS_SUCCESS);
+            break;
+        }
         case USB_REQUEST_GET_DESCRIPTOR:
         {
             switch (Urb->UrbControlVendorClassRequest.Value >> 8)
index 582b7fb..e7920e2 100644 (file)
@@ -188,6 +188,15 @@ DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown)
 
     virtual NTSTATUS GetControlHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0;
 
+//-----------------------------------------------------------------------------------------
+//
+// GetIsochronousHeadEndpointDescriptor
+//
+// Description: returns the control head endpoint descriptor
+
+    virtual NTSTATUS GetIsochronousHeadEndpointDescriptor(struct _OHCI_ENDPOINT_DESCRIPTOR ** OutDescriptor) = 0;
+
+
 //-----------------------------------------------------------------------------------------
 //
 // GetInterruptEndpointDescriptors
@@ -301,6 +310,14 @@ DECLARE_INTERFACE_(IUSBHardwareDevice, IUnknown)
 // Description: releases the device lock
 
     virtual void ReleaseDeviceLock(KIRQL OldLevel) = 0;
+
+//----------------------------------------------------------------------------------------
+//
+// GetCurrentFrameNumber
+//
+// Description: returns the current frame  number
+
+    virtual VOID GetCurrentFrameNumber(PULONG FrameNumber) = 0;
 };
 
 typedef IUSBHardwareDevice *PUSBHARDWAREDEVICE;
index 5880b61..a977350 100644 (file)
@@ -61,6 +61,7 @@ protected:
     PUSBHARDWAREDEVICE m_Hardware;                                                      // hardware
     POHCI_ENDPOINT_DESCRIPTOR m_BulkHeadEndpointDescriptor;                             // bulk head descriptor
     POHCI_ENDPOINT_DESCRIPTOR m_ControlHeadEndpointDescriptor;                          // control head descriptor
+    POHCI_ENDPOINT_DESCRIPTOR m_IsoHeadEndpointDescriptor;                              // isochronous head descriptor
     POHCI_ENDPOINT_DESCRIPTOR * m_InterruptEndpoints;
 };
 
@@ -100,6 +101,9 @@ CUSBQueue::Initialize(
     //
     Hardware->GetControlHeadEndpointDescriptor(&m_ControlHeadEndpointDescriptor);
 
+    //
+    Hardware->GetIsochronousHeadEndpointDescriptor(&m_IsoHeadEndpointDescriptor);
+
     //
     // get interrupt endpoints
     //
@@ -164,6 +168,8 @@ CUSBQueue::AddUSBRequest(
     KIRQL OldLevel;
     POHCI_ENDPOINT_DESCRIPTOR HeadDescriptor;
     POHCI_ENDPOINT_DESCRIPTOR Descriptor;
+    POHCI_ISO_TD CurrentDescriptor;
+    ULONG FrameNumber;
 
     DPRINT("CUSBQueue::AddUSBRequest\n");
 
@@ -177,39 +183,6 @@ CUSBQueue::AddUSBRequest(
     //
     Type = Request->GetTransferType();
 
-    //
-    // check if supported
-    //
-    switch(Type)
-    {
-        case USB_ENDPOINT_TYPE_ISOCHRONOUS:
-            /* NOT IMPLEMENTED IN QUEUE */
-            Status = STATUS_NOT_SUPPORTED;
-            break;
-        case USB_ENDPOINT_TYPE_INTERRUPT:
-        case USB_ENDPOINT_TYPE_CONTROL:
-        case USB_ENDPOINT_TYPE_BULK:
-            Status = STATUS_SUCCESS;
-            break;
-        default:
-            /* BUG */
-            PC_ASSERT(FALSE);
-            Status = STATUS_NOT_SUPPORTED;
-    }
-
-    //
-    // check for success
-    //
-    if (!NT_SUCCESS(Status))
-    {
-        //
-        // request not supported, please try later
-        //
-        DPRINT1("Request Type %x not supported\n", Type);
-        ASSERT(FALSE);
-        return Status;
-    }
-
     //
     // add extra reference which is released when the request is completed
     //
@@ -258,6 +231,48 @@ CUSBQueue::AddUSBRequest(
         HeadDescriptor = FindInterruptEndpointDescriptor(Request->GetInterval());
         ASSERT(HeadDescriptor);
     }
+    else if (Type == USB_ENDPOINT_TYPE_ISOCHRONOUS)
+    {
+        //
+        // get head descriptor
+        //
+        HeadDescriptor = m_IsoHeadEndpointDescriptor;
+
+        //
+        // get current frame number
+        //
+        m_Hardware->GetCurrentFrameNumber(&FrameNumber);
+
+        //
+        // increment frame number
+        //
+        FrameNumber++;
+
+        //
+        // apply frame number to iso transfer descriptors
+        //
+        CurrentDescriptor = (POHCI_ISO_TD)Descriptor->HeadLogicalDescriptor;
+
+        DPRINT1("ISO: NextFrameNumber %x\n", FrameNumber);
+
+        while(CurrentDescriptor)
+        {
+            //
+            // set current frame number
+            //
+            CurrentDescriptor->Flags |= OHCI_ITD_SET_STARTING_FRAME(FrameNumber);
+
+            //
+            // move to next frame number
+            //
+            FrameNumber++;
+
+            //
+            // move to next descriptor
+            //
+            CurrentDescriptor = CurrentDescriptor->NextLogicalDescriptor;
+        }
+    }
 
     //
     // insert endpoint at end
@@ -269,9 +284,6 @@ CUSBQueue::AddUSBRequest(
     //
     Descriptor->Flags &= ~OHCI_ENDPOINT_SKIP;
 
-    DPRINT("Request %x Logical %x added to queue Queue %p Logical %x\n", Descriptor, Descriptor->PhysicalAddress.LowPart, HeadDescriptor, HeadDescriptor->PhysicalAddress.LowPart);
-
-
     if (Type == USB_ENDPOINT_TYPE_CONTROL || Type == USB_ENDPOINT_TYPE_BULK)
     {
         //
@@ -280,6 +292,7 @@ CUSBQueue::AddUSBRequest(
         m_Hardware->HeadEndpointDescriptorModified(Type);
     }
 
+
     return STATUS_SUCCESS;
 }
 
index f3db44a..dcb9c19 100644 (file)
@@ -57,9 +57,11 @@ public:
     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();
 
@@ -243,6 +245,81 @@ CUSBRequest::InitializeWithIrp(
     //
     switch (Urb->UrbHeader.Function)
     {
+        case URB_FUNCTION_ISOCH_TRANSFER:
+        {
+            //
+            // there must be at least one packet
+            //
+            ASSERT(Urb->UrbIsochronousTransfer.NumberOfPackets);
+
+            //
+            // is there data to be transferred
+            //
+            if (Urb->UrbIsochronousTransfer.TransferBufferLength)
+            {
+                //
+                // Check if there is a MDL
+                //
+                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);
+                }
+                else
+                {
+                    //
+                    // use provided mdl
+                    //
+                    m_TransferBufferMDL = Urb->UrbIsochronousTransfer.TransferBufferMDL;
+                }
+            }
+
+            //
+            // save buffer length
+            //
+            m_TransferBufferLength = Urb->UrbIsochronousTransfer.TransferBufferLength;
+
+            //
+            // Set Length Completed to 0
+            //
+            m_TransferBufferLengthCompleted = 0;
+
+            //
+            // get endpoint descriptor
+            //
+            m_EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbIsochronousTransfer.PipeHandle;
+
+            //
+            // completed initialization
+            //
+            break;
+        }
         //
         // luckily those request have the same structure layout
         //
@@ -525,6 +602,230 @@ 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, PreviousDescriptor = NULL, CurrentDescriptor;
+    POHCI_ENDPOINT_DESCRIPTOR EndpointDescriptor;
+    ULONG Index = 0, SubIndex, NumberOfPackets, PageOffset;
+    NTSTATUS Status;
+    PVOID Buffer;
+    PIO_STACK_LOCATION IoStack;
+    PURB Urb;
+
+    DPRINT1("cp\n");
+    //
+    // 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
+        //
+        return Status;
+    }
+    DPRINT1("cp\n");
+    //
+    // get buffer
+    //
+    Buffer = MmGetSystemAddressForMdlSafe(m_TransferBufferMDL, NormalPagePriority);
+    ASSERT(Buffer);
+
+    DPRINT1("cp\n");
+    while(Index < Urb->UrbIsochronousTransfer.NumberOfPackets)
+    {
+        //
+        // get number of packets remaining
+        //
+        NumberOfPackets = min(Urb->UrbIsochronousTransfer.NumberOfPackets - Index, OHCI_ITD_NOFFSET);
+    DPRINT1("cp Number Packets %lu\n", NumberOfPackets);
+        //
+        // allocate iso descriptor
+        //
+        Status = CreateIsochronousTransferDescriptor(&CurrentDescriptor, NumberOfPackets);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // FIXME: cleanup
+            // failed to allocate descriptor
+            //
+            ASSERT(FALSE);
+            return Status;
+        }
+    DPRINT1("cp\n");
+        //
+        // initialize descriptor
+        //
+        CurrentDescriptor->BufferPhysical = (MmGetPhysicalAddress(Buffer).LowPart & ~ (PAGE_SIZE - 1));
+
+        //
+        // get page offset
+        //
+        PageOffset = BYTE_OFFSET(MmGetPhysicalAddress(Buffer).LowPart);
+    DPRINT1("cp\n");
+        for(SubIndex = 0; SubIndex < NumberOfPackets; SubIndex++)
+        {
+            //
+            // store buffer offset
+            //
+            CurrentDescriptor->Offset[SubIndex] = Urb->UrbIsochronousTransfer.IsoPacket[Index].Offset + PageOffset;
+        }
+
+        //
+        // increment packet offset
+        //
+        Index += NumberOfPackets;
+
+        //
+        // check if this is the last descriptor
+        //
+        if (Index == Urb->UrbIsochronousTransfer.NumberOfPackets)
+        {
+            //
+            // end of transfer
+            //
+            CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + m_TransferBufferLength - 1;
+        }
+        else
+        {
+            //
+            // use start address of next packet - 1
+            //
+            CurrentDescriptor->LastPhysicalByteAddress = CurrentDescriptor->BufferPhysical + PageOffset + Urb->UrbIsochronousTransfer.IsoPacket[Index + 1].Offset - 1;
+
+            //
+            // move buffer to next address
+            //
+            Buffer = (PVOID)((ULONG_PTR)Buffer + Urb->UrbIsochronousTransfer.IsoPacket[Index + 1].Offset);
+        }
+
+        //
+        // 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("cp\n");
+
+    //
+    // 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;
+    DPRINT1("cp\n");
+    //
+    // store result
+    //
+    *OutEndpointDescriptor = EndpointDescriptor;
+
+    //
+    // done
+    //
+    return STATUS_SUCCESS;
+}
+
 //----------------------------------------------------------------------------------------
 NTSTATUS
 CUSBRequest::CreateGeneralTransferDescriptor(
@@ -1084,8 +1385,7 @@ CUSBRequest::GetEndpointDescriptor(
             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);
@@ -1201,7 +1501,6 @@ VOID
 CUSBRequest::CompletionCallback(
     struct _OHCI_ENDPOINT_DESCRIPTOR * OutDescriptor)
 {
-    POHCI_GENERAL_TD TransferDescriptor, NextTransferDescriptor;
     PIO_STACK_LOCATION IoStack;
     PURB Urb;