TianoCore EDK2 master
Loading...
Searching...
No Matches
UhciSched.c
Go to the documentation of this file.
1
10#include "Uhci.h"
11
24 IN USB_HC_DEV *Uhc
25 )
26{
27 EFI_PHYSICAL_ADDRESS MappedAddr;
28 EFI_STATUS Status;
29 VOID *Buffer;
30 VOID *Mapping;
31 UINTN Pages;
32 UINTN Bytes;
33 UINTN Index;
35
36 //
37 // The Frame List is a common buffer that will be
38 // accessed by both the cpu and the usb bus master
39 // at the same time. The Frame List ocupies 4K bytes,
40 // and must be aligned on 4-Kbyte boundaries.
41 //
42 Bytes = 4096;
43 Pages = EFI_SIZE_TO_PAGES (Bytes);
44
45 Status = Uhc->PciIo->AllocateBuffer (
46 Uhc->PciIo,
49 Pages,
50 &Buffer,
51 0
52 );
53
54 if (EFI_ERROR (Status)) {
55 return EFI_OUT_OF_RESOURCES;
56 }
57
58 Status = Uhc->PciIo->Map (
59 Uhc->PciIo,
61 Buffer,
62 &Bytes,
63 &MappedAddr,
64 &Mapping
65 );
66
67 if (EFI_ERROR (Status) || (Bytes != 4096)) {
68 Status = EFI_UNSUPPORTED;
69 goto ON_ERROR;
70 }
71
72 Uhc->FrameBase = (UINT32 *)(UINTN)Buffer;
73 Uhc->FrameMapping = Mapping;
74
75 //
76 // Tell the Host Controller where the Frame List lies,
77 // by set the Frame List Base Address Register.
78 //
79 UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *)(UINTN)MappedAddr);
80
81 //
82 // Allocate the QH used by sync interrupt/control/bulk transfer.
83 // FS ctrl/bulk queue head is set to loopback so additional BW
84 // can be reclaimed. Notice, LS don't support bulk transfer and
85 // also doesn't support BW reclamation.
86 //
87 Uhc->SyncIntQh = UhciCreateQh (Uhc, 1);
88 Uhc->CtrlQh = UhciCreateQh (Uhc, 1);
89 Uhc->BulkQh = UhciCreateQh (Uhc, 1);
90
91 if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) {
92 Uhc->PciIo->Unmap (Uhc->PciIo, Mapping);
93 Status = EFI_OUT_OF_RESOURCES;
94 goto ON_ERROR;
95 }
96
97 //
98 // +-------------+
99 // | |
100 // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
101 // Each frame entry is linked to this sequence of QH. These QH
102 // will remain on the schedul, never got removed
103 //
104 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_HW));
105 Uhc->SyncIntQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
106 Uhc->SyncIntQh->NextQh = Uhc->CtrlQh;
107
108 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_HW));
109 Uhc->CtrlQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
110 Uhc->CtrlQh->NextQh = Uhc->BulkQh;
111
112 //
113 // Some old platform such as Intel's Tiger 4 has a difficult time
114 // in supporting the full speed bandwidth reclamation in the previous
115 // mentioned form. Most new platforms don't suffer it.
116 //
117 Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
118
119 Uhc->BulkQh->NextQh = NULL;
120
121 Uhc->FrameBaseHostAddr = AllocateZeroPool (4096);
122 if (Uhc->FrameBaseHostAddr == NULL) {
123 Status = EFI_OUT_OF_RESOURCES;
124 goto ON_ERROR;
125 }
126
127 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_HW));
128 for (Index = 0; Index < UHCI_FRAME_NUM; Index++) {
129 Uhc->FrameBase[Index] = QH_HLINK (PhyAddr, FALSE);
130 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Uhc->SyncIntQh;
131 }
132
133 return EFI_SUCCESS;
134
135ON_ERROR:
136 if (Uhc->SyncIntQh != NULL) {
137 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
138 }
139
140 if (Uhc->CtrlQh != NULL) {
141 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
142 }
143
144 if (Uhc->BulkQh != NULL) {
145 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
146 }
147
148 Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer);
149 return Status;
150}
151
158VOID
160 IN USB_HC_DEV *Uhc
161 )
162{
163 //
164 // Unmap the common buffer for framelist entry,
165 // and free the common buffer.
166 // Uhci's frame list occupy 4k memory.
167 //
168 Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping);
169
170 Uhc->PciIo->FreeBuffer (
171 Uhc->PciIo,
172 EFI_SIZE_TO_PAGES (4096),
173 (VOID *)Uhc->FrameBase
174 );
175
176 if (Uhc->FrameBaseHostAddr != NULL) {
177 FreePool (Uhc->FrameBaseHostAddr);
178 }
179
180 if (Uhc->SyncIntQh != NULL) {
181 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
182 }
183
184 if (Uhc->CtrlQh != NULL) {
185 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
186 }
187
188 if (Uhc->BulkQh != NULL) {
189 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
190 }
191
192 Uhc->FrameBase = NULL;
193 Uhc->FrameBaseHostAddr = NULL;
194 Uhc->SyncIntQh = NULL;
195 Uhc->CtrlQh = NULL;
196 Uhc->BulkQh = NULL;
197}
198
208UINTN
210 IN UINTN Interval
211 )
212{
213 UINTN BitCount;
214
215 ASSERT (Interval != 0);
216
217 //
218 // To safeguard RELEASE mode wherein ASSERT is effectively not there,
219 // if inadvertently Interval is still 0 here, treat it the same as 1.
220 //
221 if (Interval == 0) {
222 Interval = 1;
223 }
224
225 //
226 // Find the index (1 based) of the highest non-zero bit
227 //
228 BitCount = 0;
229
230 while (Interval != 0) {
231 Interval >>= 1;
232 BitCount++;
233 }
234
235 return (UINTN)1 << (BitCount - 1);
236}
237
246VOID
248 USB_HC_DEV *Uhc,
249 UHCI_QH_SW *Qh
250 )
251{
252 UINTN Index;
253 UHCI_QH_SW *Prev;
254 UHCI_QH_SW *Next;
255 EFI_PHYSICAL_ADDRESS PhyAddr;
256 EFI_PHYSICAL_ADDRESS QhPciAddr;
257
258 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
259
260 QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW));
261
262 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
263 //
264 // First QH can't be NULL because we always keep static queue
265 // heads on the frame list
266 //
267 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
268 Next = (UHCI_QH_SW *)(UINTN)Uhc->FrameBaseHostAddr[Index];
269 Prev = NULL;
270
271 //
272 // Now, insert the queue head (Qh) into this frame:
273 // 1. Find a queue head with the same poll interval, just insert
274 // Qh after this queue head, then we are done.
275 //
276 // 2. Find the position to insert the queue head into:
277 // Previous head's interval is bigger than Qh's
278 // Next head's interval is less than Qh's
279 // Then, insert the Qh between then
280 //
281 // This method is very much the same as that used by EHCI.
282 // Because each QH's interval is round down to 2^n, poll
283 // rate is correct.
284 //
285 while (Next->Interval > Qh->Interval) {
286 Prev = Next;
287 Next = Next->NextQh;
288 ASSERT (Next != NULL);
289 }
290
291 //
292 // The entry may have been linked into the frame by early insertation.
293 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
294 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
295 // It isn't necessary to compare all the QH with the same interval to
296 // Qh. This is because if there is other QH with the same interval, Qh
297 // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
298 // impossible (Next == Qh)
299 //
300 if (Next == Qh) {
301 continue;
302 }
303
304 if (Next->Interval == Qh->Interval) {
305 //
306 // If there is a QH with the same interval, it locates at
307 // FrameBase[0], and we can simply insert it after this QH. We
308 // are all done.
309 //
310 ASSERT ((Index == 0) && (Qh->NextQh == NULL));
311
312 Prev = Next;
313 Next = Next->NextQh;
314
315 Qh->NextQh = Next;
316 Prev->NextQh = Qh;
317
318 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
319
320 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
321 break;
322 }
323
324 //
325 // OK, find the right position, insert it in. If Qh's next
326 // link has already been set, it is in position. This is
327 // guarranted by 2^n polling interval.
328 //
329 if (Qh->NextQh == NULL) {
330 Qh->NextQh = Next;
331 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW));
332 Qh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
333 }
334
335 if (Prev == NULL) {
336 Uhc->FrameBase[Index] = QH_HLINK (QhPciAddr, FALSE);
337 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh;
338 } else {
339 Prev->NextQh = Qh;
340 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
341 }
342 }
343}
344
354VOID
356 USB_HC_DEV *Uhc,
357 UHCI_QH_SW *Qh
358 )
359{
360 UINTN Index;
361 UHCI_QH_SW *Prev;
362 UHCI_QH_SW *This;
363
364 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
365
366 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
367 //
368 // Frame link can't be NULL because we always keep static
369 // queue heads on the frame list
370 //
371 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
372 This = (UHCI_QH_SW *)(UINTN)Uhc->FrameBaseHostAddr[Index];
373 Prev = NULL;
374
375 //
376 // Walk through the frame's QH list to find the
377 // queue head to remove
378 //
379 while ((This != NULL) && (This != Qh)) {
380 Prev = This;
381 This = This->NextQh;
382 }
383
384 //
385 // Qh may have already been unlinked from this frame
386 // by early action.
387 //
388 if (This == NULL) {
389 continue;
390 }
391
392 if (Prev == NULL) {
393 //
394 // Qh is the first entry in the frame
395 //
396 Uhc->FrameBase[Index] = Qh->QhHw.HorizonLink;
397 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh->NextQh;
398 } else {
399 Prev->NextQh = Qh->NextQh;
400 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
401 }
402 }
403}
404
416BOOLEAN
418 IN USB_HC_DEV *Uhc,
419 IN UHCI_TD_SW *Td,
420 IN BOOLEAN IsLow,
421 OUT UHCI_QH_RESULT *QhResult
422 )
423{
424 UINTN Len;
425 UINT8 State;
426 UHCI_TD_HW *TdHw;
427 BOOLEAN Finished;
428
429 Finished = TRUE;
430
431 //
432 // Initialize the data toggle to that of the first
433 // TD. The next toggle to use is either:
434 // 1. first TD's toggle if no TD is executed OK
435 // 2. the next toggle of last executed-OK TD
436 //
437 QhResult->Result = EFI_USB_NOERROR;
438 QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
439 QhResult->Complete = 0;
440
441 while (Td != NULL) {
442 TdHw = &Td->TdHw;
443 State = (UINT8)TdHw->Status;
444
445 //
446 // UHCI will set STALLED bit when it abort the execution
447 // of TD list. There are several reasons:
448 // 1. BABBLE error happened
449 // 2. Received a STALL response
450 // 3. Error count decreased to zero.
451 //
452 // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
453 // bits when corresponding conditions happen. But these
454 // conditions are not deadly, that is a TD can successfully
455 // completes even these bits are set. But it is likely that
456 // upper layer won't distinguish these condtions. So, only
457 // set these bits when TD is actually halted.
458 //
459 if ((State & USBTD_STALLED) != 0) {
460 if ((State & USBTD_BABBLE) != 0) {
461 QhResult->Result |= EFI_USB_ERR_BABBLE;
462 } else if (TdHw->ErrorCount != 0) {
463 QhResult->Result |= EFI_USB_ERR_STALL;
464 }
465
466 if ((State & USBTD_CRC) != 0) {
467 QhResult->Result |= EFI_USB_ERR_CRC;
468 }
469
470 if ((State & USBTD_BUFFERR) != 0) {
471 QhResult->Result |= EFI_USB_ERR_BUFFER;
472 }
473
474 if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) {
475 QhResult->Result |= EFI_USB_ERR_BITSTUFF;
476 }
477
478 if (TdHw->ErrorCount == 0) {
479 QhResult->Result |= EFI_USB_ERR_TIMEOUT;
480 }
481
482 Finished = TRUE;
483 goto ON_EXIT;
484 } else if ((State & USBTD_ACTIVE) != 0) {
485 //
486 // The TD is still active, no need to check further.
487 //
488 QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
489
490 Finished = FALSE;
491 goto ON_EXIT;
492 } else {
493 //
494 // Update the next data toggle, it is always the
495 // next to the last known-good TD's data toggle if
496 // any TD is executed OK
497 //
498 QhResult->NextToggle = (UINT8)(1 - (UINT8)TdHw->DataToggle);
499
500 //
501 // This TD is finished OK or met short packet read. Update the
502 // transfer length if it isn't a SETUP.
503 //
504 Len = (TdHw->ActualLen + 1) & 0x7FF;
505
506 if (TdHw->PidCode != SETUP_PACKET_ID) {
507 QhResult->Complete += Len;
508 }
509
510 //
511 // Short packet condition for full speed input TD, also
512 // terminate the transfer
513 //
514 if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
515 DEBUG ((DEBUG_VERBOSE, "UhciCheckTdStatus: short packet read occurred\n"));
516
517 Finished = TRUE;
518 goto ON_EXIT;
519 }
520 }
521
522 Td = Td->NextTd;
523 }
524
525ON_EXIT:
526 //
527 // Check whether HC is halted. Don't move this up. It must be
528 // called after data toggle is successfully updated.
529 //
530 if (!UhciIsHcWorking (Uhc->PciIo)) {
531 QhResult->Result |= EFI_USB_ERR_SYSTEM;
532 Finished = TRUE;
533 }
534
535 if (Finished) {
536 Uhc->PciIo->Flush (Uhc->PciIo);
537 }
538
540 return Finished;
541}
542
559 IN USB_HC_DEV *Uhc,
560 IN UHCI_QH_SW *Qh,
561 IN UHCI_TD_SW *Td,
562 IN UINTN TimeOut,
563 IN BOOLEAN IsLow,
564 OUT UHCI_QH_RESULT *QhResult
565 )
566{
567 UINTN Index;
568 UINTN Delay;
569 BOOLEAN Finished;
570 EFI_STATUS Status;
571 BOOLEAN InfiniteLoop;
572
573 Finished = FALSE;
574 Status = EFI_SUCCESS;
575 Delay = TimeOut * UHC_1_MILLISECOND;
576 InfiniteLoop = FALSE;
577
578 //
579 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
580 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
581 // is returned.
582 //
583 if (TimeOut == 0) {
584 InfiniteLoop = TRUE;
585 }
586
587 for (Index = 0; InfiniteLoop || (Index < Delay); Index++) {
588 Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
589
590 //
591 // Transfer is OK or some error occurred (TD inactive)
592 //
593 if (Finished) {
594 break;
595 }
596
597 gBS->Stall (UHC_1_MICROSECOND);
598 }
599
600 if (!Finished) {
601 DEBUG ((DEBUG_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut));
602 UhciDumpQh (Qh);
603 UhciDumpTds (Td);
604
605 Status = EFI_TIMEOUT;
606 } else if (QhResult->Result != EFI_USB_NOERROR) {
607 DEBUG ((DEBUG_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
608 UhciDumpQh (Qh);
609 UhciDumpTds (Td);
610
611 Status = EFI_DEVICE_ERROR;
612 }
613
614 return Status;
615}
616
626VOID
628 IN USB_HC_DEV *Uhc,
629 IN UHCI_ASYNC_REQUEST *AsyncReq,
630 IN UINT32 Result,
631 IN UINT32 NextToggle
632 )
633{
634 UHCI_QH_SW *Qh;
635 UHCI_TD_SW *FirstTd;
636 UHCI_TD_SW *Td;
637
638 Qh = AsyncReq->QhSw;
639 FirstTd = AsyncReq->FirstTd;
640
641 if (Result == EFI_USB_NOERROR) {
642 //
643 // The last transfer succeeds. Then we need to update
644 // the Qh and Td for next round of transfer.
645 // 1. Update the TD's data toggle
646 // 2. Activate all the TDs
647 // 3. Link the TD to the queue head again since during
648 // execution, queue head's TD pointer is changed by
649 // hardware.
650 //
651 for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
652 Td->TdHw.DataToggle = NextToggle;
653 NextToggle ^= 1;
654 Td->TdHw.Status |= USBTD_ACTIVE;
655 }
656
657 UhciLinkTdToQh (Uhc, Qh, FirstTd);
658 return;
659 }
660}
661
684 IN USB_HC_DEV *Uhc,
685 IN UHCI_QH_SW *Qh,
686 IN UHCI_TD_SW *FirstTd,
687 IN UINT8 DevAddr,
688 IN UINT8 EndPoint,
689 IN UINTN DataLen,
690 IN UINTN Interval,
691 IN UINT8 *Data,
693 IN VOID *Context,
694 IN BOOLEAN IsLow
695 )
696{
697 UHCI_ASYNC_REQUEST *AsyncReq;
698
699 AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
700
701 if (AsyncReq == NULL) {
702 return EFI_OUT_OF_RESOURCES;
703 }
704
705 //
706 // Fill Request field. Data is allocated host memory, not mapped
707 //
708 AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE;
709 AsyncReq->DevAddr = DevAddr;
710 AsyncReq->EndPoint = EndPoint;
711 AsyncReq->DataLen = DataLen;
712 AsyncReq->Interval = UhciConvertPollRate (Interval);
713 AsyncReq->Data = Data;
714 AsyncReq->Callback = Callback;
715 AsyncReq->Context = Context;
716 AsyncReq->QhSw = Qh;
717 AsyncReq->FirstTd = FirstTd;
718 AsyncReq->IsLow = IsLow;
719
720 //
721 // Insert the new interrupt transfer to the head of the list.
722 // The interrupt transfer's monitor function scans the whole
723 // list from head to tail. The new interrupt transfer MUST be
724 // added to the head of the list.
725 //
726 InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
727
728 return EFI_SUCCESS;
729}
730
738VOID
740 IN USB_HC_DEV *Uhc,
741 IN UHCI_ASYNC_REQUEST *AsyncReq
742 )
743{
744 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
745
746 UhciDestoryTds (Uhc, AsyncReq->FirstTd);
747 UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
748
749 if (AsyncReq->Data != NULL) {
750 UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen);
751 }
752
753 gBS->FreePool (AsyncReq);
754}
755
769VOID
771 IN USB_HC_DEV *Uhc,
772 IN UHCI_ASYNC_REQUEST *AsyncReq,
773 IN BOOLEAN FreeNow
774 )
775{
776 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
777
778 RemoveEntryList (&(AsyncReq->Link));
779 UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw);
780
781 if (FreeNow) {
782 UhciFreeAsyncReq (Uhc, AsyncReq);
783 } else {
784 //
785 // To sychronize with hardware, mark the queue head as inactive
786 // then add AsyncReq to UHC's recycle list
787 //
788 AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
789 AsyncReq->Recycle = Uhc->RecycleWait;
790 Uhc->RecycleWait = AsyncReq;
791 }
792}
793
809 IN USB_HC_DEV *Uhc,
810 IN UINT8 DevAddr,
811 IN UINT8 EndPoint,
812 OUT UINT8 *Toggle
813 )
814{
815 EFI_STATUS Status;
816 UHCI_ASYNC_REQUEST *AsyncReq;
817 UHCI_QH_RESULT QhResult;
818 LIST_ENTRY *Link;
819 BOOLEAN Found;
820
821 Status = EFI_SUCCESS;
822
823 //
824 // If no asynchronous interrupt transaction exists
825 //
826 if (IsListEmpty (&(Uhc->AsyncIntList))) {
827 return EFI_SUCCESS;
828 }
829
830 //
831 // Find the asynchronous transfer to this device/endpoint pair
832 //
833 Found = FALSE;
834 Link = Uhc->AsyncIntList.ForwardLink;
835
836 do {
837 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
838 Link = Link->ForwardLink;
839
840 if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
841 Found = TRUE;
842 break;
843 }
844 } while (Link != &(Uhc->AsyncIntList));
845
846 if (!Found) {
847 return EFI_NOT_FOUND;
848 }
849
850 //
851 // Check the result of the async transfer then update it
852 // to get the next data toggle to use.
853 //
854 UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
855 *Toggle = QhResult.NextToggle;
856
857 //
858 // Don't release the request now, keep it to synchronize with hardware.
859 //
860 UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
861 return Status;
862}
863
880VOID
882 IN USB_HC_DEV *Uhc
883 )
884{
886 UHCI_ASYNC_REQUEST *Next;
887
888 Req = Uhc->Recycle;
889
890 while (Req != NULL) {
891 Next = Req->Recycle;
892 UhciFreeAsyncReq (Uhc, Req);
893 Req = Next;
894 }
895
896 Uhc->Recycle = Uhc->RecycleWait;
897 Uhc->RecycleWait = NULL;
898}
899
906VOID
908 IN USB_HC_DEV *Uhc
909 )
910{
911 LIST_ENTRY *Head;
912 UHCI_ASYNC_REQUEST *AsyncReq;
913
914 //
915 // Call UhciRecycleAsyncReq twice. The requests on Recycle
916 // will be released at the first call; The requests on
917 // RecycleWait will be released at the second call.
918 //
921
922 Head = &(Uhc->AsyncIntList);
923
924 if (IsListEmpty (Head)) {
925 return;
926 }
927
928 while (!IsListEmpty (Head)) {
929 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
930 UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
931 }
932}
933
941VOID
942EFIAPI
944 IN EFI_EVENT Event,
945 IN VOID *Context
946 )
947{
948 UHCI_ASYNC_REQUEST *AsyncReq;
949 LIST_ENTRY *Link;
950 USB_HC_DEV *Uhc;
951 VOID *Data;
952 BOOLEAN Finished;
953 UHCI_QH_RESULT QhResult;
954
955 Uhc = (USB_HC_DEV *)Context;
956
957 //
958 // Recycle the asynchronous requests expired, and promote
959 // requests waiting to be recycled the next time when this
960 // timer expires
961 //
963
964 if (IsListEmpty (&(Uhc->AsyncIntList))) {
965 return;
966 }
967
968 //
969 // This loop must be delete safe
970 //
971 Link = Uhc->AsyncIntList.ForwardLink;
972
973 do {
974 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
975 Link = Link->ForwardLink;
976
977 Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
978
979 if (!Finished) {
980 continue;
981 }
982
983 //
984 // Copy the data to temporary buffer if there are some
985 // data transferred. We may have zero-length packet.
986 // Make sure the data received from HW is no more than expected.
987 //
988 Data = NULL;
989
990 if ((QhResult.Complete != 0) && (QhResult.Complete <= AsyncReq->DataLen)) {
991 Data = AllocatePool (QhResult.Complete);
992
993 if (Data == NULL) {
994 return;
995 }
996
997 CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
998 }
999
1000 UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle);
1001
1002 //
1003 // Now, either transfer is SUCCESS or met errors since
1004 // we have skipped to next transfer earlier if current
1005 // transfer is still active.
1006 //
1007 if (QhResult.Result == EFI_USB_NOERROR) {
1008 AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
1009 } else {
1010 //
1011 // Leave error recovery to its related device driver.
1012 // A common case of the error recovery is to re-submit
1013 // the interrupt transfer. When an interrupt transfer
1014 // is re-submitted, its position in the linked list is
1015 // changed. It is inserted to the head of the linked
1016 // list, while this function scans the whole list from
1017 // head to tail. Thus, the re-submitted interrupt transfer's
1018 // callback function will not be called again in this round.
1019 //
1020 AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
1021 }
1022
1023 if (Data != NULL) {
1024 gBS->FreePool (Data);
1025 }
1026 } while (Link != &(Uhc->AsyncIntList));
1027}
UINT64 UINTN
BOOLEAN EFIAPI IsListEmpty(IN CONST LIST_ENTRY *ListHead)
Definition: LinkedList.c:403
LIST_ENTRY *EFIAPI InsertHeadList(IN OUT LIST_ENTRY *ListHead, IN OUT LIST_ENTRY *Entry)
Definition: LinkedList.c:218
LIST_ENTRY *EFIAPI RemoveEntryList(IN CONST LIST_ENTRY *Entry)
Definition: LinkedList.c:590
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
EFI_PHYSICAL_ADDRESS UsbHcGetPciAddressForHostMem(IN USBHC_MEM_POOL *Pool, IN VOID *Mem, IN UINTN Size)
Definition: UsbHcMem.c:223
VOID UsbHcFreeMem(IN USBHC_MEM_POOL *Pool, IN VOID *Mem, IN UINTN Size)
Definition: UsbHcMem.c:493
VOID *EFIAPI AllocateZeroPool(IN UINTN AllocationSize)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:434
@ EfiPciIoOperationBusMasterCommonBuffer
Definition: PciIo.h:90
EFI_STATUS(EFIAPI * EFI_ASYNC_USB_TRANSFER_CALLBACK)(IN VOID *Data, IN UINTN DataLength, IN VOID *Context, IN UINT32 Status)
Definition: UsbIo.h:80
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
@ EfiBootServicesData
@ AllocateAnyPages
Definition: UefiSpec.h:33
VOID UhciDumpQh(IN UHCI_QH_SW *QhSw)
Definition: UhciDebug.c:19
VOID UhciDumpTds(IN UHCI_TD_SW *TdSw)
Definition: UhciDebug.c:38
VOID UhciDestoryTds(IN USB_HC_DEV *Uhc, IN UHCI_TD_SW *FirstTd)
Definition: UhciQueue.c:222
VOID UhciLinkTdToQh(IN USB_HC_DEV *Uhc, IN UHCI_QH_SW *Qh, IN UHCI_TD_SW *Td)
Definition: UhciQueue.c:152
UHCI_QH_SW * UhciCreateQh(IN USB_HC_DEV *Uhc, IN UINTN Interval)
Definition: UhciQueue.c:249
BOOLEAN UhciIsHcWorking(IN EFI_PCI_IO_PROTOCOL *PciIo)
Definition: UhciReg.c:195
VOID UhciSetFrameListBaseAddr(IN EFI_PCI_IO_PROTOCOL *PciIo, IN VOID *Addr)
Definition: UhciReg.c:220
VOID UhciAckAllInterrupt(IN USB_HC_DEV *Uhc)
Definition: UhciReg.c:131
VOID UhciFreeAsyncReq(IN USB_HC_DEV *Uhc, IN UHCI_ASYNC_REQUEST *AsyncReq)
Definition: UhciSched.c:739
EFI_STATUS UhciInitFrameList(IN USB_HC_DEV *Uhc)
Definition: UhciSched.c:23
EFI_STATUS UhciExecuteTransfer(IN USB_HC_DEV *Uhc, IN UHCI_QH_SW *Qh, IN UHCI_TD_SW *Td, IN UINTN TimeOut, IN BOOLEAN IsLow, OUT UHCI_QH_RESULT *QhResult)
Definition: UhciSched.c:558
VOID UhciFreeAllAsyncReq(IN USB_HC_DEV *Uhc)
Definition: UhciSched.c:907
VOID EFIAPI UhciMonitorAsyncReqList(IN EFI_EVENT Event, IN VOID *Context)
Definition: UhciSched.c:943
BOOLEAN UhciCheckTdStatus(IN USB_HC_DEV *Uhc, IN UHCI_TD_SW *Td, IN BOOLEAN IsLow, OUT UHCI_QH_RESULT *QhResult)
Definition: UhciSched.c:417
VOID UhciRecycleAsyncReq(IN USB_HC_DEV *Uhc)
Definition: UhciSched.c:881
VOID UhciLinkQhToFrameList(USB_HC_DEV *Uhc, UHCI_QH_SW *Qh)
Definition: UhciSched.c:247
VOID UhciUpdateAsyncReq(IN USB_HC_DEV *Uhc, IN UHCI_ASYNC_REQUEST *AsyncReq, IN UINT32 Result, IN UINT32 NextToggle)
Definition: UhciSched.c:627
VOID UhciDestoryFrameList(IN USB_HC_DEV *Uhc)
Definition: UhciSched.c:159
VOID UhciUnlinkAsyncReq(IN USB_HC_DEV *Uhc, IN UHCI_ASYNC_REQUEST *AsyncReq, IN BOOLEAN FreeNow)
Definition: UhciSched.c:770
EFI_STATUS UhciRemoveAsyncReq(IN USB_HC_DEV *Uhc, IN UINT8 DevAddr, IN UINT8 EndPoint, OUT UINT8 *Toggle)
Definition: UhciSched.c:808
UINTN UhciConvertPollRate(IN UINTN Interval)
Definition: UhciSched.c:209
EFI_STATUS UhciCreateAsyncReq(IN USB_HC_DEV *Uhc, IN UHCI_QH_SW *Qh, IN UHCI_TD_SW *FirstTd, IN UINT8 DevAddr, IN UINT8 EndPoint, IN UINTN DataLen, IN UINTN Interval, IN UINT8 *Data, IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback, IN VOID *Context, IN BOOLEAN IsLow)
Definition: UhciSched.c:683
VOID UhciUnlinkQhFromFrameList(USB_HC_DEV *Uhc, UHCI_QH_SW *Qh)
Definition: UhciSched.c:355