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)
16 class CUSBQueue
: public IEHCIQueue
19 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
21 STDMETHODIMP_(ULONG
) AddRef()
23 InterlockedIncrement(&m_Ref
);
26 STDMETHODIMP_(ULONG
) Release()
28 InterlockedDecrement(&m_Ref
);
38 // IUSBQueue functions
41 // IEHCIQueue functions
44 // constructor / destructor
45 CUSBQueue(IUnknown
*OuterUnknown
){}
46 virtual ~CUSBQueue(){}
49 LONG m_Ref
; // reference count
50 PKSPIN_LOCK m_Lock
; // list lock
51 PDMA_ADAPTER m_Adapter
; // dma adapter
52 PEHCIHARDWAREDEVICE m_Hardware
; // stores hardware object
53 PQUEUE_HEAD AsyncListQueueHead
; // async queue head
54 LIST_ENTRY m_CompletedRequestAsyncList
; // completed async request list
55 LIST_ENTRY m_PendingRequestAsyncList
; // pending async request list
56 ULONG m_MaxPeriodicListEntries
; // max perdiodic list entries
57 ULONG m_MaxPollingInterval
; // max polling interval
58 PHYSICAL_ADDRESS m_SyncFrameListAddr
; // physical address of sync frame list
59 PULONG m_SyncFrameList
; // virtual address of sync frame list
61 // queue head manipulation functions
62 VOID
LinkQueueHead(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
63 VOID
UnlinkQueueHead(PQUEUE_HEAD QueueHead
);
64 VOID
LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
65 PQUEUE_HEAD
UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, ULONG Count
);
67 // processes the async list
68 VOID
ProcessAsyncList(IN NTSTATUS Status
, OUT PULONG ShouldRingDoorBell
);
70 // processes the async list
71 VOID
ProcessPeriodicSchedule(IN NTSTATUS Status
, OUT PULONG ShouldRingDoorBell
);
73 // called for each completed queue head
74 VOID
QueueHeadCompletion(PQUEUE_HEAD QueueHead
, NTSTATUS Status
);
76 // called for each completed queue head
77 VOID
QueueHeadInterruptCompletion(PQUEUE_HEAD QueueHead
, NTSTATUS Status
);
79 // called when the completion queue is cleaned up
80 VOID
QueueHeadCleanup(PQUEUE_HEAD QueueHead
);
82 // intializes the sync schedule
83 NTSTATUS
InitializeSyncSchedule(IN PEHCIHARDWAREDEVICE Hardware
, IN PDMAMEMORYMANAGER MemManager
);
85 // links interrupt queue head
86 VOID
LinkInterruptQueueHead(PQUEUE_HEAD QueueHead
);
89 UCHAR
GetIntervalIndex(UCHAR Interval
);
92 // interrupt queue heads
93 PQUEUE_HEAD m_InterruptQueueHeads
[EHCI_INTERRUPT_ENTRIES_COUNT
];
95 // contains the periodic queue heads
96 LIST_ENTRY m_PeriodicQueueHeads
;
99 //=================================================================================================
104 CUSBQueue::QueryInterface(
108 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
110 *Output
= PVOID(PUNKNOWN(this));
111 PUNKNOWN(*Output
)->AddRef();
112 return STATUS_SUCCESS
;
115 return STATUS_UNSUCCESSFUL
;
120 CUSBQueue::Initialize(
121 IN PUSBHARDWAREDEVICE Hardware
,
122 IN PDMA_ADAPTER AdapterObject
,
123 IN PDMAMEMORYMANAGER MemManager
,
124 IN OPTIONAL PKSPIN_LOCK Lock
)
126 NTSTATUS Status
= STATUS_SUCCESS
;
128 DPRINT("CUSBQueue::Initialize()\n");
138 // store hardware object
140 m_Hardware
= PEHCIHARDWAREDEVICE(Hardware
);
144 // Get the AsyncQueueHead
146 AsyncListQueueHead
= (PQUEUE_HEAD
)m_Hardware
->GetAsyncListQueueHead();
149 // Initialize the List Head
151 InitializeListHead(&AsyncListQueueHead
->LinkedQueueHeads
);
154 // Initialize completed async list head
156 InitializeListHead(&m_CompletedRequestAsyncList
);
159 // Initialize pending async list head
161 InitializeListHead(&m_PendingRequestAsyncList
);
164 // initialize periodic queue heads
166 InitializeListHead(&m_PeriodicQueueHeads
);
169 // now initialize sync schedule
171 Status
= InitializeSyncSchedule(m_Hardware
, MemManager
);
178 CUSBQueue::InitializeSyncSchedule(
179 IN PEHCIHARDWAREDEVICE Hardware
,
180 IN PDMAMEMORYMANAGER MemManager
)
182 PHYSICAL_ADDRESS QueueHeadPhysAddr
;
184 ULONG Index
, Interval
, IntervalIndex
;
185 PQUEUE_HEAD QueueHead
;
188 // FIXME: check if smaller list sizes are supported
190 m_MaxPeriodicListEntries
= 1024;
193 // use polling scheme of 512ms
195 m_MaxPollingInterval
= 512;
198 // first allocate a page to hold the queue array
200 Status
= MemManager
->Allocate(m_MaxPeriodicListEntries
* sizeof(PVOID
), (PVOID
*)&m_SyncFrameList
, &m_SyncFrameListAddr
);
201 if (!NT_SUCCESS(Status
))
204 // failed to allocate sync frame list array
206 DPRINT1("Failed to allocate sync frame list\n");
207 return STATUS_INSUFFICIENT_RESOURCES
;
210 for(Index
= 0; Index
< EHCI_INTERRUPT_ENTRIES_COUNT
; Index
++)
213 // allocate queue head
215 Status
= MemManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&QueueHead
, &QueueHeadPhysAddr
);
216 if (!NT_SUCCESS(Status
))
219 // failed to create queue head
221 DPRINT1("Failed to create queue head\n");
226 // initialize queue head
228 QueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
229 QueueHead
->AlternateNextPointer
= TERMINATE_POINTER
;
230 QueueHead
->NextPointer
= TERMINATE_POINTER
;
231 QueueHead
->EndPointCharacteristics
.MaximumPacketLength
= 64;
232 QueueHead
->EndPointCharacteristics
.NakCountReload
= 0x3;
233 QueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
234 QueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x01;
235 QueueHead
->PhysicalAddr
= QueueHeadPhysAddr
.LowPart
;
236 QueueHead
->Token
.Bits
.Halted
= TRUE
; //FIXME
237 m_InterruptQueueHeads
[Index
]= QueueHead
;
241 // link all to the first queue head
242 QueueHead
->HorizontalLinkPointer
= m_InterruptQueueHeads
[0]->PhysicalAddr
| QH_TYPE_QH
;
243 QueueHead
->NextQueueHead
= m_InterruptQueueHeads
[0];
248 // build interrupt tree
250 Interval
= EHCI_FRAMELIST_ENTRIES_COUNT
;
251 IntervalIndex
= EHCI_INTERRUPT_ENTRIES_COUNT
- 1;
254 for (Index
= Interval
/ 2; Index
< EHCI_FRAMELIST_ENTRIES_COUNT
; Index
+= Interval
)
256 DPRINT("Index %lu IntervalIndex %lu\n", Index
, IntervalIndex
);
257 m_SyncFrameList
[Index
] = m_InterruptQueueHeads
[IntervalIndex
]->PhysicalAddr
| QH_TYPE_QH
;
264 // now set the sync base
266 Hardware
->SetPeriodicListRegister(m_SyncFrameListAddr
.LowPart
);
269 // sync frame list initialized
271 return STATUS_SUCCESS
;
276 CUSBQueue::AddUSBRequest(
279 PQUEUE_HEAD QueueHead
;
283 PEHCIREQUEST Request
;
289 Request
= PEHCIREQUEST(Req
);
292 Type
= Request
->GetTransferType();
294 // check if supported
297 case USB_ENDPOINT_TYPE_ISOCHRONOUS
:
298 /* NOT IMPLEMENTED IN QUEUE */
299 Status
= STATUS_NOT_SUPPORTED
;
301 case USB_ENDPOINT_TYPE_INTERRUPT
:
302 case USB_ENDPOINT_TYPE_BULK
:
303 case USB_ENDPOINT_TYPE_CONTROL
:
304 Status
= STATUS_SUCCESS
;
309 Status
= STATUS_NOT_SUPPORTED
;
313 if (!NT_SUCCESS(Status
))
315 // request not supported, please try later
320 Status
= Request
->GetQueueHead(&QueueHead
);
323 if (!NT_SUCCESS(Status
))
325 // failed to get queue head
330 KeAcquireSpinLock(m_Lock
, &OldLevel
);
332 if (Type
== USB_ENDPOINT_TYPE_BULK
|| Type
== USB_ENDPOINT_TYPE_CONTROL
)
335 LinkQueueHead(AsyncListQueueHead
, QueueHead
);
337 else if (Type
== USB_ENDPOINT_TYPE_INTERRUPT
)
340 LinkInterruptQueueHead(QueueHead
);
344 KeReleaseSpinLock(m_Lock
, OldLevel
);
347 // add extra reference which is released when the request is completed
351 return STATUS_SUCCESS
;
356 CUSBQueue::CreateUSBRequest(
357 IUSBRequest
**OutRequest
)
359 PUSBREQUEST UsbRequest
;
363 Status
= InternalCreateUSBRequest(&UsbRequest
);
365 if (NT_SUCCESS(Status
))
367 *OutRequest
= UsbRequest
;
374 CUSBQueue::GetIntervalIndex(
381 else if (Interval
== 2)
383 else if (Interval
<= 4)
385 else if (Interval
<= 8)
387 else if (Interval
<= 16)
389 else if (Interval
<= 32)
391 else if (Interval
<= 64)
393 else if (Interval
<= 128)
395 else if (Interval
<= 256)
400 return IntervalIndex
;
404 CUSBQueue::LinkInterruptQueueHead(
405 PQUEUE_HEAD QueueHead
)
407 PEHCIREQUEST Request
;
408 UCHAR Interval
, IntervalIndex
;
409 USB_DEVICE_SPEED DeviceSpeed
;
410 PQUEUE_HEAD InterruptQueueHead
;
413 Request
= PEHCIREQUEST(QueueHead
->Request
);
417 Interval
= Request
->GetInterval();
420 DeviceSpeed
= Request
->GetSpeed();
421 if (DeviceSpeed
== UsbHighSpeed
)
423 // interrupt queue head can be scheduled on each possible micro frame
424 QueueHead
->EndPointCapabilities
.InterruptScheduleMask
= 0xFF;
428 // As we do not yet support FSTNs to correctly reference low/full
429 // speed interrupt transfers, we simply put them into the 1 interval
430 // queue. This way we ensure that we reach them on every micro frame
431 // and can do the corresponding start/complete split transactions.
432 // ToDo: use FSTNs to correctly link non high speed interrupt transfers
435 // For now we also force start splits to be in micro frame 0 and
436 // complete splits to be in micro frame 2, 3 and 4.
437 QueueHead
->EndPointCapabilities
.InterruptScheduleMask
= 0x01;
438 QueueHead
->EndPointCapabilities
.SplitCompletionMask
= 0x1C;
441 // sanitize interrupt interval
442 Interval
= max(1, Interval
);
444 // get interval index
445 IntervalIndex
= GetIntervalIndex(Interval
);
448 // get interrupt queue head
449 InterruptQueueHead
= m_InterruptQueueHeads
[IntervalIndex
];
452 QueueHead
->HorizontalLinkPointer
= InterruptQueueHead
->HorizontalLinkPointer
;
453 QueueHead
->NextQueueHead
= InterruptQueueHead
->NextQueueHead
;
455 InterruptQueueHead
->HorizontalLinkPointer
= QueueHead
->PhysicalAddr
| QH_TYPE_QH
;
456 InterruptQueueHead
->NextQueueHead
= QueueHead
;
458 // store in periodic list
459 InsertTailList(&m_PeriodicQueueHeads
, &QueueHead
->LinkedQueueHeads
);
463 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
466 CUSBQueue::LinkQueueHead(
467 PQUEUE_HEAD HeadQueueHead
,
468 PQUEUE_HEAD NewQueueHead
)
470 PQUEUE_HEAD LastQueueHead
, NextQueueHead
;
472 ASSERT(HeadQueueHead
);
473 ASSERT(NewQueueHead
);
476 // Link the LIST_ENTRYs
478 //ASSERT(IsListEmpty(&HeadQueueHead->LinkedQueueHeads));
479 InsertTailList(&HeadQueueHead
->LinkedQueueHeads
, &NewQueueHead
->LinkedQueueHeads
);
482 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
484 Entry
= NewQueueHead
->LinkedQueueHeads
.Flink
;
485 NextQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
486 //ASSERT(NextQueueHead == HeadQueueHead);
487 NewQueueHead
->HorizontalLinkPointer
= (NextQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
492 // Update HLP for Previous QueueHead, which should be the last in list.
494 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
495 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
496 //ASSERT(LastQueueHead == HeadQueueHead);
497 LastQueueHead
->HorizontalLinkPointer
= (NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
500 // head queue head must be halted
502 //PC_ASSERT(HeadQueueHead->Token.Bits.Halted == TRUE);
506 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
509 CUSBQueue::UnlinkQueueHead(
510 PQUEUE_HEAD QueueHead
)
512 PQUEUE_HEAD PreviousQH
, NextQH
;
516 // sanity check: there must be at least one queue head with halted bit set
518 //PC_ASSERT(QueueHead->Token.Bits.Halted == 0);
523 Entry
= QueueHead
->LinkedQueueHeads
.Blink
;
526 // get queue head structure
528 PreviousQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
533 Entry
= QueueHead
->LinkedQueueHeads
.Flink
;
536 // get queue head structure
538 NextQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
543 ASSERT(QueueHead
->HorizontalLinkPointer
== (NextQH
->PhysicalAddr
| QH_TYPE_QH
));
546 // remove queue head from linked list
548 PreviousQH
->HorizontalLinkPointer
= NextQH
->PhysicalAddr
| QH_TYPE_QH
;
551 // remove software link
553 RemoveEntryList(&QueueHead
->LinkedQueueHeads
);
557 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
560 CUSBQueue::LinkQueueHeadChain(
561 PQUEUE_HEAD HeadQueueHead
,
562 PQUEUE_HEAD NewQueueHead
)
564 PQUEUE_HEAD LastQueueHead
;
566 ASSERT(HeadQueueHead
);
567 ASSERT(NewQueueHead
);
570 // Find the last QueueHead in NewQueueHead
572 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
573 ASSERT(Entry
!= NewQueueHead
->LinkedQueueHeads
.Flink
);
574 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
577 // Set the LinkPointer and Flink
579 LastQueueHead
->HorizontalLinkPointer
= HeadQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
580 LastQueueHead
->LinkedQueueHeads
.Flink
= &HeadQueueHead
->LinkedQueueHeads
;
583 // Fine the last QueueHead in HeadQueueHead
585 Entry
= HeadQueueHead
->LinkedQueueHeads
.Blink
;
586 HeadQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
587 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
588 LastQueueHead
->LinkedQueueHeads
.Flink
= &NewQueueHead
->LinkedQueueHeads
;
589 LastQueueHead
->HorizontalLinkPointer
= NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
593 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
594 // returns the chain of QueueHeads removed from HeadQueueHead.
597 CUSBQueue::UnlinkQueueHeadChain(
598 PQUEUE_HEAD HeadQueueHead
,
601 PQUEUE_HEAD LastQueueHead
, FirstQueueHead
;
606 // Find the last QueueHead in NewQueueHead
608 Entry
= &HeadQueueHead
->LinkedQueueHeads
;
609 FirstQueueHead
= CONTAINING_RECORD(Entry
->Flink
, QUEUE_HEAD
, LinkedQueueHeads
);
611 for (Index
= 0; Index
< Count
; Index
++)
613 Entry
= Entry
->Flink
;
615 if (Entry
== &HeadQueueHead
->LinkedQueueHeads
)
617 DPRINT1("Warning; Only %lu QueueHeads in HeadQueueHead\n", Index
);
623 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
624 HeadQueueHead
->LinkedQueueHeads
.Flink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
625 if (Count
+ 1 == Index
)
627 HeadQueueHead
->LinkedQueueHeads
.Blink
= &HeadQueueHead
->LinkedQueueHeads
;
630 HeadQueueHead
->LinkedQueueHeads
.Blink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
632 FirstQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
633 LastQueueHead
->LinkedQueueHeads
.Flink
= &FirstQueueHead
->LinkedQueueHeads
;
634 LastQueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
635 return FirstQueueHead
;
639 CUSBQueue::QueueHeadInterruptCompletion(
640 PQUEUE_HEAD QueueHead
,
643 PEHCIREQUEST Request
;
644 UCHAR Interval
, IntervalIndex
;
645 PQUEUE_HEAD InterruptQueueHead
, LastQueueHead
= NULL
;
651 PC_ASSERT(QueueHead
->Request
);
654 // get IUSBRequest interface
656 Request
= (PEHCIREQUEST
)QueueHead
->Request
;
659 Interval
= Request
->GetInterval();
662 Interval
= max(1, Interval
);
664 // get interval index
665 IntervalIndex
= GetIntervalIndex(Interval
);
667 // get interrupt queue head from index
668 InterruptQueueHead
= m_InterruptQueueHeads
[IntervalIndex
];
670 while(InterruptQueueHead
!= NULL
)
672 if (InterruptQueueHead
== QueueHead
)
675 // move to next queue head
676 LastQueueHead
= InterruptQueueHead
;
677 InterruptQueueHead
= (PQUEUE_HEAD
)InterruptQueueHead
->NextQueueHead
;
680 if (InterruptQueueHead
!= QueueHead
)
682 // queue head not in list
687 // now unlink queue head
688 LastQueueHead
->HorizontalLinkPointer
= QueueHead
->HorizontalLinkPointer
;
689 LastQueueHead
->NextQueueHead
= QueueHead
->NextQueueHead
;
691 DPRINT1("Periodic QueueHead %p Addr %x unlinked\n", QueueHead
, QueueHead
->PhysicalAddr
);
693 // insert into completed list
694 InsertTailList(&m_CompletedRequestAsyncList
, &QueueHead
->LinkedQueueHeads
);
700 CUSBQueue::QueueHeadCompletion(
701 PQUEUE_HEAD CurrentQH
,
705 // now unlink the queue head
706 // FIXME: implement chained queue heads
707 // no need to acquire locks, as it is called with locks held
713 UnlinkQueueHead(CurrentQH
);
716 // insert into completed list
718 InsertTailList(&m_CompletedRequestAsyncList
, &CurrentQH
->LinkedQueueHeads
);
723 CUSBQueue::ProcessPeriodicSchedule(
725 OUT PULONG ShouldRingDoorBell
)
729 PQUEUE_HEAD QueueHead
;
730 PEHCIREQUEST Request
;
731 BOOLEAN IsQueueHeadComplete
;
734 // lock completed async list
736 KeAcquireSpinLock(m_Lock
, &OldLevel
);
741 ASSERT(AsyncListQueueHead
);
742 Entry
= m_PeriodicQueueHeads
.Flink
;
744 while(Entry
!= &m_PeriodicQueueHeads
)
747 // get queue head structure
749 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
755 PC_ASSERT(QueueHead
->Request
);
758 // get IUSBRequest interface
760 Request
= (PEHCIREQUEST
)QueueHead
->Request
;
763 // move to next entry
765 Entry
= Entry
->Flink
;
768 // check if queue head is complete
770 IsQueueHeadComplete
= Request
->IsQueueHeadComplete(QueueHead
);
772 DPRINT("Request %p QueueHead %p Complete %c\n", Request
, QueueHead
, IsQueueHeadComplete
);
775 // check if queue head is complete
777 if (IsQueueHeadComplete
)
780 // current queue head is complete
782 QueueHeadInterruptCompletion(QueueHead
, Status
);
785 // ring door bell is going to be necessary
787 *ShouldRingDoorBell
= TRUE
;
794 KeReleaseSpinLock(m_Lock
, OldLevel
);
799 CUSBQueue::ProcessAsyncList(
801 OUT PULONG ShouldRingDoorBell
)
805 PQUEUE_HEAD QueueHead
;
806 PEHCIREQUEST Request
;
807 BOOLEAN IsQueueHeadComplete
;
810 // lock completed async list
812 KeAcquireSpinLock(m_Lock
, &OldLevel
);
817 ASSERT(AsyncListQueueHead
);
818 Entry
= AsyncListQueueHead
->LinkedQueueHeads
.Flink
;
820 while(Entry
!= &AsyncListQueueHead
->LinkedQueueHeads
)
823 // get queue head structure
825 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
831 PC_ASSERT(QueueHead
->Request
);
834 // get IUSBRequest interface
836 Request
= (PEHCIREQUEST
)QueueHead
->Request
;
839 // move to next entry
841 Entry
= Entry
->Flink
;
844 // check if queue head is complete
846 IsQueueHeadComplete
= Request
->IsQueueHeadComplete(QueueHead
);
848 DPRINT("Request %p QueueHead %p Complete %c\n", Request
, QueueHead
, IsQueueHeadComplete
);
851 // check if queue head is complete
853 if (IsQueueHeadComplete
)
856 // current queue head is complete
858 QueueHeadCompletion(QueueHead
, Status
);
861 // ring door bell is going to be necessary
863 *ShouldRingDoorBell
= TRUE
;
870 KeReleaseSpinLock(m_Lock
, OldLevel
);
876 CUSBQueue::InterruptCallback(
878 OUT PULONG ShouldRingDoorBell
)
880 DPRINT("CUSBQueue::InterruptCallback\n");
883 // process periodic schedule
885 ProcessPeriodicSchedule(Status
, ShouldRingDoorBell
);
888 // iterate asynchronous list
890 *ShouldRingDoorBell
= FALSE
;
891 ProcessAsyncList(Status
, ShouldRingDoorBell
);
895 CUSBQueue::QueueHeadCleanup(
896 PQUEUE_HEAD CurrentQH
)
898 PQUEUE_HEAD NewQueueHead
;
899 PEHCIREQUEST Request
;
900 BOOLEAN ShouldReleaseWhenDone
;
901 USBD_STATUS UrbStatus
;
907 PC_ASSERT(CurrentQH
->Token
.Bits
.Active
== 0);
908 PC_ASSERT(CurrentQH
->Request
);
914 Request
= (PEHCIREQUEST
)CurrentQH
->Request
;
922 // check if the queue head was completed with errors
924 if (CurrentQH
->Token
.Bits
.Halted
)
926 if (CurrentQH
->Token
.Bits
.DataBufferError
)
931 UrbStatus
= USBD_STATUS_DATA_BUFFER_ERROR
;
933 else if (CurrentQH
->Token
.Bits
.BabbleDetected
)
938 UrbStatus
= USBD_STATUS_BABBLE_DETECTED
;
945 UrbStatus
= USBD_STATUS_STALL_PID
;
953 UrbStatus
= USBD_STATUS_SUCCESS
;
957 // Check if the transfer was completed and if UrbStatus is ok
959 if ((Request
->IsRequestComplete() == FALSE
) && (UrbStatus
== USBD_STATUS_SUCCESS
))
962 // request is incomplete, get new queue head
964 if (Request
->GetQueueHead(&NewQueueHead
) == STATUS_SUCCESS
)
967 // let IUSBRequest free the queue head
969 Request
->FreeQueueHead(CurrentQH
);
972 // first acquire request lock
974 KeAcquireSpinLock(m_Lock
, &OldLevel
);
977 // add to pending list
979 InsertTailList(&m_PendingRequestAsyncList
, &NewQueueHead
->LinkedQueueHeads
);
982 // release queue head
984 KeReleaseSpinLock(m_Lock
, OldLevel
);
991 DPRINT1("Unable to create a new QueueHead\n");
995 // Else there was a problem
996 // FIXME: Find better return
997 UrbStatus
= USBD_STATUS_INSUFFICIENT_RESOURCES
;
1000 if (UrbStatus
!= USBD_STATUS_SUCCESS
)
1002 DPRINT1("URB failed with status 0x%x\n", UrbStatus
);
1007 // notify request that a transfer has completed
1009 Request
->CompletionCallback(UrbStatus
!= USBD_STATUS_SUCCESS
? STATUS_UNSUCCESSFUL
: STATUS_SUCCESS
,
1014 // let IUSBRequest free the queue head
1016 Request
->FreeQueueHead(CurrentQH
);
1019 // check if we should release request when done
1021 ShouldReleaseWhenDone
= Request
->ShouldReleaseRequestAfterCompletion();
1024 // release reference when the request was added
1029 // check if the operation was asynchronous
1031 if (ShouldReleaseWhenDone
)
1034 // release outstanding reference count
1040 // request is now released
1046 CUSBQueue::CompleteAsyncRequests()
1050 PQUEUE_HEAD CurrentQH
;
1052 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
1055 // first acquire request lock
1057 KeAcquireSpinLock(m_Lock
, &OldLevel
);
1060 // the list should not be empty
1062 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList
));
1064 while(!IsListEmpty(&m_CompletedRequestAsyncList
))
1067 // remove first entry
1069 Entry
= RemoveHeadList(&m_CompletedRequestAsyncList
);
1072 // get queue head structure
1074 CurrentQH
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
1079 KeReleaseSpinLock(m_Lock
, OldLevel
);
1082 // complete request now
1084 QueueHeadCleanup(CurrentQH
);
1087 // first acquire request lock
1089 KeAcquireSpinLock(m_Lock
, &OldLevel
);
1093 // is there a pending async entry
1095 if (!IsListEmpty(&m_PendingRequestAsyncList
))
1098 // remove first entry
1100 Entry
= RemoveHeadList(&m_PendingRequestAsyncList
);
1103 // get queue head structure
1105 CurrentQH
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
1108 // Add it to the AsyncList list
1110 LinkQueueHead(AsyncListQueueHead
, CurrentQH
);
1116 KeReleaseSpinLock(m_Lock
, OldLevel
);
1121 CUSBQueue::AbortDevicePipe(
1122 IN UCHAR DeviceAddress
,
1123 IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
1127 PQUEUE_HEAD QueueHead
;
1128 LIST_ENTRY ListHead
;
1131 // lock completed async list
1133 KeAcquireSpinLock(m_Lock
, &OldLevel
);
1135 DPRINT1("AbortDevicePipe DeviceAddress %x EndpointDescriptor %p Addr %x\n", DeviceAddress
, EndpointDescriptor
, EndpointDescriptor
->bEndpointAddress
);
1140 InitializeListHead(&ListHead
);
1146 ASSERT(AsyncListQueueHead
);
1147 Entry
= AsyncListQueueHead
->LinkedQueueHeads
.Flink
;
1149 while(Entry
!= &AsyncListQueueHead
->LinkedQueueHeads
)
1152 // get queue head structure
1154 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
1158 // move to next entry
1160 Entry
= Entry
->Flink
;
1162 if (QueueHead
->EndPointCharacteristics
.DeviceAddress
== DeviceAddress
&&
1163 QueueHead
->EndPointCharacteristics
.EndPointNumber
== (EndpointDescriptor
->bEndpointAddress
& 0xF) && QueueHead
->Token
.Bits
.Halted
)
1166 // unlink queue head
1168 UnlinkQueueHead(QueueHead
);
1173 InsertTailList(&ListHead
, &QueueHead
->LinkedQueueHeads
);
1180 KeReleaseSpinLock(m_Lock
, OldLevel
);
1182 while(!IsListEmpty(&ListHead
))
1187 Entry
= RemoveHeadList(&ListHead
);
1190 // get queue head structure
1192 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
1196 // cleanup queue head
1198 QueueHeadCleanup(QueueHead
);
1200 return STATUS_SUCCESS
;
1207 PUSBQUEUE
*OutUsbQueue
)
1212 // allocate controller
1214 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBQueue(0);
1218 // failed to allocate
1220 return STATUS_INSUFFICIENT_RESOURCES
;
1224 // add reference count
1231 *OutUsbQueue
= (PUSBQUEUE
)This
;
1236 return STATUS_SUCCESS
;