Author: janderwald
Date: Fri Mar  2 18:26:08 2012
New Revision: 55960
URL: 
http://svn.reactos.org/svn/reactos?rev=55960&view=rev
Log:
[USBEHCI]
- Check if allocation of queue heads failed
- Implement removing of completed queue head
Modified:
    trunk/reactos/drivers/usb/usbehci/usb_queue.cpp
Modified: trunk/reactos/drivers/usb/usbehci/usb_queue.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/drivers/usb/usbehci/usb_qu…
==============================================================================
--- trunk/reactos/drivers/usb/usbehci/usb_queue.cpp [iso-8859-1] (original)
+++ trunk/reactos/drivers/usb/usbehci/usb_queue.cpp [iso-8859-1] Fri Mar  2 18:26:08 2012
@@ -71,6 +71,9 @@
     // called for each completed queue head
     VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
+    // called for each completed queue head
+    VOID QueueHeadInterruptCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
+
     // called when the completion queue is cleaned up
     VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead);
@@ -80,10 +83,15 @@
     // links interrupt queue head
     VOID LinkInterruptQueueHead(PQUEUE_HEAD QueueHead);
+    // interval index
+    UCHAR GetIntervalIndex(UCHAR Interval);
+
+
     // interrupt queue heads
     PQUEUE_HEAD m_InterruptQueueHeads[EHCI_INTERRUPT_ENTRIES_COUNT];
-
+    // contains the periodic queue heads
+    LIST_ENTRY m_PeriodicQueueHeads;
 };
//=================================================================================================
@@ -151,6 +159,11 @@
     InitializeListHead(&m_PendingRequestAsyncList);
     //
+    // initialize periodic queue heads
+    //
+    InitializeListHead(&m_PeriodicQueueHeads);
+
+    //
     // now initialize sync schedule
     //
     Status = InitializeSyncSchedule(m_Hardware, MemManager);
@@ -198,6 +211,14 @@
         // allocate queue head
         //
         Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead,
&QueueHeadPhysAddr);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // failed to create queue head
+            //
+            DPRINT1("Failed to create queue head\n");
+            return Status;
+        }
         //
         // initialize queue head
@@ -347,46 +368,11 @@
     return Status;
 }
-VOID
-CUSBQueue::LinkInterruptQueueHead(
-    PQUEUE_HEAD QueueHead)
-{
-    PEHCIREQUEST Request;
-    UCHAR Interval, IntervalIndex;
-    USB_DEVICE_SPEED DeviceSpeed;
-    PQUEUE_HEAD InterruptQueueHead;
-
-    // get internal req
-    Request = PEHCIREQUEST(QueueHead->Request);
-    ASSERT(Request);
-
-    // get interval
-    Interval = Request->GetInterval();
-
-    // get device speed
-    DeviceSpeed = Request->GetSpeed();
-    if (DeviceSpeed == UsbHighSpeed)
-    {
-        // interrupt queue head can be scheduled on each possible micro frame
-        QueueHead->EndPointCapabilities.InterruptScheduleMask = 0xFF;
-    }
-    else
-    {
-        // As we do not yet support FSTNs to correctly reference low/full
-        // speed interrupt transfers, we simply put them into the 1 interval
-        // queue. This way we ensure that we reach them on every micro frame
-        // and can do the corresponding start/complete split transactions.
-        // ToDo: use FSTNs to correctly link non high speed interrupt transfers
-        Interval = 1;
-
-        // For now we also force start splits to be in micro frame 0 and
-        // complete splits to be in micro frame 2, 3 and 4.
-        QueueHead->EndPointCapabilities.InterruptScheduleMask = 0x01;
-        QueueHead->EndPointCapabilities.SplitCompletionMask = 0x1C;
-    }
-
-    // sanitize interrupt interval
-    Interval = max(1, Interval);
+UCHAR
+CUSBQueue::GetIntervalIndex(
+    UCHAR Interval)
+{
+    UCHAR IntervalIndex;
     if (Interval == 1)
         IntervalIndex = 1;
@@ -409,6 +395,54 @@
     else
         IntervalIndex = 10;
+    return IntervalIndex;
+}
+
+VOID
+CUSBQueue::LinkInterruptQueueHead(
+    PQUEUE_HEAD QueueHead)
+{
+    PEHCIREQUEST Request;
+    UCHAR Interval, IntervalIndex;
+    USB_DEVICE_SPEED DeviceSpeed;
+    PQUEUE_HEAD InterruptQueueHead;
+
+    // get internal req
+    Request = PEHCIREQUEST(QueueHead->Request);
+    ASSERT(Request);
+
+    // get interval
+    Interval = Request->GetInterval();
+
+    // get device speed
+    DeviceSpeed = Request->GetSpeed();
+    if (DeviceSpeed == UsbHighSpeed)
+    {
+        // interrupt queue head can be scheduled on each possible micro frame
+        QueueHead->EndPointCapabilities.InterruptScheduleMask = 0xFF;
+    }
+    else
+    {
+        // As we do not yet support FSTNs to correctly reference low/full
+        // speed interrupt transfers, we simply put them into the 1 interval
+        // queue. This way we ensure that we reach them on every micro frame
+        // and can do the corresponding start/complete split transactions.
+        // ToDo: use FSTNs to correctly link non high speed interrupt transfers
+        Interval = 1;
+
+        // For now we also force start splits to be in micro frame 0 and
+        // complete splits to be in micro frame 2, 3 and 4.
+        QueueHead->EndPointCapabilities.InterruptScheduleMask = 0x01;
+        QueueHead->EndPointCapabilities.SplitCompletionMask = 0x1C;
+    }
+
+    // sanitize interrupt interval
+    Interval = max(1, Interval);
+
+    // get interval index
+    IntervalIndex = GetIntervalIndex(Interval);
+
+
     // get interrupt queue head
     InterruptQueueHead = m_InterruptQueueHeads[IntervalIndex];
@@ -418,6 +452,9 @@
     InterruptQueueHead->HorizontalLinkPointer = QueueHead->PhysicalAddr |
QH_TYPE_QH;
     InterruptQueueHead->NextQueueHead = QueueHead;
+
+    // store in periodic list
+    InsertTailList(&m_PeriodicQueueHeads, &QueueHead->LinkedQueueHeads);
 }
 //
@@ -595,6 +632,67 @@
 }
 VOID
+CUSBQueue::QueueHeadInterruptCompletion(
+    PQUEUE_HEAD QueueHead,
+    NTSTATUS Status)
+{
+    PEHCIREQUEST Request;
+    UCHAR Interval, IntervalIndex;
+    PQUEUE_HEAD InterruptQueueHead, LastQueueHead = NULL;
+
+
+    //
+    // sanity check
+    //
+    PC_ASSERT(QueueHead->Request);
+
+    //
+    // get IUSBRequest interface
+    //
+    Request = (PEHCIREQUEST)QueueHead->Request;
+
+    // get interval
+    Interval = Request->GetInterval();
+
+    // sanitize interval
+    Interval = max(1, Interval);
+
+    // get interval index
+    IntervalIndex = GetIntervalIndex(Interval);
+
+    // get interrupt queue head from index
+    InterruptQueueHead = m_InterruptQueueHeads[IntervalIndex];
+
+    while(InterruptQueueHead != NULL)
+    {
+        if (InterruptQueueHead == QueueHead)
+            break;
+
+        // move to next queue head
+        LastQueueHead = InterruptQueueHead;
+        InterruptQueueHead = (PQUEUE_HEAD)InterruptQueueHead->NextQueueHead;
+    }
+
+    if (InterruptQueueHead != QueueHead)
+    {
+        // queue head not in list
+        ASSERT(FALSE);
+        return;
+    }
+
+    // now unlink queue head
+    LastQueueHead->HorizontalLinkPointer = QueueHead->HorizontalLinkPointer;
+    LastQueueHead->NextQueueHead = QueueHead->NextQueueHead;
+
+    DPRINT1("Periodic QueueHead %p Addr $x unlinked\n", QueueHead,
QueueHead->PhysicalAddr);
+
+    // insert into completed list
+    InsertTailList(&m_CompletedRequestAsyncList,
&QueueHead->LinkedQueueHeads);
+}
+
+
+
+VOID
 CUSBQueue::QueueHeadCompletion(
     PQUEUE_HEAD CurrentQH,
     NTSTATUS Status)
@@ -619,15 +717,6 @@
 VOID
 CUSBQueue::ProcessPeriodicSchedule(
-    IN NTSTATUS Status,
-    OUT PULONG ShouldRingDoorBell)
-{
-
-
-}
-
-VOID
-CUSBQueue::ProcessAsyncList(
     IN NTSTATUS Status,
     OUT PULONG ShouldRingDoorBell)
 {
@@ -646,6 +735,82 @@
     // walk async list
     //
     ASSERT(AsyncListQueueHead);
+    Entry = m_PeriodicQueueHeads.Flink;
+
+    while(Entry != &m_PeriodicQueueHeads)
+    {
+        //
+        // get queue head structure
+        //
+        QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
+        ASSERT(QueueHead);
+
+        //
+        // sanity check
+        //
+        PC_ASSERT(QueueHead->Request);
+
+        //
+        // get IUSBRequest interface
+        //
+        Request = (PEHCIREQUEST)QueueHead->Request;
+
+        //
+        // move to next entry
+        //
+        Entry = Entry->Flink;
+
+        //
+        // check if queue head is complete
+        //
+        IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
+
+        DPRINT("Request %p QueueHead %p Complete %d\n", Request, QueueHead,
IsQueueHeadComplete);
+
+        //
+        // check if queue head is complete
+        //
+        if (IsQueueHeadComplete)
+        {
+            //
+            // current queue head is complete
+            //
+            QueueHeadInterruptCompletion(QueueHead, Status);
+
+            //
+            // ring door bell is going to be necessary
+            //
+            *ShouldRingDoorBell = TRUE;
+        }
+    }
+
+    //
+    // release lock
+    //
+    KeReleaseSpinLock(m_Lock, OldLevel);
+
+}
+
+VOID
+CUSBQueue::ProcessAsyncList(
+    IN NTSTATUS Status,
+    OUT PULONG ShouldRingDoorBell)
+{
+    KIRQL OldLevel;
+    PLIST_ENTRY Entry;
+    PQUEUE_HEAD QueueHead;
+    PEHCIREQUEST Request;
+    BOOLEAN IsQueueHeadComplete;
+
+    //
+    // lock completed async list
+    //
+    KeAcquireSpinLock(m_Lock, &OldLevel);
+
+    //
+    // walk async list
+    //
+    ASSERT(AsyncListQueueHead);
     Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
     while(Entry != &AsyncListQueueHead->LinkedQueueHeads)