2 * PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/usb/usbehci/usb_queue.cpp
5 * PURPOSE: USB EHCI device driver.
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
14 class CUSBQueue
: public IUSBQueue
17 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
19 STDMETHODIMP_(ULONG
) AddRef()
21 InterlockedIncrement(&m_Ref
);
24 STDMETHODIMP_(ULONG
) Release()
26 InterlockedDecrement(&m_Ref
);
36 virtual NTSTATUS
Initialize(IN PUSBHARDWAREDEVICE Hardware
, PDMA_ADAPTER AdapterObject
, IN OPTIONAL PKSPIN_LOCK Lock
);
37 virtual ULONG
GetPendingRequestCount();
38 virtual NTSTATUS
AddUSBRequest(PURB Urb
);
39 virtual NTSTATUS
AddUSBRequest(IUSBRequest
* Request
);
40 virtual NTSTATUS
CancelRequests();
41 virtual NTSTATUS
CreateUSBRequest(IUSBRequest
**OutRequest
);
42 virtual VOID
InterruptCallback(IN NTSTATUS Status
, OUT PULONG ShouldRingDoorBell
);
43 virtual VOID
CompleteAsyncRequests();
45 // constructor / destructor
46 CUSBQueue(IUnknown
*OuterUnknown
){}
47 virtual ~CUSBQueue(){}
52 PDMA_ADAPTER m_Adapter
;
53 PQUEUE_HEAD AsyncListQueueHead
;
54 PQUEUE_HEAD PendingListQueueHead
;
55 LIST_ENTRY m_CompletedRequestAsyncList
;
57 // queue head manipulation functions
58 VOID
LinkQueueHead(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
59 VOID
UnlinkQueueHead(PQUEUE_HEAD QueueHead
);
60 VOID
LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
61 PQUEUE_HEAD
UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, ULONG Count
);
63 // processes the async list
64 VOID
ProcessAsyncList(IN NTSTATUS Status
, OUT PULONG ShouldRingDoorBell
);
66 // called for each completed queue head
67 VOID
QueueHeadCompletion(PQUEUE_HEAD QueueHead
, NTSTATUS Status
);
69 // called when the completion queue is cleaned up
70 VOID
QueueHeadCleanup(PQUEUE_HEAD QueueHead
);
73 //=================================================================================================
78 CUSBQueue::QueryInterface(
82 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
84 *Output
= PVOID(PUNKNOWN(this));
85 PUNKNOWN(*Output
)->AddRef();
86 return STATUS_SUCCESS
;
89 return STATUS_UNSUCCESSFUL
;
93 CUSBQueue::Initialize(
94 IN PUSBHARDWAREDEVICE Hardware
,
95 PDMA_ADAPTER AdapterObject
,
96 IN OPTIONAL PKSPIN_LOCK Lock
)
98 NTSTATUS Status
= STATUS_SUCCESS
;
100 DPRINT1("CUSBQueue::Initialize()\n");
105 // initialize device lock
107 KeInitializeSpinLock(&m_Lock
);
110 // Get the AsyncQueueHead
112 AsyncListQueueHead
= (PQUEUE_HEAD
)Hardware
->GetAsyncListRegister();
115 // Create the PendingListQueueHead from NONPAGEDPOOL. It will never be linked into the Asynclist Schedule
117 PendingListQueueHead
= (PQUEUE_HEAD
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(QUEUE_HEAD
), TAG_USBEHCI
);
118 if (!PendingListQueueHead
)
120 DPRINT1("Pool Allocation failed!\n");
121 return STATUS_INSUFFICIENT_RESOURCES
;
125 // Initialize the List Head
127 InitializeListHead(&PendingListQueueHead
->LinkedQueueHeads
);
130 // fake the queue head as the first queue head
132 PendingListQueueHead
->PhysicalAddr
= ((ULONG_PTR
)AsyncListQueueHead
| QH_TYPE_QH
);
135 // Initialize completed async list head
137 InitializeListHead(&m_CompletedRequestAsyncList
);
144 CUSBQueue::GetPendingRequestCount()
147 // Loop through the pending list and iterrate one for each QueueHead that
148 // has a IRP to complete.
156 CUSBQueue::AddUSBRequest(
157 IUSBRequest
* Request
)
159 PQUEUE_HEAD QueueHead
;
160 ASSERT(Request
!= NULL
);
162 Request
->GetQueueHead(&QueueHead
);
165 // Add it to the pending list
167 LinkQueueHead(PendingListQueueHead
, QueueHead
);
170 // add extra reference which is released when the request is completed
174 return STATUS_SUCCESS
;
178 CUSBQueue::AddUSBRequest(
182 return STATUS_NOT_IMPLEMENTED
;
186 CUSBQueue::CancelRequests()
189 return STATUS_NOT_IMPLEMENTED
;
193 CUSBQueue::CreateUSBRequest(
194 IUSBRequest
**OutRequest
)
196 PUSBREQUEST UsbRequest
;
200 Status
= InternalCreateUSBRequest(&UsbRequest
);
202 if (NT_SUCCESS(Status
))
204 *OutRequest
= UsbRequest
;
211 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
214 CUSBQueue::LinkQueueHead(
215 PQUEUE_HEAD HeadQueueHead
,
216 PQUEUE_HEAD NewQueueHead
)
218 PQUEUE_HEAD LastQueueHead
, NextQueueHead
;
220 ASSERT(HeadQueueHead
);
221 ASSERT(NewQueueHead
);
224 // Link the LIST_ENTRYs
226 InsertTailList(&HeadQueueHead
->LinkedQueueHeads
, &NewQueueHead
->LinkedQueueHeads
);
229 // Update HLP for Previous QueueHead, which should be the last in list.
231 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
232 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
233 LastQueueHead
->HorizontalLinkPointer
= (NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
236 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
238 Entry
= NewQueueHead
->LinkedQueueHeads
.Flink
;
239 NextQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
240 ASSERT(NextQueueHead
== HeadQueueHead
);
241 NewQueueHead
->HorizontalLinkPointer
= NextQueueHead
->PhysicalAddr
;
245 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
248 CUSBQueue::UnlinkQueueHead(
249 PQUEUE_HEAD QueueHead
)
251 PQUEUE_HEAD PreviousQH
, NextQH
;
255 // sanity check: there must be at least one queue head with halted bit set
257 PC_ASSERT(QueueHead
->Token
.Bits
.Halted
== 0);
262 Entry
= QueueHead
->LinkedQueueHeads
.Blink
;
265 // get queue head structure
267 PreviousQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
272 Entry
= QueueHead
->LinkedQueueHeads
.Flink
;
275 // get queue head structure
277 NextQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
282 ASSERT(QueueHead
->HorizontalLinkPointer
== (NextQH
->PhysicalAddr
| QH_TYPE_QH
));
285 // remove queue head from linked list
287 PreviousQH
->HorizontalLinkPointer
= NextQH
->PhysicalAddr
| QH_TYPE_QH
;
290 // remove software link
292 RemoveEntryList(&QueueHead
->LinkedQueueHeads
);
296 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
299 CUSBQueue::LinkQueueHeadChain(
300 PQUEUE_HEAD HeadQueueHead
,
301 PQUEUE_HEAD NewQueueHead
)
303 PQUEUE_HEAD LastQueueHead
;
305 ASSERT(HeadQueueHead
);
306 ASSERT(NewQueueHead
);
309 // Find the last QueueHead in NewQueueHead
311 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
312 ASSERT(Entry
!= NewQueueHead
->LinkedQueueHeads
.Flink
);
313 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
316 // Set the LinkPointer and Flink
318 LastQueueHead
->HorizontalLinkPointer
= HeadQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
319 LastQueueHead
->LinkedQueueHeads
.Flink
= &HeadQueueHead
->LinkedQueueHeads
;
322 // Fine the last QueueHead in HeadQueueHead
324 Entry
= HeadQueueHead
->LinkedQueueHeads
.Blink
;
325 HeadQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
326 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
327 LastQueueHead
->LinkedQueueHeads
.Flink
= &NewQueueHead
->LinkedQueueHeads
;
328 LastQueueHead
->HorizontalLinkPointer
= NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
332 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
333 // returns the chain of QueueHeads removed from HeadQueueHead.
336 CUSBQueue::UnlinkQueueHeadChain(
337 PQUEUE_HEAD HeadQueueHead
,
340 PQUEUE_HEAD LastQueueHead
, FirstQueueHead
;
345 // Find the last QueueHead in NewQueueHead
347 Entry
= &HeadQueueHead
->LinkedQueueHeads
;
348 FirstQueueHead
= CONTAINING_RECORD(Entry
->Flink
, QUEUE_HEAD
, LinkedQueueHeads
);
350 for (Index
= 0; Index
< Count
; Index
++)
352 Entry
= Entry
->Flink
;
354 if (Entry
== &HeadQueueHead
->LinkedQueueHeads
)
356 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index
);
362 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
363 HeadQueueHead
->LinkedQueueHeads
.Flink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
364 if (Count
+ 1 == Index
)
366 HeadQueueHead
->LinkedQueueHeads
.Blink
= &HeadQueueHead
->LinkedQueueHeads
;
369 HeadQueueHead
->LinkedQueueHeads
.Blink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
371 FirstQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
372 LastQueueHead
->LinkedQueueHeads
.Flink
= &FirstQueueHead
->LinkedQueueHeads
;
373 LastQueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
374 return FirstQueueHead
;
378 CUSBQueue::QueueHeadCompletion(
379 PQUEUE_HEAD CurrentQH
,
382 IUSBRequest
*Request
;
383 USBD_STATUS UrbStatus
;
384 PQUEUE_HEAD NewQueueHead
;
387 // this function is called when a queue head has been completed
389 PC_ASSERT(CurrentQH
->Token
.Bits
.Active
== 0);
392 // get contained usb request
394 Request
= (IUSBRequest
*)CurrentQH
->Request
;
402 // check if the queue head was completed with errors
404 if (CurrentQH
->Token
.Bits
.Halted
)
406 if (CurrentQH
->Token
.Bits
.DataBufferError
)
411 UrbStatus
= USBD_STATUS_DATA_BUFFER_ERROR
;
413 else if (CurrentQH
->Token
.Bits
.BabbleDetected
)
418 UrbStatus
= USBD_STATUS_BABBLE_DETECTED
;
425 UrbStatus
= USBD_STATUS_STALL_PID
;
433 UrbStatus
= USBD_STATUS_SUCCESS
;
437 // notify request that a queue head has been completed
439 Request
->CompletionCallback(Status
, UrbStatus
, CurrentQH
);
442 // now unlink the queue head
443 // FIXME: implement chained queue heads
445 UnlinkQueueHead(CurrentQH
);
448 // check if the request is complete
450 if (Request
->IsRequestComplete() == FALSE
)
453 // request is still in complete
454 // get new queue head
456 Status
= Request
->GetQueueHead(&NewQueueHead
);
459 // add to pending list
461 LinkQueueHead(PendingListQueueHead
, NewQueueHead
);
466 // put queue head into completed queue head list
468 InsertTailList(&m_CompletedRequestAsyncList
, &CurrentQH
->LinkedQueueHeads
);
473 CUSBQueue::ProcessAsyncList(
475 OUT PULONG ShouldRingDoorBell
)
479 PQUEUE_HEAD QueueHead
;
480 IUSBRequest
* Request
;
483 // lock completed async list
485 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
490 Entry
= PendingListQueueHead
->LinkedQueueHeads
.Flink
;
492 while(Entry
!= &PendingListQueueHead
->LinkedQueueHeads
)
495 // get queue head structure
497 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
502 PC_ASSERT(QueueHead
->Request
);
505 // get IUSBRequest interface
507 Request
= (IUSBRequest
*)QueueHead
->Request
;
511 // move to next entry
513 Entry
= Entry
->Flink
;
516 // check if queue head is complete
518 if (Request
->IsQueueHeadComplete(QueueHead
))
521 // current queue head is complete
523 QueueHeadCompletion(QueueHead
, Status
);
526 // ring door bell is going to be necessary
528 *ShouldRingDoorBell
= TRUE
;
535 KeReleaseSpinLock(&m_Lock
, OldLevel
);
541 CUSBQueue::InterruptCallback(
543 OUT PULONG ShouldRingDoorBell
)
546 // iterate asynchronous list
548 *ShouldRingDoorBell
= FALSE
;
549 ProcessAsyncList(Status
, ShouldRingDoorBell
);
552 // TODO: implement periodic schedule processing
557 CUSBQueue::QueueHeadCleanup(
558 PQUEUE_HEAD CurrentQH
)
560 IUSBRequest
* Request
;
561 BOOLEAN ShouldReleaseWhenDone
;
566 PC_ASSERT(CurrentQH
->Token
.Bits
.Active
== 0);
567 PC_ASSERT(CurrentQH
->Request
);
572 Request
= (IUSBRequest
*)CurrentQH
->Request
;
575 // let IUSBRequest free the queue head
577 Request
->FreeQueueHead(CurrentQH
);
580 // check if we should release request when done
582 ShouldReleaseWhenDone
= Request
->ShouldReleaseRequestAfterCompletion();
585 // release reference when the request was added
590 // check if the operation was asynchronous
592 if (ShouldReleaseWhenDone
)
595 // release outstanding reference count
601 // request is now released
606 CUSBQueue::CompleteAsyncRequests()
610 PQUEUE_HEAD CurrentQH
;
613 // first acquire request lock
615 KeAcquireSpinLock(&m_Lock
, &OldLevel
);
618 // the list should not be empty
620 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList
));
622 while(!IsListEmpty(&m_CompletedRequestAsyncList
))
625 // remove first entry
627 Entry
= RemoveHeadList(&m_CompletedRequestAsyncList
);
630 // get queue head structure
632 CurrentQH
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
635 // complete request now
637 QueueHeadCleanup(CurrentQH
);
643 KeReleaseSpinLock(&m_Lock
, OldLevel
);
648 PUSBQUEUE
*OutUsbQueue
)
653 // allocate controller
655 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBQueue(0);
659 // failed to allocate
661 return STATUS_INSUFFICIENT_RESOURCES
;
665 // add reference count
672 *OutUsbQueue
= (PUSBQUEUE
)This
;
677 return STATUS_SUCCESS
;