[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 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 PKSPIN_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 m_Lock = 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 DPRINT(__FUNCTION__ " acquire\n");
359 KeAcquireSpinLock(m_Lock, &OldLevel);
360 DPRINT(__FUNCTION__ " acquired\n");
361
362 LinkQueueHead(AsyncListQueueHead, QueueHead);
363 DPRINT(__FUNCTION__ "release\n");
364 KeReleaseSpinLock(m_Lock, OldLevel);
365 }
366
367
368 //
369 // add extra reference which is released when the request is completed
370 //
371 Request->AddRef();
372
373
374 return STATUS_SUCCESS;
375 }
376
377 NTSTATUS
378 CUSBQueue::AddUSBRequest(
379 PURB Urb)
380 {
381 UNIMPLEMENTED
382 return STATUS_NOT_IMPLEMENTED;
383 }
384
385 NTSTATUS
386 CUSBQueue::CancelRequests()
387 {
388 UNIMPLEMENTED
389 return STATUS_NOT_IMPLEMENTED;
390 }
391
392 NTSTATUS
393 CUSBQueue::CreateUSBRequest(
394 IUSBRequest **OutRequest)
395 {
396 PUSBREQUEST UsbRequest;
397 NTSTATUS Status;
398
399 *OutRequest = NULL;
400 Status = InternalCreateUSBRequest(&UsbRequest);
401
402 if (NT_SUCCESS(Status))
403 {
404 *OutRequest = UsbRequest;
405 }
406
407 return Status;
408 }
409
410 //
411 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
412 //
413 VOID
414 CUSBQueue::LinkQueueHead(
415 PQUEUE_HEAD HeadQueueHead,
416 PQUEUE_HEAD NewQueueHead)
417 {
418 PQUEUE_HEAD LastQueueHead, NextQueueHead;
419 PLIST_ENTRY Entry;
420 ASSERT(HeadQueueHead);
421 ASSERT(NewQueueHead);
422
423 //
424 // Link the LIST_ENTRYs
425 //
426 InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads);
427
428 //
429 // Update HLP for Previous QueueHead, which should be the last in list.
430 //
431 Entry = NewQueueHead->LinkedQueueHeads.Blink;
432 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
433 LastQueueHead->HorizontalLinkPointer = (NewQueueHead->PhysicalAddr | QH_TYPE_QH);
434
435 //
436 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
437 //
438 Entry = NewQueueHead->LinkedQueueHeads.Flink;
439 NextQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
440 ASSERT(NextQueueHead == HeadQueueHead);
441 NewQueueHead->HorizontalLinkPointer = (NextQueueHead->PhysicalAddr | QH_TYPE_QH);
442 }
443
444 //
445 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
446 //
447 VOID
448 CUSBQueue::UnlinkQueueHead(
449 PQUEUE_HEAD QueueHead)
450 {
451 PQUEUE_HEAD PreviousQH, NextQH;
452 PLIST_ENTRY Entry;
453
454 //
455 // sanity check: there must be at least one queue head with halted bit set
456 //
457 PC_ASSERT(QueueHead->Token.Bits.Halted == 0);
458
459 //
460 // get previous link
461 //
462 Entry = QueueHead->LinkedQueueHeads.Blink;
463
464 //
465 // get queue head structure
466 //
467 PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
468
469 //
470 // get next link
471 //
472 Entry = QueueHead->LinkedQueueHeads.Flink;
473
474 //
475 // get queue head structure
476 //
477 NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
478
479 //
480 // sanity check
481 //
482 ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH));
483
484 //
485 // remove queue head from linked list
486 //
487 PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH;
488
489 //
490 // remove software link
491 //
492 RemoveEntryList(&QueueHead->LinkedQueueHeads);
493 }
494
495 //
496 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
497 //
498 VOID
499 CUSBQueue::LinkQueueHeadChain(
500 PQUEUE_HEAD HeadQueueHead,
501 PQUEUE_HEAD NewQueueHead)
502 {
503 PQUEUE_HEAD LastQueueHead;
504 PLIST_ENTRY Entry;
505 ASSERT(HeadQueueHead);
506 ASSERT(NewQueueHead);
507
508 //
509 // Find the last QueueHead in NewQueueHead
510 //
511 Entry = NewQueueHead->LinkedQueueHeads.Blink;
512 ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink);
513 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
514
515 //
516 // Set the LinkPointer and Flink
517 //
518 LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH;
519 LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads;
520
521 //
522 // Fine the last QueueHead in HeadQueueHead
523 //
524 Entry = HeadQueueHead->LinkedQueueHeads.Blink;
525 HeadQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
526 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
527 LastQueueHead->LinkedQueueHeads.Flink = &NewQueueHead->LinkedQueueHeads;
528 LastQueueHead->HorizontalLinkPointer = NewQueueHead->PhysicalAddr | QH_TYPE_QH;
529 }
530
531 //
532 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
533 // returns the chain of QueueHeads removed from HeadQueueHead.
534 //
535 PQUEUE_HEAD
536 CUSBQueue::UnlinkQueueHeadChain(
537 PQUEUE_HEAD HeadQueueHead,
538 ULONG Count)
539 {
540 PQUEUE_HEAD LastQueueHead, FirstQueueHead;
541 PLIST_ENTRY Entry;
542 ULONG Index;
543
544 //
545 // Find the last QueueHead in NewQueueHead
546 //
547 Entry = &HeadQueueHead->LinkedQueueHeads;
548 FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads);
549
550 for (Index = 0; Index < Count; Index++)
551 {
552 Entry = Entry->Flink;
553
554 if (Entry == &HeadQueueHead->LinkedQueueHeads)
555 {
556 DPRINT1("Warnnig; Only %d QueueHeads in HeadQueueHead\n", Index);
557 Count = Index + 1;
558 break;
559 }
560 }
561
562 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
563 HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink;
564 if (Count + 1 == Index)
565 {
566 HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads;
567 }
568 else
569 HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink;
570
571 FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
572 LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads;
573 LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
574 return FirstQueueHead;
575 }
576
577 VOID
578 CUSBQueue::QueueHeadCompletion(
579 PQUEUE_HEAD CurrentQH,
580 NTSTATUS Status)
581 {
582 //
583 // now unlink the queue head
584 // FIXME: implement chained queue heads
585 // no need to acquire locks, as it is called with locks held
586 //
587
588 //
589 // unlink queue head
590 //
591 UnlinkQueueHead(CurrentQH);
592
593 //
594 // insert into completed list
595 //
596 InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads);
597 }
598
599 VOID
600 CUSBQueue::ProcessAsyncList(
601 IN NTSTATUS Status,
602 OUT PULONG ShouldRingDoorBell)
603 {
604 KIRQL OldLevel;
605 PLIST_ENTRY Entry;
606 PQUEUE_HEAD QueueHead;
607 IUSBRequest * Request;
608 BOOLEAN IsQueueHeadComplete;
609
610 //
611 // lock completed async list
612 //
613 DPRINT(__FUNCTION__ " acquire\n");
614 KeAcquireSpinLock(m_Lock, &OldLevel);
615 DPRINT(__FUNCTION__ " acquired\n");
616
617 //
618 // walk async list
619 //
620 Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
621
622 while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
623 {
624 //
625 // get queue head structure
626 //
627 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
628
629 //
630 // sanity check
631 //
632 PC_ASSERT(QueueHead->Request);
633
634 //
635 // get IUSBRequest interface
636 //
637 Request = (IUSBRequest*)QueueHead->Request;
638
639 //
640 // move to next entry
641 //
642 Entry = Entry->Flink;
643
644 //
645 // check if queue head is complete
646 //
647 IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
648
649 DPRINT("Request %p QueueHead %p Complete %d\n", Request, QueueHead, IsQueueHeadComplete);
650
651 //
652 // check if queue head is complete
653 //
654 if (IsQueueHeadComplete)
655 {
656 //
657 // current queue head is complete
658 //
659 QueueHeadCompletion(QueueHead, Status);
660
661 //
662 // ring door bell is going to be necessary
663 //
664 *ShouldRingDoorBell = TRUE;
665 }
666 }
667
668 //
669 // release lock
670 //
671 DPRINT(__FUNCTION__ "release\n");
672 KeReleaseSpinLock(m_Lock, OldLevel);
673 }
674
675
676 VOID
677 CUSBQueue::InterruptCallback(
678 IN NTSTATUS Status,
679 OUT PULONG ShouldRingDoorBell)
680 {
681
682 DPRINT("CUSBQueue::InterruptCallback\n");
683
684 //
685 // iterate asynchronous list
686 //
687 *ShouldRingDoorBell = FALSE;
688 ProcessAsyncList(Status, ShouldRingDoorBell);
689
690 //
691 // TODO: implement periodic schedule processing
692 //
693 }
694
695 VOID
696 CUSBQueue::QueueHeadCleanup(
697 PQUEUE_HEAD CurrentQH)
698 {
699 PQUEUE_HEAD NewQueueHead;
700 IUSBRequest * Request;
701 BOOLEAN ShouldReleaseWhenDone;
702 USBD_STATUS UrbStatus;
703 KIRQL OldLevel;
704
705 //
706 // sanity checks
707 //
708 PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
709 PC_ASSERT(CurrentQH->Request);
710
711
712 //
713 // get request
714 //
715 Request = (IUSBRequest*)CurrentQH->Request;
716
717 //
718 // sanity check
719 //
720 PC_ASSERT(Request);
721
722 //
723 // check if the queue head was completed with errors
724 //
725 if (CurrentQH->Token.Bits.Halted)
726 {
727 if (CurrentQH->Token.Bits.DataBufferError)
728 {
729 //
730 // data buffer error
731 //
732 UrbStatus = USBD_STATUS_DATA_BUFFER_ERROR;
733 }
734 else if (CurrentQH->Token.Bits.BabbleDetected)
735 {
736 //
737 // babble detected
738 //
739 UrbStatus = USBD_STATUS_BABBLE_DETECTED;
740 }
741 else
742 {
743 //
744 // stall pid
745 //
746 UrbStatus = USBD_STATUS_STALL_PID;
747 }
748 }
749 else
750 {
751 //
752 // well done ;)
753 //
754 UrbStatus = USBD_STATUS_SUCCESS;
755 }
756
757 //
758 // Check if the transfer was completed and if UrbStatus is ok
759 //
760 if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS))
761 {
762 //
763 // let IUSBRequest free the queue head
764 //
765 Request->FreeQueueHead(CurrentQH);
766
767 //
768 // request is incomplete, get new queue head
769 //
770 if (Request->GetQueueHead(&NewQueueHead) == STATUS_SUCCESS)
771 {
772 //
773 // first acquire request lock
774 //
775 DPRINT(__FUNCTION__ " acquire\n");
776 KeAcquireSpinLock(m_Lock, &OldLevel);
777 DPRINT(__FUNCTION__ " acquired\n");
778
779 //
780 // add to pending list
781 //
782 InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads);
783
784 //
785 // release queue head
786 //
787 DPRINT(__FUNCTION__ "release\n");
788 KeReleaseSpinLock(m_Lock, OldLevel);
789
790 //
791 // Done for now
792 //
793 return;
794 }
795 DPRINT1("Unable to create a new QueueHead\n");
796 PC_ASSERT(FALSE);
797
798 //
799 // Else there was a problem
800 // FIXME: Find better return
801 UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
802 }
803
804 if (UrbStatus != USBD_STATUS_SUCCESS) PC_ASSERT(FALSE);
805
806 //
807 // notify request that a transfer has completed
808 //
809 Request->CompletionCallback(UrbStatus != USBD_STATUS_SUCCESS ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS,
810 UrbStatus,
811 CurrentQH);
812
813 //
814 // let IUSBRequest free the queue head
815 //
816 Request->FreeQueueHead(CurrentQH);
817
818 //
819 // check if we should release request when done
820 //
821 ShouldReleaseWhenDone = Request->ShouldReleaseRequestAfterCompletion();
822
823 //
824 // release reference when the request was added
825 //
826 Request->Release();
827
828 //
829 // check if the operation was asynchronous
830 //
831 if (ShouldReleaseWhenDone)
832 {
833 //
834 // release outstanding reference count
835 //
836 Request->Release();
837 }
838
839 //
840 // request is now released
841 //
842 }
843
844 VOID
845 CUSBQueue::CompleteAsyncRequests()
846 {
847 KIRQL OldLevel;
848 PLIST_ENTRY Entry;
849 PQUEUE_HEAD CurrentQH;
850 IUSBRequest *Request;
851
852 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
853
854 //
855 // first acquire request lock
856 //
857 DPRINT(__FUNCTION__ " acquire\n");
858 KeAcquireSpinLock(m_Lock, &OldLevel);
859 DPRINT(__FUNCTION__ " acquired\n");
860
861 //
862 // the list should not be empty
863 //
864 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList));
865
866 while(!IsListEmpty(&m_CompletedRequestAsyncList))
867 {
868 //
869 // remove first entry
870 //
871 Entry = RemoveHeadList(&m_CompletedRequestAsyncList);
872
873 //
874 // get queue head structure
875 //
876 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
877
878 //
879 // Get the Request for this QueueHead
880 //
881 Request = (IUSBRequest*) CurrentQH->Request;
882
883 //
884 // release lock
885 //
886 DPRINT(__FUNCTION__ "release\n");
887 KeReleaseSpinLock(m_Lock, OldLevel);
888
889 //
890 // complete request now
891 //
892 QueueHeadCleanup(CurrentQH);
893
894 //
895 // first acquire request lock
896 //
897 DPRINT(__FUNCTION__ " acquire\n");
898 KeAcquireSpinLock(m_Lock, &OldLevel);
899 DPRINT(__FUNCTION__ " acquired\n");
900
901 }
902
903 //
904 // is there a pending async entry
905 //
906 if (!IsListEmpty(&m_PendingRequestAsyncList))
907 {
908 //
909 // remove first entry
910 //
911 Entry = RemoveHeadList(&m_PendingRequestAsyncList);
912
913 //
914 // get queue head structure
915 //
916 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
917
918 //
919 // Add it to the AsyncList list
920 //
921 LinkQueueHead(AsyncListQueueHead, CurrentQH);
922 }
923
924 //
925 // release lock
926 //
927 DPRINT(__FUNCTION__ "release\n");
928 KeReleaseSpinLock(m_Lock, OldLevel);
929 }
930
931 NTSTATUS
932 CreateUSBQueue(
933 PUSBQUEUE *OutUsbQueue)
934 {
935 PUSBQUEUE This;
936
937 //
938 // allocate controller
939 //
940 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
941 if (!This)
942 {
943 //
944 // failed to allocate
945 //
946 return STATUS_INSUFFICIENT_RESOURCES;
947 }
948
949 //
950 // add reference count
951 //
952 This->AddRef();
953
954 //
955 // return result
956 //
957 *OutUsbQueue = (PUSBQUEUE)This;
958
959 //
960 // done
961 //
962 return STATUS_SUCCESS;
963 }