9f2361f4c3414879b781000a770a35c4e27ecb06
[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
13 class CUSBQueue : public IEHCIQueue
14 {
15 public:
16 STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
17
18 STDMETHODIMP_(ULONG) AddRef()
19 {
20 InterlockedIncrement(&m_Ref);
21 return m_Ref;
22 }
23 STDMETHODIMP_(ULONG) Release()
24 {
25 InterlockedDecrement(&m_Ref);
26
27 if (!m_Ref)
28 {
29 delete this;
30 return 0;
31 }
32 return m_Ref;
33 }
34
35 // IUSBQueue functions
36 IMP_IUSBQUEUE
37
38 // IEHCIQueue functions
39 IMP_IEHCIQUEUE
40
41 // constructor / destructor
42 CUSBQueue(IUnknown *OuterUnknown){}
43 virtual ~CUSBQueue(){}
44
45 protected:
46 LONG m_Ref; // reference count
47 PKSPIN_LOCK m_Lock; // list lock
48 PDMA_ADAPTER m_Adapter; // dma adapter
49 PEHCIHARDWAREDEVICE m_Hardware; // stores hardware object
50 PQUEUE_HEAD AsyncListQueueHead; // async queue head
51 LIST_ENTRY m_CompletedRequestAsyncList; // completed async request list
52 LIST_ENTRY m_PendingRequestAsyncList; // pending async request list
53 ULONG m_MaxPeriodicListEntries; // max perdiodic list entries
54 ULONG m_MaxPollingInterval; // max polling interval
55 PHYSICAL_ADDRESS m_SyncFrameListAddr; // physical address of sync frame list
56 PULONG m_SyncFrameList; // virtual address of sync frame list
57
58 // queue head manipulation functions
59 VOID LinkQueueHead(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
60 VOID UnlinkQueueHead(PQUEUE_HEAD QueueHead);
61 VOID LinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, PQUEUE_HEAD NewQueueHead);
62 PQUEUE_HEAD UnlinkQueueHeadChain(PQUEUE_HEAD HeadQueueHead, ULONG Count);
63
64 // processes the async list
65 VOID ProcessAsyncList(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell);
66
67 // processes the async list
68 VOID ProcessPeriodicSchedule(IN NTSTATUS Status, OUT PULONG ShouldRingDoorBell);
69
70 // called for each completed queue head
71 VOID QueueHeadCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
72
73 // called for each completed queue head
74 VOID QueueHeadInterruptCompletion(PQUEUE_HEAD QueueHead, NTSTATUS Status);
75
76 // called when the completion queue is cleaned up
77 VOID QueueHeadCleanup(PQUEUE_HEAD QueueHead);
78
79 // intializes the sync schedule
80 NTSTATUS InitializeSyncSchedule(IN PEHCIHARDWAREDEVICE Hardware, IN PDMAMEMORYMANAGER MemManager);
81
82 // links interrupt queue head
83 VOID LinkInterruptQueueHead(PQUEUE_HEAD QueueHead);
84
85 // interval index
86 UCHAR GetIntervalIndex(UCHAR Interval);
87
88
89 // interrupt queue heads
90 PQUEUE_HEAD m_InterruptQueueHeads[EHCI_INTERRUPT_ENTRIES_COUNT];
91
92 // contains the periodic queue heads
93 LIST_ENTRY m_PeriodicQueueHeads;
94 };
95
96 //=================================================================================================
97 // COM
98 //
99 NTSTATUS
100 STDMETHODCALLTYPE
101 CUSBQueue::QueryInterface(
102 IN REFIID refiid,
103 OUT PVOID* Output)
104 {
105 if (IsEqualGUIDAligned(refiid, IID_IUnknown))
106 {
107 *Output = PVOID(PUNKNOWN(this));
108 PUNKNOWN(*Output)->AddRef();
109 return STATUS_SUCCESS;
110 }
111
112 return STATUS_UNSUCCESSFUL;
113 }
114
115 NTSTATUS
116 STDMETHODCALLTYPE
117 CUSBQueue::Initialize(
118 IN PUSBHARDWAREDEVICE Hardware,
119 IN PDMA_ADAPTER AdapterObject,
120 IN PDMAMEMORYMANAGER MemManager,
121 IN OPTIONAL PKSPIN_LOCK Lock)
122 {
123 NTSTATUS Status = STATUS_SUCCESS;
124
125 DPRINT("CUSBQueue::Initialize()\n");
126
127 ASSERT(Hardware);
128
129 //
130 // store device lock
131 //
132 m_Lock = Lock;
133
134 //
135 // store hardware object
136 //
137 m_Hardware = PEHCIHARDWAREDEVICE(Hardware);
138
139
140 //
141 // Get the AsyncQueueHead
142 //
143 AsyncListQueueHead = (PQUEUE_HEAD)m_Hardware->GetAsyncListQueueHead();
144
145 //
146 // Initialize the List Head
147 //
148 InitializeListHead(&AsyncListQueueHead->LinkedQueueHeads);
149
150 //
151 // Initialize completed async list head
152 //
153 InitializeListHead(&m_CompletedRequestAsyncList);
154
155 //
156 // Initialize pending async list head
157 //
158 InitializeListHead(&m_PendingRequestAsyncList);
159
160 //
161 // initialize periodic queue heads
162 //
163 InitializeListHead(&m_PeriodicQueueHeads);
164
165 //
166 // now initialize sync schedule
167 //
168 Status = InitializeSyncSchedule(m_Hardware, MemManager);
169
170
171 return Status;
172 }
173
174 NTSTATUS
175 CUSBQueue::InitializeSyncSchedule(
176 IN PEHCIHARDWAREDEVICE Hardware,
177 IN PDMAMEMORYMANAGER MemManager)
178 {
179 PHYSICAL_ADDRESS QueueHeadPhysAddr;
180 NTSTATUS Status;
181 ULONG Index, Interval, IntervalIndex;
182 PQUEUE_HEAD QueueHead;
183
184 //
185 // FIXME: check if smaller list sizes are supported
186 //
187 m_MaxPeriodicListEntries = 1024;
188
189 //
190 // use polling scheme of 512ms
191 //
192 m_MaxPollingInterval = 512;
193
194 //
195 // first allocate a page to hold the queue array
196 //
197 Status = MemManager->Allocate(m_MaxPeriodicListEntries * sizeof(PVOID), (PVOID*)&m_SyncFrameList, &m_SyncFrameListAddr);
198 if (!NT_SUCCESS(Status))
199 {
200 //
201 // failed to allocate sync frame list array
202 //
203 DPRINT1("Failed to allocate sync frame list\n");
204 return STATUS_INSUFFICIENT_RESOURCES;
205 }
206
207 for(Index = 0; Index < EHCI_INTERRUPT_ENTRIES_COUNT; Index++)
208 {
209 //
210 // allocate queue head
211 //
212 Status = MemManager->Allocate(sizeof(QUEUE_HEAD), (PVOID*)&QueueHead, &QueueHeadPhysAddr);
213 if (!NT_SUCCESS(Status))
214 {
215 //
216 // failed to create queue head
217 //
218 DPRINT1("Failed to create queue head\n");
219 return Status;
220 }
221
222 //
223 // initialize queue head
224 //
225 QueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
226 QueueHead->AlternateNextPointer = TERMINATE_POINTER;
227 QueueHead->NextPointer = TERMINATE_POINTER;
228 QueueHead->EndPointCharacteristics.MaximumPacketLength = 64;
229 QueueHead->EndPointCharacteristics.NakCountReload = 0x3;
230 QueueHead->EndPointCharacteristics.EndPointSpeed = QH_ENDPOINT_HIGHSPEED;
231 QueueHead->EndPointCapabilities.NumberOfTransactionPerFrame = 0x01;
232 QueueHead->PhysicalAddr = QueueHeadPhysAddr.LowPart;
233 QueueHead->Token.Bits.Halted = TRUE; //FIXME
234 m_InterruptQueueHeads[Index]= QueueHead;
235
236 if (Index > 0)
237 {
238 // link all to the first queue head
239 QueueHead->HorizontalLinkPointer = m_InterruptQueueHeads[0]->PhysicalAddr | QH_TYPE_QH;
240 QueueHead->NextQueueHead = m_InterruptQueueHeads[0];
241 }
242 }
243
244 //
245 // build interrupt tree
246 //
247 Interval = EHCI_FRAMELIST_ENTRIES_COUNT;
248 IntervalIndex = EHCI_INTERRUPT_ENTRIES_COUNT - 1;
249 while (Interval > 1)
250 {
251 for (Index = Interval / 2; Index < EHCI_FRAMELIST_ENTRIES_COUNT; Index += Interval)
252 {
253 DPRINT("Index %lu IntervalIndex %lu\n", Index, IntervalIndex);
254 m_SyncFrameList[Index] = m_InterruptQueueHeads[IntervalIndex]->PhysicalAddr | QH_TYPE_QH;
255 }
256 IntervalIndex--;
257 Interval /= 2;
258 }
259
260 //
261 // now set the sync base
262 //
263 Hardware->SetPeriodicListRegister(m_SyncFrameListAddr.LowPart);
264
265 //
266 // sync frame list initialized
267 //
268 return STATUS_SUCCESS;
269 }
270
271 NTSTATUS
272 STDMETHODCALLTYPE
273 CUSBQueue::AddUSBRequest(
274 IUSBRequest * Req)
275 {
276 PQUEUE_HEAD QueueHead;
277 NTSTATUS Status;
278 ULONG Type;
279 KIRQL OldLevel;
280 PEHCIREQUEST Request;
281
282 // sanity check
283 ASSERT(Req != NULL);
284
285 // get internal req
286 Request = PEHCIREQUEST(Req);
287
288 // get request type
289 Type = Request->GetTransferType();
290
291 // check if supported
292 switch(Type)
293 {
294 case USB_ENDPOINT_TYPE_ISOCHRONOUS:
295 /* NOT IMPLEMENTED IN QUEUE */
296 Status = STATUS_NOT_SUPPORTED;
297 break;
298 case USB_ENDPOINT_TYPE_INTERRUPT:
299 case USB_ENDPOINT_TYPE_BULK:
300 case USB_ENDPOINT_TYPE_CONTROL:
301 Status = STATUS_SUCCESS;
302 break;
303 default:
304 /* BUG */
305 PC_ASSERT(FALSE);
306 Status = STATUS_NOT_SUPPORTED;
307 }
308
309 // check for success
310 if (!NT_SUCCESS(Status))
311 {
312 // request not supported, please try later
313 return Status;
314 }
315
316 // get queue head
317 Status = Request->GetQueueHead(&QueueHead);
318
319 // check for success
320 if (!NT_SUCCESS(Status))
321 {
322 // failed to get queue head
323 return Status;
324 }
325
326 // acquire lock
327 KeAcquireSpinLock(m_Lock, &OldLevel);
328
329 if (Type == USB_ENDPOINT_TYPE_BULK || Type == USB_ENDPOINT_TYPE_CONTROL)
330 {
331 // Add to list
332 LinkQueueHead(AsyncListQueueHead, QueueHead);
333 }
334 else if (Type == USB_ENDPOINT_TYPE_INTERRUPT)
335 {
336 // get interval
337 LinkInterruptQueueHead(QueueHead);
338 }
339
340 // release lock
341 KeReleaseSpinLock(m_Lock, OldLevel);
342
343
344 // add extra reference which is released when the request is completed
345 Request->AddRef();
346
347 // done
348 return STATUS_SUCCESS;
349 }
350
351 NTSTATUS
352 STDMETHODCALLTYPE
353 CUSBQueue::CreateUSBRequest(
354 IUSBRequest **OutRequest)
355 {
356 PUSBREQUEST UsbRequest;
357 NTSTATUS Status;
358
359 *OutRequest = NULL;
360 Status = InternalCreateUSBRequest(&UsbRequest);
361
362 if (NT_SUCCESS(Status))
363 {
364 *OutRequest = UsbRequest;
365 }
366
367 return Status;
368 }
369
370 UCHAR
371 CUSBQueue::GetIntervalIndex(
372 UCHAR Interval)
373 {
374 UCHAR IntervalIndex;
375
376 if (Interval == 1)
377 IntervalIndex = 1;
378 else if (Interval == 2)
379 IntervalIndex = 2;
380 else if (Interval <= 4)
381 IntervalIndex = 3;
382 else if (Interval <= 8)
383 IntervalIndex = 4;
384 else if (Interval <= 16)
385 IntervalIndex = 5;
386 else if (Interval <= 32)
387 IntervalIndex = 6;
388 else if (Interval <= 64)
389 IntervalIndex = 7;
390 else if (Interval <= 128)
391 IntervalIndex = 8;
392 else if (Interval <= 256)
393 IntervalIndex = 9;
394 else
395 IntervalIndex = 10;
396
397 return IntervalIndex;
398 }
399
400 VOID
401 CUSBQueue::LinkInterruptQueueHead(
402 PQUEUE_HEAD QueueHead)
403 {
404 PEHCIREQUEST Request;
405 UCHAR Interval, IntervalIndex;
406 USB_DEVICE_SPEED DeviceSpeed;
407 PQUEUE_HEAD InterruptQueueHead;
408
409 // get internal req
410 Request = PEHCIREQUEST(QueueHead->Request);
411 ASSERT(Request);
412
413 // get interval
414 Interval = Request->GetInterval();
415
416 // get device speed
417 DeviceSpeed = Request->GetSpeed();
418 if (DeviceSpeed == UsbHighSpeed)
419 {
420 // interrupt queue head can be scheduled on each possible micro frame
421 QueueHead->EndPointCapabilities.InterruptScheduleMask = 0xFF;
422 }
423 else
424 {
425 // As we do not yet support FSTNs to correctly reference low/full
426 // speed interrupt transfers, we simply put them into the 1 interval
427 // queue. This way we ensure that we reach them on every micro frame
428 // and can do the corresponding start/complete split transactions.
429 // ToDo: use FSTNs to correctly link non high speed interrupt transfers
430 Interval = 1;
431
432 // For now we also force start splits to be in micro frame 0 and
433 // complete splits to be in micro frame 2, 3 and 4.
434 QueueHead->EndPointCapabilities.InterruptScheduleMask = 0x01;
435 QueueHead->EndPointCapabilities.SplitCompletionMask = 0x1C;
436 }
437
438 // sanitize interrupt interval
439 Interval = max(1, Interval);
440
441 // get interval index
442 IntervalIndex = GetIntervalIndex(Interval);
443
444
445 // get interrupt queue head
446 InterruptQueueHead = m_InterruptQueueHeads[IntervalIndex];
447
448 // link queue head
449 QueueHead->HorizontalLinkPointer = InterruptQueueHead->HorizontalLinkPointer;
450 QueueHead->NextQueueHead = InterruptQueueHead->NextQueueHead;
451
452 InterruptQueueHead->HorizontalLinkPointer = QueueHead->PhysicalAddr | QH_TYPE_QH;
453 InterruptQueueHead->NextQueueHead = QueueHead;
454
455 // store in periodic list
456 InsertTailList(&m_PeriodicQueueHeads, &QueueHead->LinkedQueueHeads);
457 }
458
459 //
460 // LinkQueueHead - Links one QueueHead to the end of HeadQueueHead list, updating HorizontalLinkPointer.
461 //
462 VOID
463 CUSBQueue::LinkQueueHead(
464 PQUEUE_HEAD HeadQueueHead,
465 PQUEUE_HEAD NewQueueHead)
466 {
467 PQUEUE_HEAD LastQueueHead, NextQueueHead;
468 PLIST_ENTRY Entry;
469 ASSERT(HeadQueueHead);
470 ASSERT(NewQueueHead);
471
472 //
473 // Link the LIST_ENTRYs
474 //
475 //ASSERT(IsListEmpty(&HeadQueueHead->LinkedQueueHeads));
476 InsertTailList(&HeadQueueHead->LinkedQueueHeads, &NewQueueHead->LinkedQueueHeads);
477
478 //
479 // Update HLP for Previous QueueHead, which should be the last in list.
480 //
481 Entry = NewQueueHead->LinkedQueueHeads.Blink;
482 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
483 //ASSERT(LastQueueHead == HeadQueueHead);
484 LastQueueHead->HorizontalLinkPointer = (NewQueueHead->PhysicalAddr | QH_TYPE_QH);
485
486 //
487 // Update HLP for NewQueueHead to point to next, which should be the HeadQueueHead
488 //
489 Entry = NewQueueHead->LinkedQueueHeads.Flink;
490 NextQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
491 //ASSERT(NextQueueHead == HeadQueueHead);
492 NewQueueHead->HorizontalLinkPointer = (NextQueueHead->PhysicalAddr | QH_TYPE_QH);
493
494 //
495 // head queue head must be halted
496 //
497 //PC_ASSERT(HeadQueueHead->Token.Bits.Halted == TRUE);
498 }
499
500 //
501 // UnlinkQueueHead - Unlinks one QueueHead, updating HorizontalLinkPointer.
502 //
503 VOID
504 CUSBQueue::UnlinkQueueHead(
505 PQUEUE_HEAD QueueHead)
506 {
507 PQUEUE_HEAD PreviousQH, NextQH;
508 PLIST_ENTRY Entry;
509
510 //
511 // sanity check: there must be at least one queue head with halted bit set
512 //
513 //PC_ASSERT(QueueHead->Token.Bits.Halted == 0);
514
515 //
516 // get previous link
517 //
518 Entry = QueueHead->LinkedQueueHeads.Blink;
519
520 //
521 // get queue head structure
522 //
523 PreviousQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
524
525 //
526 // get next link
527 //
528 Entry = QueueHead->LinkedQueueHeads.Flink;
529
530 //
531 // get queue head structure
532 //
533 NextQH = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
534
535 //
536 // sanity check
537 //
538 ASSERT(QueueHead->HorizontalLinkPointer == (NextQH->PhysicalAddr | QH_TYPE_QH));
539
540 //
541 // remove queue head from linked list
542 //
543 PreviousQH->HorizontalLinkPointer = NextQH->PhysicalAddr | QH_TYPE_QH;
544
545 //
546 // remove software link
547 //
548 RemoveEntryList(&QueueHead->LinkedQueueHeads);
549 }
550
551 //
552 // LinkQueueHeadChain - Links a list of QueueHeads to the HeadQueueHead list, updating HorizontalLinkPointer.
553 //
554 VOID
555 CUSBQueue::LinkQueueHeadChain(
556 PQUEUE_HEAD HeadQueueHead,
557 PQUEUE_HEAD NewQueueHead)
558 {
559 PQUEUE_HEAD LastQueueHead;
560 PLIST_ENTRY Entry;
561 ASSERT(HeadQueueHead);
562 ASSERT(NewQueueHead);
563
564 //
565 // Find the last QueueHead in NewQueueHead
566 //
567 Entry = NewQueueHead->LinkedQueueHeads.Blink;
568 ASSERT(Entry != NewQueueHead->LinkedQueueHeads.Flink);
569 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
570
571 //
572 // Set the LinkPointer and Flink
573 //
574 LastQueueHead->HorizontalLinkPointer = HeadQueueHead->PhysicalAddr | QH_TYPE_QH;
575 LastQueueHead->LinkedQueueHeads.Flink = &HeadQueueHead->LinkedQueueHeads;
576
577 //
578 // Fine the last QueueHead in HeadQueueHead
579 //
580 Entry = HeadQueueHead->LinkedQueueHeads.Blink;
581 HeadQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
582 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
583 LastQueueHead->LinkedQueueHeads.Flink = &NewQueueHead->LinkedQueueHeads;
584 LastQueueHead->HorizontalLinkPointer = NewQueueHead->PhysicalAddr | QH_TYPE_QH;
585 }
586
587 //
588 // UnlinkQueueHeadChain - Unlinks a list number of QueueHeads from HeadQueueHead list, updating HorizontalLinkPointer.
589 // returns the chain of QueueHeads removed from HeadQueueHead.
590 //
591 PQUEUE_HEAD
592 CUSBQueue::UnlinkQueueHeadChain(
593 PQUEUE_HEAD HeadQueueHead,
594 ULONG Count)
595 {
596 PQUEUE_HEAD LastQueueHead, FirstQueueHead;
597 PLIST_ENTRY Entry;
598 ULONG Index;
599
600 //
601 // Find the last QueueHead in NewQueueHead
602 //
603 Entry = &HeadQueueHead->LinkedQueueHeads;
604 FirstQueueHead = CONTAINING_RECORD(Entry->Flink, QUEUE_HEAD, LinkedQueueHeads);
605
606 for (Index = 0; Index < Count; Index++)
607 {
608 Entry = Entry->Flink;
609
610 if (Entry == &HeadQueueHead->LinkedQueueHeads)
611 {
612 DPRINT1("Warning; Only %lu QueueHeads in HeadQueueHead\n", Index);
613 Count = Index + 1;
614 break;
615 }
616 }
617
618 LastQueueHead = CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
619 HeadQueueHead->LinkedQueueHeads.Flink = LastQueueHead->LinkedQueueHeads.Flink;
620 if (Count + 1 == Index)
621 {
622 HeadQueueHead->LinkedQueueHeads.Blink = &HeadQueueHead->LinkedQueueHeads;
623 }
624 else
625 HeadQueueHead->LinkedQueueHeads.Blink = LastQueueHead->LinkedQueueHeads.Flink;
626
627 FirstQueueHead->LinkedQueueHeads.Blink = &LastQueueHead->LinkedQueueHeads;
628 LastQueueHead->LinkedQueueHeads.Flink = &FirstQueueHead->LinkedQueueHeads;
629 LastQueueHead->HorizontalLinkPointer = TERMINATE_POINTER;
630 return FirstQueueHead;
631 }
632
633 VOID
634 CUSBQueue::QueueHeadInterruptCompletion(
635 PQUEUE_HEAD QueueHead,
636 NTSTATUS Status)
637 {
638 PEHCIREQUEST Request;
639 UCHAR Interval, IntervalIndex;
640 PQUEUE_HEAD InterruptQueueHead, LastQueueHead = NULL;
641
642
643 //
644 // sanity check
645 //
646 PC_ASSERT(QueueHead->Request);
647
648 //
649 // get IUSBRequest interface
650 //
651 Request = (PEHCIREQUEST)QueueHead->Request;
652
653 // get interval
654 Interval = Request->GetInterval();
655
656 // sanitize interval
657 Interval = max(1, Interval);
658
659 // get interval index
660 IntervalIndex = GetIntervalIndex(Interval);
661
662 // get interrupt queue head from index
663 InterruptQueueHead = m_InterruptQueueHeads[IntervalIndex];
664
665 while(InterruptQueueHead != NULL)
666 {
667 if (InterruptQueueHead == QueueHead)
668 break;
669
670 // move to next queue head
671 LastQueueHead = InterruptQueueHead;
672 InterruptQueueHead = (PQUEUE_HEAD)InterruptQueueHead->NextQueueHead;
673 }
674
675 if (InterruptQueueHead != QueueHead)
676 {
677 // queue head not in list
678 ASSERT(FALSE);
679 return;
680 }
681
682 // now unlink queue head
683 LastQueueHead->HorizontalLinkPointer = QueueHead->HorizontalLinkPointer;
684 LastQueueHead->NextQueueHead = QueueHead->NextQueueHead;
685
686 DPRINT1("Periodic QueueHead %p Addr %x unlinked\n", QueueHead, QueueHead->PhysicalAddr);
687
688 // insert into completed list
689 InsertTailList(&m_CompletedRequestAsyncList, &QueueHead->LinkedQueueHeads);
690 }
691
692
693
694 VOID
695 CUSBQueue::QueueHeadCompletion(
696 PQUEUE_HEAD CurrentQH,
697 NTSTATUS Status)
698 {
699 //
700 // now unlink the queue head
701 // FIXME: implement chained queue heads
702 // no need to acquire locks, as it is called with locks held
703 //
704
705 //
706 // unlink queue head
707 //
708 UnlinkQueueHead(CurrentQH);
709
710 //
711 // insert into completed list
712 //
713 InsertTailList(&m_CompletedRequestAsyncList, &CurrentQH->LinkedQueueHeads);
714 }
715
716
717 VOID
718 CUSBQueue::ProcessPeriodicSchedule(
719 IN NTSTATUS Status,
720 OUT PULONG ShouldRingDoorBell)
721 {
722 KIRQL OldLevel;
723 PLIST_ENTRY Entry;
724 PQUEUE_HEAD QueueHead;
725 PEHCIREQUEST Request;
726 BOOLEAN IsQueueHeadComplete;
727
728 //
729 // lock completed async list
730 //
731 KeAcquireSpinLock(m_Lock, &OldLevel);
732
733 //
734 // walk async list
735 //
736 ASSERT(AsyncListQueueHead);
737 Entry = m_PeriodicQueueHeads.Flink;
738
739 while(Entry != &m_PeriodicQueueHeads)
740 {
741 //
742 // get queue head structure
743 //
744 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
745 ASSERT(QueueHead);
746
747 //
748 // sanity check
749 //
750 PC_ASSERT(QueueHead->Request);
751
752 //
753 // get IUSBRequest interface
754 //
755 Request = (PEHCIREQUEST)QueueHead->Request;
756
757 //
758 // move to next entry
759 //
760 Entry = Entry->Flink;
761
762 //
763 // check if queue head is complete
764 //
765 IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
766
767 DPRINT("Request %p QueueHead %p Complete %c\n", Request, QueueHead, IsQueueHeadComplete);
768
769 //
770 // check if queue head is complete
771 //
772 if (IsQueueHeadComplete)
773 {
774 //
775 // current queue head is complete
776 //
777 QueueHeadInterruptCompletion(QueueHead, Status);
778
779 //
780 // ring door bell is going to be necessary
781 //
782 *ShouldRingDoorBell = TRUE;
783 }
784 }
785
786 //
787 // release lock
788 //
789 KeReleaseSpinLock(m_Lock, OldLevel);
790
791 }
792
793 VOID
794 CUSBQueue::ProcessAsyncList(
795 IN NTSTATUS Status,
796 OUT PULONG ShouldRingDoorBell)
797 {
798 KIRQL OldLevel;
799 PLIST_ENTRY Entry;
800 PQUEUE_HEAD QueueHead;
801 PEHCIREQUEST Request;
802 BOOLEAN IsQueueHeadComplete;
803
804 //
805 // lock completed async list
806 //
807 KeAcquireSpinLock(m_Lock, &OldLevel);
808
809 //
810 // walk async list
811 //
812 ASSERT(AsyncListQueueHead);
813 Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
814
815 while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
816 {
817 //
818 // get queue head structure
819 //
820 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
821 ASSERT(QueueHead);
822
823 //
824 // sanity check
825 //
826 PC_ASSERT(QueueHead->Request);
827
828 //
829 // get IUSBRequest interface
830 //
831 Request = (PEHCIREQUEST)QueueHead->Request;
832
833 //
834 // move to next entry
835 //
836 Entry = Entry->Flink;
837
838 //
839 // check if queue head is complete
840 //
841 IsQueueHeadComplete = Request->IsQueueHeadComplete(QueueHead);
842
843 DPRINT("Request %p QueueHead %p Complete %c\n", Request, QueueHead, IsQueueHeadComplete);
844
845 //
846 // check if queue head is complete
847 //
848 if (IsQueueHeadComplete)
849 {
850 //
851 // current queue head is complete
852 //
853 QueueHeadCompletion(QueueHead, Status);
854
855 //
856 // ring door bell is going to be necessary
857 //
858 *ShouldRingDoorBell = TRUE;
859 }
860 }
861
862 //
863 // release lock
864 //
865 KeReleaseSpinLock(m_Lock, OldLevel);
866 }
867
868
869 VOID
870 STDMETHODCALLTYPE
871 CUSBQueue::InterruptCallback(
872 IN NTSTATUS Status,
873 OUT PULONG ShouldRingDoorBell)
874 {
875 DPRINT("CUSBQueue::InterruptCallback\n");
876
877 //
878 // process periodic schedule
879 //
880 ProcessPeriodicSchedule(Status, ShouldRingDoorBell);
881
882 //
883 // iterate asynchronous list
884 //
885 *ShouldRingDoorBell = FALSE;
886 ProcessAsyncList(Status, ShouldRingDoorBell);
887 }
888
889 VOID
890 CUSBQueue::QueueHeadCleanup(
891 PQUEUE_HEAD CurrentQH)
892 {
893 PQUEUE_HEAD NewQueueHead;
894 PEHCIREQUEST Request;
895 BOOLEAN ShouldReleaseWhenDone;
896 USBD_STATUS UrbStatus;
897 KIRQL OldLevel;
898
899 //
900 // sanity checks
901 //
902 PC_ASSERT(CurrentQH->Token.Bits.Active == 0);
903 PC_ASSERT(CurrentQH->Request);
904
905
906 //
907 // get request
908 //
909 Request = (PEHCIREQUEST)CurrentQH->Request;
910
911 //
912 // sanity check
913 //
914 PC_ASSERT(Request);
915
916 //
917 // check if the queue head was completed with errors
918 //
919 if (CurrentQH->Token.Bits.Halted)
920 {
921 if (CurrentQH->Token.Bits.DataBufferError)
922 {
923 //
924 // data buffer error
925 //
926 UrbStatus = USBD_STATUS_DATA_BUFFER_ERROR;
927 }
928 else if (CurrentQH->Token.Bits.BabbleDetected)
929 {
930 //
931 // babble detected
932 //
933 UrbStatus = USBD_STATUS_BABBLE_DETECTED;
934 }
935 else
936 {
937 //
938 // stall pid
939 //
940 UrbStatus = USBD_STATUS_STALL_PID;
941 }
942 }
943 else
944 {
945 //
946 // well done ;)
947 //
948 UrbStatus = USBD_STATUS_SUCCESS;
949 }
950
951 //
952 // Check if the transfer was completed and if UrbStatus is ok
953 //
954 if ((Request->IsRequestComplete() == FALSE) && (UrbStatus == USBD_STATUS_SUCCESS))
955 {
956 //
957 // request is incomplete, get new queue head
958 //
959 if (Request->GetQueueHead(&NewQueueHead) == STATUS_SUCCESS)
960 {
961 //
962 // let IUSBRequest free the queue head
963 //
964 Request->FreeQueueHead(CurrentQH);
965
966 //
967 // first acquire request lock
968 //
969 KeAcquireSpinLock(m_Lock, &OldLevel);
970
971 //
972 // add to pending list
973 //
974 InsertTailList(&m_PendingRequestAsyncList, &NewQueueHead->LinkedQueueHeads);
975
976 //
977 // release queue head
978 //
979 KeReleaseSpinLock(m_Lock, OldLevel);
980
981 //
982 // Done for now
983 //
984 return;
985 }
986 DPRINT1("Unable to create a new QueueHead\n");
987 //ASSERT(FALSE);
988
989 //
990 // Else there was a problem
991 // FIXME: Find better return
992 UrbStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
993 }
994
995 if (UrbStatus != USBD_STATUS_SUCCESS)
996 {
997 DPRINT1("URB failed with status 0x%x\n", UrbStatus);
998 //PC_ASSERT(FALSE);
999 }
1000
1001 //
1002 // notify request that a transfer has completed
1003 //
1004 Request->CompletionCallback(UrbStatus != USBD_STATUS_SUCCESS ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS,
1005 UrbStatus,
1006 CurrentQH);
1007
1008 //
1009 // let IUSBRequest free the queue head
1010 //
1011 Request->FreeQueueHead(CurrentQH);
1012
1013 //
1014 // check if we should release request when done
1015 //
1016 ShouldReleaseWhenDone = Request->ShouldReleaseRequestAfterCompletion();
1017
1018 //
1019 // release reference when the request was added
1020 //
1021 Request->Release();
1022
1023 //
1024 // check if the operation was asynchronous
1025 //
1026 if (ShouldReleaseWhenDone)
1027 {
1028 //
1029 // release outstanding reference count
1030 //
1031 Request->Release();
1032 }
1033
1034 //
1035 // request is now released
1036 //
1037 }
1038
1039 VOID
1040 STDMETHODCALLTYPE
1041 CUSBQueue::CompleteAsyncRequests()
1042 {
1043 KIRQL OldLevel;
1044 PLIST_ENTRY Entry;
1045 PQUEUE_HEAD CurrentQH;
1046
1047 DPRINT("CUSBQueue::CompleteAsyncRequests\n");
1048
1049 //
1050 // first acquire request lock
1051 //
1052 KeAcquireSpinLock(m_Lock, &OldLevel);
1053
1054 //
1055 // the list should not be empty
1056 //
1057 PC_ASSERT(!IsListEmpty(&m_CompletedRequestAsyncList));
1058
1059 while(!IsListEmpty(&m_CompletedRequestAsyncList))
1060 {
1061 //
1062 // remove first entry
1063 //
1064 Entry = RemoveHeadList(&m_CompletedRequestAsyncList);
1065
1066 //
1067 // get queue head structure
1068 //
1069 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
1070
1071 //
1072 // release lock
1073 //
1074 KeReleaseSpinLock(m_Lock, OldLevel);
1075
1076 //
1077 // complete request now
1078 //
1079 QueueHeadCleanup(CurrentQH);
1080
1081 //
1082 // first acquire request lock
1083 //
1084 KeAcquireSpinLock(m_Lock, &OldLevel);
1085 }
1086
1087 //
1088 // is there a pending async entry
1089 //
1090 if (!IsListEmpty(&m_PendingRequestAsyncList))
1091 {
1092 //
1093 // remove first entry
1094 //
1095 Entry = RemoveHeadList(&m_PendingRequestAsyncList);
1096
1097 //
1098 // get queue head structure
1099 //
1100 CurrentQH = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
1101
1102 //
1103 // Add it to the AsyncList list
1104 //
1105 LinkQueueHead(AsyncListQueueHead, CurrentQH);
1106 }
1107
1108 //
1109 // release lock
1110 //
1111 KeReleaseSpinLock(m_Lock, OldLevel);
1112 }
1113
1114 NTSTATUS
1115 STDMETHODCALLTYPE
1116 CUSBQueue::AbortDevicePipe(
1117 IN UCHAR DeviceAddress,
1118 IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor)
1119 {
1120 KIRQL OldLevel;
1121 PLIST_ENTRY Entry;
1122 PQUEUE_HEAD QueueHead;
1123 LIST_ENTRY ListHead;
1124
1125 //
1126 // lock completed async list
1127 //
1128 KeAcquireSpinLock(m_Lock, &OldLevel);
1129
1130 DPRINT1("AbortDevicePipe DeviceAddress %x EndpointDescriptor %p Addr %x\n", DeviceAddress, EndpointDescriptor, EndpointDescriptor->bEndpointAddress);
1131
1132 //
1133 // init list head
1134 //
1135 InitializeListHead(&ListHead);
1136
1137
1138 //
1139 // walk async list
1140 //
1141 ASSERT(AsyncListQueueHead);
1142 Entry = AsyncListQueueHead->LinkedQueueHeads.Flink;
1143
1144 while(Entry != &AsyncListQueueHead->LinkedQueueHeads)
1145 {
1146 //
1147 // get queue head structure
1148 //
1149 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
1150 ASSERT(QueueHead);
1151
1152 //
1153 // move to next entry
1154 //
1155 Entry = Entry->Flink;
1156
1157 if (QueueHead->EndPointCharacteristics.DeviceAddress == DeviceAddress &&
1158 QueueHead->EndPointCharacteristics.EndPointNumber == (EndpointDescriptor->bEndpointAddress & 0xF) && QueueHead->Token.Bits.Halted)
1159 {
1160 //
1161 // unlink queue head
1162 //
1163 UnlinkQueueHead(QueueHead);
1164
1165 //
1166 // add to temp list
1167 //
1168 InsertTailList(&ListHead, &QueueHead->LinkedQueueHeads);
1169 }
1170 }
1171
1172 //
1173 // release lock
1174 //
1175 KeReleaseSpinLock(m_Lock, OldLevel);
1176
1177 while(!IsListEmpty(&ListHead))
1178 {
1179 //
1180 // remove entry
1181 //
1182 Entry = RemoveHeadList(&ListHead);
1183
1184 //
1185 // get queue head structure
1186 //
1187 QueueHead = (PQUEUE_HEAD)CONTAINING_RECORD(Entry, QUEUE_HEAD, LinkedQueueHeads);
1188 ASSERT(QueueHead);
1189
1190 //
1191 // cleanup queue head
1192 //
1193 QueueHeadCleanup(QueueHead);
1194 }
1195 return STATUS_SUCCESS;
1196 }
1197
1198
1199 NTSTATUS
1200 NTAPI
1201 CreateUSBQueue(
1202 PUSBQUEUE *OutUsbQueue)
1203 {
1204 PUSBQUEUE This;
1205
1206 //
1207 // allocate controller
1208 //
1209 This = new(NonPagedPool, TAG_USBEHCI) CUSBQueue(0);
1210 if (!This)
1211 {
1212 //
1213 // failed to allocate
1214 //
1215 return STATUS_INSUFFICIENT_RESOURCES;
1216 }
1217
1218 //
1219 // add reference count
1220 //
1221 This->AddRef();
1222
1223 //
1224 // return result
1225 //
1226 *OutUsbQueue = (PUSBQUEUE)This;
1227
1228 //
1229 // done
1230 //
1231 return STATUS_SUCCESS;
1232 }
1233