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 IEHCIQueue
17 STDMETHODIMP
QueryInterface( REFIID InterfaceId
, PVOID
* Interface
);
19 STDMETHODIMP_(ULONG
) AddRef()
21 InterlockedIncrement(&m_Ref
);
24 STDMETHODIMP_(ULONG
) Release()
26 InterlockedDecrement(&m_Ref
);
36 // IUSBQueue functions
39 // IEHCIQueue functions
42 // constructor / destructor
43 CUSBQueue(IUnknown
*OuterUnknown
){}
44 virtual ~CUSBQueue(){}
47 LONG m_Ref
; // reference count
48 PKSPIN_LOCK m_Lock
; // list lock
49 PDMA_ADAPTER m_Adapter
; // dma adapter
50 PEHCIHARDWAREDEVICE m_Hardware
; // stores hardware object
51 PQUEUE_HEAD AsyncListQueueHead
; // async queue head
52 LIST_ENTRY m_CompletedRequestAsyncList
; // completed async request list
53 LIST_ENTRY m_PendingRequestAsyncList
; // pending async request list
54 ULONG m_MaxPeriodicListEntries
; // max perdiodic list entries
55 ULONG m_MaxPollingInterval
; // max polling interval
56 PHYSICAL_ADDRESS m_SyncFrameListAddr
; // physical address of sync frame list
57 PULONG m_SyncFrameList
; // virtual address of sync frame list
58 PQUEUE_HEAD
* m_SyncFrameListQueueHeads
; // stores the frame list of queue head
60 // queue head manipulation functions
61 VOID
LinkQueueHead(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
62 VOID
UnlinkQueueHead(PQUEUE_HEAD QueueHead
);
63 VOID
LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, PQUEUE_HEAD NewQueueHead
);
64 PQUEUE_HEAD
UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead
, ULONG Count
);
66 // processes the async list
67 VOID
ProcessAsyncList(IN NTSTATUS Status
, OUT PULONG ShouldRingDoorBell
);
69 // called for each completed queue head
70 VOID
QueueHeadCompletion(PQUEUE_HEAD QueueHead
, NTSTATUS Status
);
72 // called when the completion queue is cleaned up
73 VOID
QueueHeadCleanup(PQUEUE_HEAD QueueHead
);
75 // intializes the sync schedule
76 NTSTATUS
InitializeSyncSchedule(IN PEHCIHARDWAREDEVICE Hardware
, IN PDMAMEMORYMANAGER MemManager
);
79 //=================================================================================================
84 CUSBQueue::QueryInterface(
88 if (IsEqualGUIDAligned(refiid
, IID_IUnknown
))
90 *Output
= PVOID(PUNKNOWN(this));
91 PUNKNOWN(*Output
)->AddRef();
92 return STATUS_SUCCESS
;
95 return STATUS_UNSUCCESSFUL
;
99 CUSBQueue::Initialize(
100 IN PUSBHARDWAREDEVICE Hardware
,
101 IN PDMA_ADAPTER AdapterObject
,
102 IN PDMAMEMORYMANAGER MemManager
,
103 IN OPTIONAL PKSPIN_LOCK Lock
)
105 NTSTATUS Status
= STATUS_SUCCESS
;
107 DPRINT("CUSBQueue::Initialize()\n");
117 // store hardware object
119 m_Hardware
= PEHCIHARDWAREDEVICE(Hardware
);
123 // Get the AsyncQueueHead
125 AsyncListQueueHead
= (PQUEUE_HEAD
)m_Hardware
->GetAsyncListQueueHead();
128 // Initialize the List Head
130 InitializeListHead(&AsyncListQueueHead
->LinkedQueueHeads
);
133 // Initialize completed async list head
135 InitializeListHead(&m_CompletedRequestAsyncList
);
138 // Initialize pending async list head
140 InitializeListHead(&m_PendingRequestAsyncList
);
143 // now initialize sync schedule
145 Status
= InitializeSyncSchedule(m_Hardware
, MemManager
);
152 CUSBQueue::InitializeSyncSchedule(
153 IN PEHCIHARDWAREDEVICE Hardware
,
154 IN PDMAMEMORYMANAGER MemManager
)
156 PHYSICAL_ADDRESS QueueHeadPhysAddr
;
159 PQUEUE_HEAD QueueHead
;
162 // FIXME: check if smaller list sizes are supported
164 m_MaxPeriodicListEntries
= 1024;
167 // use polling scheme of 32ms
169 m_MaxPollingInterval
= 32;
172 // allocate dummy frame list array
174 m_SyncFrameListQueueHeads
= (PQUEUE_HEAD
*)ExAllocatePool(NonPagedPool
, m_MaxPollingInterval
* sizeof(PQUEUE_HEAD
));
175 if (!m_SyncFrameListQueueHeads
)
180 return STATUS_INSUFFICIENT_RESOURCES
;
185 // first allocate a page to hold the queue array
187 Status
= MemManager
->Allocate(m_MaxPeriodicListEntries
* sizeof(PVOID
), (PVOID
*)&m_SyncFrameList
, &m_SyncFrameListAddr
);
188 if (!NT_SUCCESS(Status
))
191 // failed to allocate sync frame list array
193 DPRINT1("Failed to allocate sync frame list\n");
194 ExFreePool(m_SyncFrameListQueueHeads
);
196 return STATUS_INSUFFICIENT_RESOURCES
;
200 // now allocate queue head descriptors for the polling interval
202 for(Index
= 0; Index
< m_MaxPeriodicListEntries
; Index
++)
205 // check if is inside our polling interrupt frequency window
207 if (Index
< m_MaxPollingInterval
)
210 // allocate queue head
212 Status
= MemManager
->Allocate(sizeof(QUEUE_HEAD
), (PVOID
*)&QueueHead
, &QueueHeadPhysAddr
);
215 // initialize queue head
217 QueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
218 QueueHead
->AlternateNextPointer
= TERMINATE_POINTER
;
219 QueueHead
->NextPointer
= TERMINATE_POINTER
;
222 // 1 for non high speed, 0 for high speed device
224 QueueHead
->EndPointCharacteristics
.ControlEndPointFlag
= 0;
225 QueueHead
->EndPointCharacteristics
.HeadOfReclamation
= FALSE
;
226 QueueHead
->EndPointCharacteristics
.MaximumPacketLength
= 64;
229 // Set NakCountReload to max value possible
231 QueueHead
->EndPointCharacteristics
.NakCountReload
= 0xF;
234 // Get the Initial Data Toggle from the QEDT
236 QueueHead
->EndPointCharacteristics
.QEDTDataToggleControl
= FALSE
;
239 // FIXME: check if High Speed Device
241 QueueHead
->EndPointCharacteristics
.EndPointSpeed
= QH_ENDPOINT_HIGHSPEED
;
242 QueueHead
->EndPointCapabilities
.NumberOfTransactionPerFrame
= 0x03;
243 QueueHead
->Token
.DWord
= 0;
244 QueueHead
->Token
.Bits
.InterruptOnComplete
= FALSE
;
245 QueueHead
->PhysicalAddr
= QueueHeadPhysAddr
.LowPart
;
249 // store in queue head array
251 m_SyncFrameListQueueHeads
[Index
] = QueueHead
;
258 QueueHead
= m_SyncFrameListQueueHeads
[m_MaxPeriodicListEntries
% m_MaxPollingInterval
];
264 m_SyncFrameList
[Index
] = (QueueHead
->PhysicalAddr
| 0x2);
268 // now set the sync base
270 Hardware
->SetPeriodicListRegister(m_SyncFrameListAddr
.LowPart
);
273 // sync frame list initialized
275 return STATUS_SUCCESS
;
279 CUSBQueue::AddUSBRequest(
282 PQUEUE_HEAD QueueHead
;
286 PEHCIREQUEST Request
;
292 Request
= PEHCIREQUEST(Req
);
297 Type
= Request
->GetTransferType();
300 // check if supported
304 case USB_ENDPOINT_TYPE_ISOCHRONOUS
:
305 case USB_ENDPOINT_TYPE_INTERRUPT
:
306 /* NOT IMPLEMENTED IN QUEUE */
307 Status
= STATUS_NOT_SUPPORTED
;
309 case USB_ENDPOINT_TYPE_BULK
:
310 case USB_ENDPOINT_TYPE_CONTROL
:
311 Status
= STATUS_SUCCESS
;
316 Status
= STATUS_NOT_SUPPORTED
;
322 if (!NT_SUCCESS(Status
))
325 // request not supported, please try later
330 if (Type
== USB_ENDPOINT_TYPE_BULK
|| Type
== USB_ENDPOINT_TYPE_CONTROL
)
335 Status
= Request
->GetQueueHead(&QueueHead
);
340 if (!NT_SUCCESS(Status
))
343 // failed to get queue head
348 DPRINT("Request %p QueueHead %p inserted into AsyncQueue\n", Request
, QueueHead
);
351 // Add it to the pending list
353 KeAcquireSpinLock(m_Lock
, &OldLevel
);
354 LinkQueueHead(AsyncListQueueHead
, QueueHead
);
355 KeReleaseSpinLock(m_Lock
, OldLevel
);
361 // add extra reference which is released when the request is completed
366 return STATUS_SUCCESS
;
370 CUSBQueue::CreateUSBRequest(
371 IUSBRequest
**OutRequest
)
373 PUSBREQUEST UsbRequest
;
377 Status
= InternalCreateUSBRequest(&UsbRequest
);
379 if (NT_SUCCESS(Status
))
381 *OutRequest
= UsbRequest
;
388 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
391 CUSBQueue::LinkQueueHead(
392 PQUEUE_HEAD HeadQueueHead
,
393 PQUEUE_HEAD NewQueueHead
)
395 PQUEUE_HEAD LastQueueHead
, NextQueueHead
;
397 ASSERT(HeadQueueHead
);
398 ASSERT(NewQueueHead
);
401 // Link the LIST_ENTRYs
403 //ASSERT(IsListEmpty(&HeadQueueHead->LinkedQueueHeads));
404 InsertTailList(&HeadQueueHead
->LinkedQueueHeads
, &NewQueueHead
->LinkedQueueHeads
);
407 // Update HLP for Previous QueueHead, which should be the last in list.
409 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
410 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
411 //ASSERT(LastQueueHead == HeadQueueHead);
412 LastQueueHead
->HorizontalLinkPointer
= (NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
415 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
417 Entry
= NewQueueHead
->LinkedQueueHeads
.Flink
;
418 NextQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
419 //ASSERT(NextQueueHead == HeadQueueHead);
420 NewQueueHead
->HorizontalLinkPointer
= (NextQueueHead
->PhysicalAddr
| QH_TYPE_QH
);
423 // head queue head must be halted
425 //PC_ASSERT(HeadQueueHead->Token.Bits.Halted == TRUE);
429 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
432 CUSBQueue::UnlinkQueueHead(
433 PQUEUE_HEAD QueueHead
)
435 PQUEUE_HEAD PreviousQH
, NextQH
;
439 // sanity check: there must be at least one queue head with halted bit set
441 //PC_ASSERT(QueueHead->Token.Bits.Halted == 0);
446 Entry
= QueueHead
->LinkedQueueHeads
.Blink
;
449 // get queue head structure
451 PreviousQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
456 Entry
= QueueHead
->LinkedQueueHeads
.Flink
;
459 // get queue head structure
461 NextQH
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
466 ASSERT(QueueHead
->HorizontalLinkPointer
== (NextQH
->PhysicalAddr
| QH_TYPE_QH
));
469 // remove queue head from linked list
471 PreviousQH
->HorizontalLinkPointer
= NextQH
->PhysicalAddr
| QH_TYPE_QH
;
474 // remove software link
476 RemoveEntryList(&QueueHead
->LinkedQueueHeads
);
480 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
483 CUSBQueue::LinkQueueHeadChain(
484 PQUEUE_HEAD HeadQueueHead
,
485 PQUEUE_HEAD NewQueueHead
)
487 PQUEUE_HEAD LastQueueHead
;
489 ASSERT(HeadQueueHead
);
490 ASSERT(NewQueueHead
);
493 // Find the last QueueHead in NewQueueHead
495 Entry
= NewQueueHead
->LinkedQueueHeads
.Blink
;
496 ASSERT(Entry
!= NewQueueHead
->LinkedQueueHeads
.Flink
);
497 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
500 // Set the LinkPointer and Flink
502 LastQueueHead
->HorizontalLinkPointer
= HeadQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
503 LastQueueHead
->LinkedQueueHeads
.Flink
= &HeadQueueHead
->LinkedQueueHeads
;
506 // Fine the last QueueHead in HeadQueueHead
508 Entry
= HeadQueueHead
->LinkedQueueHeads
.Blink
;
509 HeadQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
510 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
511 LastQueueHead
->LinkedQueueHeads
.Flink
= &NewQueueHead
->LinkedQueueHeads
;
512 LastQueueHead
->HorizontalLinkPointer
= NewQueueHead
->PhysicalAddr
| QH_TYPE_QH
;
516 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
517 // returns the chain of QueueHeads removed from HeadQueueHead.
520 CUSBQueue::UnlinkQueueHeadChain(
521 PQUEUE_HEAD HeadQueueHead
,
524 PQUEUE_HEAD LastQueueHead
, FirstQueueHead
;
529 // Find the last QueueHead in NewQueueHead
531 Entry
= &HeadQueueHead
->LinkedQueueHeads
;
532 FirstQueueHead
= CONTAINING_RECORD(Entry
->Flink
, QUEUE_HEAD
, LinkedQueueHeads
);
534 for (Index
= 0; Index
< Count
; Index
++)
536 Entry
= Entry
->Flink
;
538 if (Entry
== &HeadQueueHead
->LinkedQueueHeads
)
540 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index
);
546 LastQueueHead
= CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
547 HeadQueueHead
->LinkedQueueHeads
.Flink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
548 if (Count
+ 1 == Index
)
550 HeadQueueHead
->LinkedQueueHeads
.Blink
= &HeadQueueHead
->LinkedQueueHeads
;
553 HeadQueueHead
->LinkedQueueHeads
.Blink
= LastQueueHead
->LinkedQueueHeads
.Flink
;
555 FirstQueueHead
->LinkedQueueHeads
.Blink
= &LastQueueHead
->LinkedQueueHeads
;
556 LastQueueHead
->LinkedQueueHeads
.Flink
= &FirstQueueHead
->LinkedQueueHeads
;
557 LastQueueHead
->HorizontalLinkPointer
= TERMINATE_POINTER
;
558 return FirstQueueHead
;
562 CUSBQueue::QueueHeadCompletion(
563 PQUEUE_HEAD CurrentQH
,
567 // now unlink the queue head
568 // FIXME: implement chained queue heads
569 // no need to acquire locks, as it is called with locks held
575 UnlinkQueueHead(CurrentQH
);
578 // insert into completed list
580 InsertTailList(&m_CompletedRequestAsyncList
, &CurrentQH
->LinkedQueueHeads
);
584 CUSBQueue::ProcessAsyncList(
586 OUT PULONG ShouldRingDoorBell
)
590 PQUEUE_HEAD QueueHead
;
591 PEHCIREQUEST Request
;
592 BOOLEAN IsQueueHeadComplete
;
595 // lock completed async list
597 KeAcquireSpinLock(m_Lock
, &OldLevel
);
602 ASSERT(AsyncListQueueHead
);
603 Entry
= AsyncListQueueHead
->LinkedQueueHeads
.Flink
;
605 while(Entry
!= &AsyncListQueueHead
->LinkedQueueHeads
)
608 // get queue head structure
610 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
616 PC_ASSERT(QueueHead
->Request
);
619 // get IUSBRequest interface
621 Request
= (PEHCIREQUEST
)QueueHead
->Request
;
624 // move to next entry
626 Entry
= Entry
->Flink
;
629 // check if queue head is complete
631 IsQueueHeadComplete
= Request
->IsQueueHeadComplete(QueueHead
);
633 DPRINT("Request %p QueueHead %p Complete %d\n", Request
, QueueHead
, IsQueueHeadComplete
);
636 // check if queue head is complete
638 if (IsQueueHeadComplete
)
641 // current queue head is complete
643 QueueHeadCompletion(QueueHead
, Status
);
646 // ring door bell is going to be necessary
648 *ShouldRingDoorBell
= TRUE
;
655 KeReleaseSpinLock(m_Lock
, OldLevel
);
660 CUSBQueue::InterruptCallback(
662 OUT PULONG ShouldRingDoorBell
)
665 DPRINT("CUSBQueue::InterruptCallback\n");
668 // iterate asynchronous list
670 *ShouldRingDoorBell
= FALSE
;
671 ProcessAsyncList(Status
, ShouldRingDoorBell
);
674 // TODO: implement periodic schedule processing
679 CUSBQueue::QueueHeadCleanup(
680 PQUEUE_HEAD CurrentQH
)
682 PQUEUE_HEAD NewQueueHead
;
683 PEHCIREQUEST Request
;
684 BOOLEAN ShouldReleaseWhenDone
;
685 USBD_STATUS UrbStatus
;
691 PC_ASSERT(CurrentQH
->Token
.Bits
.Active
== 0);
692 PC_ASSERT(CurrentQH
->Request
);
698 Request
= (PEHCIREQUEST
)CurrentQH
->Request
;
706 // check if the queue head was completed with errors
708 if (CurrentQH
->Token
.Bits
.Halted
)
710 if (CurrentQH
->Token
.Bits
.DataBufferError
)
715 UrbStatus
= USBD_STATUS_DATA_BUFFER_ERROR
;
717 else if (CurrentQH
->Token
.Bits
.BabbleDetected
)
722 UrbStatus
= USBD_STATUS_BABBLE_DETECTED
;
729 UrbStatus
= USBD_STATUS_STALL_PID
;
737 UrbStatus
= USBD_STATUS_SUCCESS
;
741 // Check if the transfer was completed and if UrbStatus is ok
743 if ((Request
->IsRequestComplete() == FALSE
) && (UrbStatus
== USBD_STATUS_SUCCESS
))
746 // request is incomplete, get new queue head
748 if (Request
->GetQueueHead(&NewQueueHead
) == STATUS_SUCCESS
)
751 // let IUSBRequest free the queue head
753 Request
->FreeQueueHead(CurrentQH
);
756 // first acquire request lock
758 KeAcquireSpinLock(m_Lock
, &OldLevel
);
761 // add to pending list
763 InsertTailList(&m_PendingRequestAsyncList
, &NewQueueHead
->LinkedQueueHeads
);
766 // release queue head
768 KeReleaseSpinLock(m_Lock
, OldLevel
);
775 DPRINT1("Unable to create a new QueueHead\n");
779 // Else there was a problem
780 // FIXME: Find better return
781 UrbStatus
= USBD_STATUS_INSUFFICIENT_RESOURCES
;
784 if (UrbStatus
!= USBD_STATUS_SUCCESS
)
786 DPRINT1("URB failed with status 0x%x\n", UrbStatus
);
791 // notify request that a transfer has completed
793 Request
->CompletionCallback(UrbStatus
!= USBD_STATUS_SUCCESS
? STATUS_UNSUCCESSFUL
: STATUS_SUCCESS
,
798 // let IUSBRequest free the queue head
800 Request
->FreeQueueHead(CurrentQH
);
803 // check if we should release request when done
805 ShouldReleaseWhenDone
= Request
->ShouldReleaseRequestAfterCompletion();
808 // release reference when the request was added
813 // check if the operation was asynchronous
815 if (ShouldReleaseWhenDone
)
818 // release outstanding reference count
824 // request is now released
829 CUSBQueue::CompleteAsyncRequests()
833 PQUEUE_HEAD CurrentQH
;
835 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
838 // first acquire request lock
840 KeAcquireSpinLock(m_Lock
, &OldLevel
);
843 // the list should not be empty
845 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList
));
847 while(!IsListEmpty(&m_CompletedRequestAsyncList
))
850 // remove first entry
852 Entry
= RemoveHeadList(&m_CompletedRequestAsyncList
);
855 // get queue head structure
857 CurrentQH
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
862 KeReleaseSpinLock(m_Lock
, OldLevel
);
865 // complete request now
867 QueueHeadCleanup(CurrentQH
);
870 // first acquire request lock
872 KeAcquireSpinLock(m_Lock
, &OldLevel
);
876 // is there a pending async entry
878 if (!IsListEmpty(&m_PendingRequestAsyncList
))
881 // remove first entry
883 Entry
= RemoveHeadList(&m_PendingRequestAsyncList
);
886 // get queue head structure
888 CurrentQH
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
891 // Add it to the AsyncList list
893 LinkQueueHead(AsyncListQueueHead
, CurrentQH
);
899 KeReleaseSpinLock(m_Lock
, OldLevel
);
903 CUSBQueue::AbortDevicePipe(
904 IN UCHAR DeviceAddress
,
905 IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor
)
909 PQUEUE_HEAD QueueHead
;
913 // lock completed async list
915 KeAcquireSpinLock(m_Lock
, &OldLevel
);
917 DPRINT1("AbortDevicePipe DeviceAddress %x EndpointDescriptor %p Addr %x\n", DeviceAddress
, EndpointDescriptor
, EndpointDescriptor
->bEndpointAddress
);
922 InitializeListHead(&ListHead
);
928 ASSERT(AsyncListQueueHead
);
929 Entry
= AsyncListQueueHead
->LinkedQueueHeads
.Flink
;
931 while(Entry
!= &AsyncListQueueHead
->LinkedQueueHeads
)
934 // get queue head structure
936 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
940 // move to next entry
942 Entry
= Entry
->Flink
;
944 if (QueueHead
->EndPointCharacteristics
.DeviceAddress
== DeviceAddress
&&
945 QueueHead
->EndPointCharacteristics
.EndPointNumber
== (EndpointDescriptor
->bEndpointAddress
& 0xF) && QueueHead
->Token
.Bits
.Halted
)
950 UnlinkQueueHead(QueueHead
);
955 InsertTailList(&ListHead
, &QueueHead
->LinkedQueueHeads
);
962 KeReleaseSpinLock(m_Lock
, OldLevel
);
964 while(!IsListEmpty(&ListHead
))
969 Entry
= RemoveHeadList(&ListHead
);
972 // get queue head structure
974 QueueHead
= (PQUEUE_HEAD
)CONTAINING_RECORD(Entry
, QUEUE_HEAD
, LinkedQueueHeads
);
978 // cleanup queue head
980 QueueHeadCleanup(QueueHead
);
982 return STATUS_SUCCESS
;
988 PUSBQUEUE
*OutUsbQueue
)
993 // allocate controller
995 This
= new(NonPagedPool
, TAG_USBEHCI
) CUSBQueue(0);
999 // failed to allocate
1001 return STATUS_INSUFFICIENT_RESOURCES
;
1005 // add reference count
1012 *OutUsbQueue
= (PUSBQUEUE
)This
;
1017 return STATUS_SUCCESS
;