[USBEHCI]
[reactos.git] / reactos / drivers / usb / usbehci / usb_queue.cpp
1 /*
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.
6 * PROGRAMMERS:
7 * Michael Martin (michael.martin@reactos.org)
8 * Johannes Anderwald (johannes.anderwald@reactos.org)
9 */
10
11 #include "usbehci.h"
12 #include "hardware.h"
13
14 class CUSBQueue : public IEHCIQueue
15 {
16 public:
17 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
18
19 STDMETHODIMP_(ULONG) AddRef()
20 {
21 InterlockedIncrement(&m_Ref);
22 return m_Ref;
23 }
24 STDMETHODIMP_(ULONG) Release()
25 {
26 InterlockedDecrement(&m_Ref);
27
28 if (!m_Ref)
29 {
30 delete this;
31 return 0;
32 }
33 return m_Ref;
34 }
35
36 // IUSBQueue functions
37 IMP_IUSBQUEUE
38
39 // IEHCIQueue functions
40 IMP_IEHCIQUEUE
41
42 // constructor / destructor
43 CUSBQueue(IUnknown *OuterUnknown){}
44 virtual ~CUSBQueue(){}
45
46 protected:
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
59
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);
65
66 // processes the async list
67 VOID ProcessAsyncList(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell);
68
69 // called for each completed queue head
70 VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
71
72 // called when the completion queue is cleaned up
73 VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead);
74
75 // intializes the sync schedule
76 NTSTATUS InitializeSyncSchedule(IN PEHCIHARDWAREDEVICE Hardware, IN PDMAMEMORYMANAGER MemManager);
77 };
78
79 //=================================================================================================
80 // COM
81 //
82 NTSTATUS
83 STDMETHODCALLTYPE
84 CUSBQueue::QueryInterface(
85 IN REFIID refiid,
86 OUT PVOID* Output)
87 {
88 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
89 {
90 *Output = PVOID(PUNKNOWN(this));
91 PUNKNOWN(*Output)->AddRef();
92 return STATUS_SUCCESS;
93 }
94
95 return STATUS_UNSUCCESSFUL;
96 }
97
98 NTSTATUS
99 CUSBQueue::Initialize(
100 IN PUSBHARDWAREDEVICE Hardware,
101 IN PDMA_ADAPTER AdapterObject,
102 IN PDMAMEMORYMANAGER MemManager,
103 IN OPTIONAL PKSPIN_LOCK Lock)
104 {
105 NTSTATUS Status = STATUS_SUCCESS;
106
107 DPRINT("CUSBQueue::Initialize()\n");
108
109 ASSERT(Hardware);
110
111 //
112 // store device lock
113 //
114 m_Lock = Lock;
115
116 //
117 // store hardware object
118 //
119 m_Hardware = PEHCIHARDWAREDEVICE(Hardware);
120
121
122 //
123 // Get the AsyncQueueHead
124 //
125 AsyncListQueueHead = (PQUEUE_HEAD)m_Hardware->GetAsyncListQueueHead();
126
127 //
128 // Initialize the List Head
129 //
130 InitializeListHead(&AsyncListQueueHead->LinkedQueueHeads);
131
132 //
133 // Initialize completed async list head
134 //
135 InitializeListHead(&m_CompletedRequestAsyncList);
136
137 //
138 // Initialize pending async list head
139 //
140 InitializeListHead(&m_PendingRequestAsyncList);
141
142 //
143 // now initialize sync schedule
144 //
145 Status = InitializeSyncSchedule(m_Hardware, MemManager);
146
147
148 return Status;
149 }
150
151 NTSTATUS
152 CUSBQueue::InitializeSyncSchedule(
153 IN PEHCIHARDWAREDEVICE Hardware,
154 IN PDMAMEMORYMANAGER MemManager)
155 {
156 PHYSICAL_ADDRESS QueueHeadPhysAddr;
157 NTSTATUS Status;
158 ULONG Index;
159 PQUEUE_HEAD QueueHead;
160
161 //
162 // FIXME: check if smaller list sizes are supported
163 //
164 m_MaxPeriodicListEntries = 1024;
165
166 //
167 // use polling scheme of 32ms
168 //
169 m_MaxPollingInterval = 32;
170
171 //
172 // allocate dummy frame list array
173 //
174 m_SyncFrameListQueueHeads = (PQUEUE_HEAD*)ExAllocatePool(NonPagedPool, m_MaxPollingInterval * sizeof(PQUEUE_HEAD));
175 if (!m_SyncFrameListQueueHeads)
176 {
177 //
178 // no memory
179 //
180 return STATUS_INSUFFICIENT_RESOURCES;
181 }
182
183
184 //
185 // first allocate a page to hold the queue array
186 //
187 Status = MemManager->Allocate(m_MaxPeriodicListEntries * sizeof(PVOID), (PVOID*)&m_SyncFrameList, &m_SyncFrameListAddr);
188 if (!NT_SUCCESS(Status))
189 {
190 //
191 // failed to allocate sync frame list array
192 //
193 DPRINT1("Failed to allocate sync frame list\n");
194 ExFreePool(m_SyncFrameListQueueHeads);
195 //ASSERT(FALSE);
196 return STATUS_INSUFFICIENT_RESOURCES;
197 }
198
199 //
200 // now allocate queue head descriptors for the polling interval
201 //
202 for(Index = 0; Index < m_MaxPeriodicListEntries; Index++)
203 {
204 //
205 // check if is inside our polling interrupt frequency window
206 //
207 if (Index < m_MaxPollingInterval)
208 {
209 //
210 // allocate queue head
211 //
212 Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysAddr);
213
214 //
215 // initialize queue head
216 //
217 QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
218 QueueHead->AlternateNextPointer = TERMINATE_POINTER;
219 QueueHead->NextPointer = TERMINATE_POINTER;
220
221 //
222 // 1 for non high speed, 0 for high speed device
223 //
224 QueueHead->EndPointCharacteristics.ControlEndPointFlag = 0;
225 QueueHead->EndPointCharacteristics.HeadOfReclamation = FALSE;
226 QueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
227
228 //
229 // Set NakCountReload to max value possible
230 //
231 QueueHead->EndPointCharacteristics.NakCountReload = 0xF;
232
233 //
234 // Get the Initial Data Toggle from the QEDT
235 //
236 QueueHead->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
237
238 //
239 // FIXME: check if High Speed Device
240 //
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;
246
247
248 //
249 // store in queue head array
250 //
251 m_SyncFrameListQueueHeads[Index] = QueueHead;
252 }
253 else
254 {
255 //
256 // get cached entry
257 //
258 QueueHead = m_SyncFrameListQueueHeads[m_MaxPeriodicListEntries % m_MaxPollingInterval];
259 }
260
261 //
262 // store entry
263 //
264 m_SyncFrameList[Index] = (QueueHead->PhysicalAddr | 0x2);
265 }
266
267 //
268 // now set the sync base
269 //
270 Hardware->SetPeriodicListRegister(m_SyncFrameListAddr.LowPart);
271
272 //
273 // sync frame list initialized
274 //
275 return STATUS_SUCCESS;
276 }
277
278 NTSTATUS
279 CUSBQueue::AddUSBRequest(
280 IUSBRequest * Req)
281 {
282 PQUEUE_HEAD QueueHead;
283 NTSTATUS Status;
284 ULONG Type;
285 KIRQL OldLevel;
286 PEHCIREQUEST Request;
287
288 // sanity check
289 ASSERT(Req != NULL);
290
291 // get internal req
292 Request = PEHCIREQUEST(Req);
293
294 //
295 // get request type
296 //
297 Type = Request->GetTransferType();
298
299 //
300 // check if supported
301 //
302 switch(Type)
303 {
304 case USB_ENDPOINT_TYPE_ISOCHRONOUS:
305 case USB_ENDPOINT_TYPE_INTERRUPT:
306 /* NOT IMPLEMENTED IN QUEUE */
307 Status = STATUS_NOT_SUPPORTED;
308 break;
309 case USB_ENDPOINT_TYPE_BULK:
310 case USB_ENDPOINT_TYPE_CONTROL:
311 Status = STATUS_SUCCESS;
312 break;
313 default:
314 /* BUG */
315 PC_ASSERT(FALSE);
316 Status = STATUS_NOT_SUPPORTED;
317 }
318
319 //
320 // check for success
321 //
322 if (!NT_SUCCESS(Status))
323 {
324 //
325 // request not supported, please try later
326 //
327 return Status;
328 }
329
330 if (Type == USB_ENDPOINT_TYPE_BULK || Type == USB_ENDPOINT_TYPE_CONTROL)
331 {
332 //
333 // get queue head
334 //
335 Status = Request->GetQueueHead(&QueueHead);
336
337 //
338 // check for success
339 //
340 if (!NT_SUCCESS(Status))
341 {
342 //
343 // failed to get queue head
344 //
345 return Status;
346 }
347
348 DPRINT("Request %p QueueHead %p inserted into AsyncQueue\n", Request, QueueHead);
349
350 //
351 // Add it to the pending list
352 //
353 KeAcquireSpinLock(m_Lock, &OldLevel);
354 LinkQueueHead(AsyncListQueueHead, QueueHead);
355 KeReleaseSpinLock(m_Lock, OldLevel);
356
357 }
358
359
360 //
361 // add extra reference which is released when the request is completed
362 //
363 Request->AddRef();
364
365
366 return STATUS_SUCCESS;
367 }
368
369 NTSTATUS
370 CUSBQueue::CreateUSBRequest(
371 IUSBRequest **OutRequest)
372 {
373 PUSBREQUEST UsbRequest;
374 NTSTATUS Status;
375
376 *OutRequest = NULL;
377 Status = InternalCreateUSBRequest(&UsbRequest);
378
379 if (NT_SUCCESS(Status))
380 {
381 *OutRequest = UsbRequest;
382 }
383
384 return Status;
385 }
386
387 //
388 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
389 //
390 VOID
391 CUSBQueue::LinkQueueHead(
392 PQUEUE_HEAD HeadQueueHead,
393 PQUEUE_HEAD NewQueueHead)
394 {
395 PQUEUE_HEAD LastQueueHead, NextQueueHead;
396 PLIST_ENTRY Entry;
397 ASSERT(HeadQueueHead);
398 ASSERT(NewQueueHead);
399
400 //
401 // Link the LIST_ENTRYs
402 //
403 //ASSERT(IsListEmpty(&HeadQueueHead->LinkedQueueHeads));
404 InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads);
405
406 //
407 // Update HLP for Previous QueueHead, which should be the last in list.
408 //
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);
413
414 //
415 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
416 //
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);
421
422 //
423 // head queue head must be halted
424 //
425 //PC_ASSERT(HeadQueueHead->Token.Bits.Halted == TRUE);
426 }
427
428 //
429 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
430 //
431 VOID
432 CUSBQueue::UnlinkQueueHead(
433 PQUEUE_HEAD QueueHead)
434 {
435 PQUEUE_HEAD PreviousQH, NextQH;
436 PLIST_ENTRY Entry;
437
438 //
439 // sanity check: there must be at least one queue head with halted bit set
440 //
441 //PC_ASSERT(QueueHead->Token.Bits.Halted == 0);
442
443 //
444 // get previous link
445 //
446 Entry = QueueHead->LinkedQueueHeads.Blink;
447
448 //
449 // get queue head structure
450 //
451 PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
452
453 //
454 // get next link
455 //
456 Entry = QueueHead->LinkedQueueHeads.Flink;
457
458 //
459 // get queue head structure
460 //
461 NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
462
463 //
464 // sanity check
465 //
466 ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH));
467
468 //
469 // remove queue head from linked list
470 //
471 PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH;
472
473 //
474 // remove software link
475 //
476 RemoveEntryList(&QueueHead->LinkedQueueHeads);
477 }
478
479 //
480 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
481 //
482 VOID
483 CUSBQueue::LinkQueueHeadChain(
484 PQUEUE_HEAD HeadQueueHead,
485 PQUEUE_HEAD NewQueueHead)
486 {
487 PQUEUE_HEAD LastQueueHead;
488 PLIST_ENTRY Entry;
489 ASSERT(HeadQueueHead);
490 ASSERT(NewQueueHead);
491
492 //
493 // Find the last QueueHead in NewQueueHead
494 //
495 Entry = NewQueueHead->LinkedQueueHeads.Blink;
496 ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink);
497 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
498
499 //
500 // Set the LinkPointer and Flink
501 //
502 LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH;
503 LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads;
504
505 //
506 // Fine the last QueueHead in HeadQueueHead
507 //
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;
513 }
514
515 //
516 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
517 // returns the chain of QueueHeads removed from HeadQueueHead.
518 //
519 PQUEUE_HEAD
520 CUSBQueue::UnlinkQueueHeadChain(
521 PQUEUE_HEAD HeadQueueHead,
522 ULONG Count)
523 {
524 PQUEUE_HEAD LastQueueHead, FirstQueueHead;
525 PLIST_ENTRY Entry;
526 ULONG Index;
527
528 //
529 // Find the last QueueHead in NewQueueHead
530 //
531 Entry = &HeadQueueHead->LinkedQueueHeads;
532 FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads);
533
534 for (Index = 0; Index < Count; Index++)
535 {
536 Entry = Entry->Flink;
537
538 if (Entry == &HeadQueueHead->LinkedQueueHeads)
539 {
540 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index);
541 Count = Index + 1;
542 break;
543 }
544 }
545
546 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
547 HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink;
548 if (Count + 1 == Index)
549 {
550 HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads;
551 }
552 else
553 HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink;
554
555 FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
556 LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads;
557 LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
558 return FirstQueueHead;
559 }
560
561 VOID
562 CUSBQueue::QueueHeadCompletion(
563 PQUEUE_HEAD CurrentQH,
564 NTSTATUS Status)
565 {
566 //
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
570 //
571
572 //
573 // unlink queue head
574 //
575 UnlinkQueueHead(CurrentQH);
576
577 //
578 // insert into completed list
579 //
580 InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads);
581 }
582
583 VOID
584 CUSBQueue::ProcessAsyncList(
585 IN NTSTATUS Status,
586 OUT PULONG ShouldRingDoorBell)
587 {
588 KIRQL OldLevel;
589 PLIST_ENTRY Entry;
590 PQUEUE_HEAD QueueHead;
591 PEHCIREQUEST Request;
592 BOOLEAN IsQueueHeadComplete;
593
594 //
595 // lock completed async list
596 //
597 KeAcquireSpinLock(m_Lock, &OldLevel);
598
599 //
600 // walk async list
601 //
602 ASSERT(AsyncListQueueHead);
603 Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
604
605 while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
606 {
607 //
608 // get queue head structure
609 //
610 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
611 ASSERT(QueueHead);
612
613 //
614 // sanity check
615 //
616 PC_ASSERT(QueueHead->Request);
617
618 //
619 // get IUSBRequest interface
620 //
621 Request = (PEHCIREQUEST)QueueHead->Request;
622
623 //
624 // move to next entry
625 //
626 Entry = Entry->Flink;
627
628 //
629 // check if queue head is complete
630 //
631 IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
632
633 DPRINT("Request %p QueueHead %p Complete %d\n", Request, QueueHead, IsQueueHeadComplete);
634
635 //
636 // check if queue head is complete
637 //
638 if (IsQueueHeadComplete)
639 {
640 //
641 // current queue head is complete
642 //
643 QueueHeadCompletion(QueueHead, Status);
644
645 //
646 // ring door bell is going to be necessary
647 //
648 *ShouldRingDoorBell = TRUE;
649 }
650 }
651
652 //
653 // release lock
654 //
655 KeReleaseSpinLock(m_Lock, OldLevel);
656 }
657
658
659 VOID
660 CUSBQueue::InterruptCallback(
661 IN NTSTATUS Status,
662 OUT PULONG ShouldRingDoorBell)
663 {
664
665 DPRINT("CUSBQueue::InterruptCallback\n");
666
667 //
668 // iterate asynchronous list
669 //
670 *ShouldRingDoorBell = FALSE;
671 ProcessAsyncList(Status, ShouldRingDoorBell);
672
673 //
674 // TODO: implement periodic schedule processing
675 //
676 }
677
678 VOID
679 CUSBQueue::QueueHeadCleanup(
680 PQUEUE_HEAD CurrentQH)
681 {
682 PQUEUE_HEAD NewQueueHead;
683 PEHCIREQUEST Request;
684 BOOLEAN ShouldReleaseWhenDone;
685 USBD_STATUS UrbStatus;
686 KIRQL OldLevel;
687
688 //
689 // sanity checks
690 //
691 PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
692 PC_ASSERT(CurrentQH->Request);
693
694
695 //
696 // get request
697 //
698 Request = (PEHCIREQUEST)CurrentQH->Request;
699
700 //
701 // sanity check
702 //
703 PC_ASSERT(Request);
704
705 //
706 // check if the queue head was completed with errors
707 //
708 if (CurrentQH->Token.Bits.Halted)
709 {
710 if (CurrentQH->Token.Bits.DataBufferError)
711 {
712 //
713 // data buffer error
714 //
715 UrbStatus = USBD_STATUS_DATA_BUFFER_ERROR;
716 }
717 else if (CurrentQH->Token.Bits.BabbleDetected)
718 {
719 //
720 // babble detected
721 //
722 UrbStatus = USBD_STATUS_BABBLE_DETECTED;
723 }
724 else
725 {
726 //
727 // stall pid
728 //
729 UrbStatus = USBD_STATUS_STALL_PID;
730 }
731 }
732 else
733 {
734 //
735 // well done ;)
736 //
737 UrbStatus = USBD_STATUS_SUCCESS;
738 }
739
740 //
741 // Check if the transfer was completed and if UrbStatus is ok
742 //
743 if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS))
744 {
745 //
746 // request is incomplete, get new queue head
747 //
748 if (Request->GetQueueHead(&NewQueueHead) == STATUS_SUCCESS)
749 {
750 //
751 // let IUSBRequest free the queue head
752 //
753 Request->FreeQueueHead(CurrentQH);
754
755 //
756 // first acquire request lock
757 //
758 KeAcquireSpinLock(m_Lock, &OldLevel);
759
760 //
761 // add to pending list
762 //
763 InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads);
764
765 //
766 // release queue head
767 //
768 KeReleaseSpinLock(m_Lock, OldLevel);
769
770 //
771 // Done for now
772 //
773 return;
774 }
775 DPRINT1("Unable to create a new QueueHead\n");
776 //ASSERT(FALSE);
777
778 //
779 // Else there was a problem
780 // FIXME: Find better return
781 UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
782 }
783
784 if (UrbStatus != USBD_STATUS_SUCCESS)
785 {
786 DPRINT1("URB failed with status 0x%x\n", UrbStatus);
787 //PC_ASSERT(FALSE);
788 }
789
790 //
791 // notify request that a transfer has completed
792 //
793 Request->CompletionCallback(UrbStatus != USBD_STATUS_SUCCESS ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS,
794 UrbStatus,
795 CurrentQH);
796
797 //
798 // let IUSBRequest free the queue head
799 //
800 Request->FreeQueueHead(CurrentQH);
801
802 //
803 // check if we should release request when done
804 //
805 ShouldReleaseWhenDone = Request->ShouldReleaseRequestAfterCompletion();
806
807 //
808 // release reference when the request was added
809 //
810 Request->Release();
811
812 //
813 // check if the operation was asynchronous
814 //
815 if (ShouldReleaseWhenDone)
816 {
817 //
818 // release outstanding reference count
819 //
820 Request->Release();
821 }
822
823 //
824 // request is now released
825 //
826 }
827
828 VOID
829 CUSBQueue::CompleteAsyncRequests()
830 {
831 KIRQL OldLevel;
832 PLIST_ENTRY Entry;
833 PQUEUE_HEAD CurrentQH;
834
835 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
836
837 //
838 // first acquire request lock
839 //
840 KeAcquireSpinLock(m_Lock, &OldLevel);
841
842 //
843 // the list should not be empty
844 //
845 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList));
846
847 while(!IsListEmpty(&m_CompletedRequestAsyncList))
848 {
849 //
850 // remove first entry
851 //
852 Entry = RemoveHeadList(&m_CompletedRequestAsyncList);
853
854 //
855 // get queue head structure
856 //
857 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
858
859 //
860 // release lock
861 //
862 KeReleaseSpinLock(m_Lock, OldLevel);
863
864 //
865 // complete request now
866 //
867 QueueHeadCleanup(CurrentQH);
868
869 //
870 // first acquire request lock
871 //
872 KeAcquireSpinLock(m_Lock, &OldLevel);
873 }
874
875 //
876 // is there a pending async entry
877 //
878 if (!IsListEmpty(&m_PendingRequestAsyncList))
879 {
880 //
881 // remove first entry
882 //
883 Entry = RemoveHeadList(&m_PendingRequestAsyncList);
884
885 //
886 // get queue head structure
887 //
888 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
889
890 //
891 // Add it to the AsyncList list
892 //
893 LinkQueueHead(AsyncListQueueHead, CurrentQH);
894 }
895
896 //
897 // release lock
898 //
899 KeReleaseSpinLock(m_Lock, OldLevel);
900 }
901
902 NTSTATUS
903 CUSBQueue::AbortDevicePipe(
904 IN UCHAR DeviceAddress,
905 IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor)
906 {
907 KIRQL OldLevel;
908 PLIST_ENTRY Entry;
909 PQUEUE_HEAD QueueHead;
910 LIST_ENTRY ListHead;
911
912 //
913 // lock completed async list
914 //
915 KeAcquireSpinLock(m_Lock, &OldLevel);
916
917 DPRINT1("AbortDevicePipe DeviceAddress %x EndpointDescriptor %p Addr %x\n", DeviceAddress, EndpointDescriptor, EndpointDescriptor->bEndpointAddress);
918
919 //
920 // init list head
921 //
922 InitializeListHead(&ListHead);
923
924
925 //
926 // walk async list
927 //
928 ASSERT(AsyncListQueueHead);
929 Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
930
931 while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
932 {
933 //
934 // get queue head structure
935 //
936 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
937 ASSERT(QueueHead);
938
939 //
940 // move to next entry
941 //
942 Entry = Entry->Flink;
943
944 if (QueueHead->EndPointCharacteristics.DeviceAddress == DeviceAddress &&
945 QueueHead->EndPointCharacteristics.EndPointNumber == (EndpointDescriptor->bEndpointAddress & 0xF) && QueueHead->Token.Bits.Halted)
946 {
947 //
948 // unlink queue head
949 //
950 UnlinkQueueHead(QueueHead);
951
952 //
953 // add to temp list
954 //
955 InsertTailList(&ListHead, &QueueHead->LinkedQueueHeads);
956 }
957 }
958
959 //
960 // release lock
961 //
962 KeReleaseSpinLock(m_Lock, OldLevel);
963
964 while(!IsListEmpty(&ListHead))
965 {
966 //
967 // remove entry
968 //
969 Entry = RemoveHeadList(&ListHead);
970
971 //
972 // get queue head structure
973 //
974 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
975 ASSERT(QueueHead);
976
977 //
978 // cleanup queue head
979 //
980 QueueHeadCleanup(QueueHead);
981 }
982 return STATUS_SUCCESS;
983 }
984
985
986 NTSTATUS
987 CreateUSBQueue(
988 PUSBQUEUE *OutUsbQueue)
989 {
990 PUSBQUEUE This;
991
992 //
993 // allocate controller
994 //
995 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
996 if (!This)
997 {
998 //
999 // failed to allocate
1000 //
1001 return STATUS_INSUFFICIENT_RESOURCES;
1002 }
1003
1004 //
1005 // add reference count
1006 //
1007 This->AddRef();
1008
1009 //
1010 // return result
1011 //
1012 *OutUsbQueue = (PUSBQUEUE)This;
1013
1014 //
1015 // done
1016 //
1017 return STATUS_SUCCESS;
1018 }
1019