[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
699 //
700 // sanity checks
701 //
702 PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
703 PC_ASSERT(CurrentQH->Request);
704
705
706 //
707 // get request
708 //
709 Request = (IUSBRequest*)CurrentQH->Request;
710
711 //
712 // sanity check
713 //
714 PC_ASSERT(Request);
715
716 //
717 // check if the queue head was completed with errors
718 //
719 if (CurrentQH->Token.Bits.Halted)
720 {
721 if (CurrentQH->Token.Bits.DataBufferError)
722 {
723 //
724 // data buffer error
725 //
726 UrbStatus = USBD_STATUS_DATA_BUFFER_ERROR;
727 }
728 else if (CurrentQH->Token.Bits.BabbleDetected)
729 {
730 //
731 // babble detected
732 //
733 UrbStatus = USBD_STATUS_BABBLE_DETECTED;
734 }
735 else
736 {
737 //
738 // stall pid
739 //
740 UrbStatus = USBD_STATUS_STALL_PID;
741 }
742 }
743 else
744 {
745 //
746 // well done ;)
747 //
748 UrbStatus = USBD_STATUS_SUCCESS;
749 }
750
751 //
752 // Check if the transfer was completed and if UrbStatus is ok
753 //
754 if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS))
755 {
756 //
757 // let IUSBRequest free the queue head
758 //
759 Request->FreeQueueHead(CurrentQH);
760
761 //
762 // request is incomplete, get new queue head
763 //
764 if (Request->GetQueueHead(&NewQueueHead) == STATUS_SUCCESS)
765 {
766 //
767 // add to pending list
768 //
769 InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads);
770
771 //
772 // Done for now
773 //
774 return;
775 }
776 DPRINT1("Unable to create a new QueueHead\n");
777 PC_ASSERT(FALSE);
778
779 //
780 // Else there was a problem
781 // FIXME: Find better return
782 UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
783 }
784
785 if (UrbStatus != USBD_STATUS_SUCCESS) PC_ASSERT(FALSE);
786
787 //
788 // notify request that a transfer has completed
789 //
790 Request->CompletionCallback(UrbStatus != USBD_STATUS_SUCCESS ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS,
791 UrbStatus,
792 CurrentQH);
793
794 //
795 // let IUSBRequest free the queue head
796 //
797 Request->FreeQueueHead(CurrentQH);
798
799 //
800 // check if we should release request when done
801 //
802 ShouldReleaseWhenDone = Request->ShouldReleaseRequestAfterCompletion();
803
804 //
805 // release reference when the request was added
806 //
807 Request->Release();
808
809 //
810 // check if the operation was asynchronous
811 //
812 if (ShouldReleaseWhenDone)
813 {
814 //
815 // release outstanding reference count
816 //
817 Request->Release();
818 }
819
820 //
821 // request is now released
822 //
823 }
824
825 VOID
826 CUSBQueue::CompleteAsyncRequests()
827 {
828 KIRQL OldLevel;
829 PLIST_ENTRY Entry;
830 PQUEUE_HEAD CurrentQH;
831 IUSBRequest *Request;
832
833 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
834
835 //
836 // first acquire request lock
837 //
838 KeAcquireSpinLock(&m_Lock, &OldLevel);
839
840 //
841 // the list should not be empty
842 //
843 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList));
844
845 while(!IsListEmpty(&m_CompletedRequestAsyncList))
846 {
847 //
848 // remove first entry
849 //
850 Entry = RemoveHeadList(&m_CompletedRequestAsyncList);
851
852 //
853 // get queue head structure
854 //
855 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
856
857 //
858 // Get the Request for this QueueHead
859 //
860 Request = (IUSBRequest*) CurrentQH->Request;
861
862 //
863 // complete request now
864 //
865 QueueHeadCleanup(CurrentQH);
866 }
867
868 //
869 // is there a pending async entry
870 //
871 if (!IsListEmpty(&m_PendingRequestAsyncList))
872 {
873 //
874 // remove first entry
875 //
876 Entry = RemoveHeadList(&m_PendingRequestAsyncList);
877
878 //
879 // get queue head structure
880 //
881 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
882
883 //
884 // Add it to the AsyncList list
885 //
886 LinkQueueHead(AsyncListQueueHead, CurrentQH);
887 }
888
889 //
890 // release lock
891 //
892 KeReleaseSpinLock(&m_Lock, OldLevel);
893 }
894
895 NTSTATUS
896 CreateUSBQueue(
897 PUSBQUEUE *OutUsbQueue)
898 {
899 PUSBQUEUE This;
900
901 //
902 // allocate controller
903 //
904 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
905 if (!This)
906 {
907 //
908 // failed to allocate
909 //
910 return STATUS_INSUFFICIENT_RESOURCES;
911 }
912
913 //
914 // add reference count
915 //
916 This->AddRef();
917
918 //
919 // return result
920 //
921 *OutUsbQueue = (PUSBQUEUE)This;
922
923 //
924 // done
925 //
926 return STATUS_SUCCESS;
927 }