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