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