]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / UhciDxe / UhciSched.c
1 /** @file
2
3 The EHCI register operation routines.
4
5 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "Uhci.h"
11
12 /**
13 Create Frame List Structure.
14
15 @param Uhc UHCI device.
16
17 @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
18 @retval EFI_UNSUPPORTED Map memory fail.
19 @retval EFI_SUCCESS Success.
20
21 **/
22 EFI_STATUS
23 UhciInitFrameList (
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;
34 EFI_PHYSICAL_ADDRESS PhyAddr;
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,
47 AllocateAnyPages,
48 EfiBootServicesData,
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,
60 EfiPciIoOperationBusMasterCommonBuffer,
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
135 ON_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
152 /**
153 Destory FrameList buffer.
154
155 @param Uhc The UHCI device.
156
157 **/
158 VOID
159 UhciDestoryFrameList (
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
199 /**
200 Convert the poll rate to the maxium 2^n that is smaller
201 than Interval.
202
203 @param Interval The poll rate to convert.
204
205 @return The converted poll rate.
206
207 **/
208 UINTN
209 UhciConvertPollRate (
210 IN UINTN Interval
211 )
212 {
213 UINTN BitCount;
214
215 ASSERT (Interval != 0);
216
217 //
218 // Find the index (1 based) of the highest non-zero bit
219 //
220 BitCount = 0;
221
222 while (Interval != 0) {
223 Interval >>= 1;
224 BitCount++;
225 }
226
227 return (UINTN)1 << (BitCount - 1);
228 }
229
230 /**
231 Link a queue head (for asynchronous interrupt transfer) to
232 the frame list.
233
234 @param Uhc The UHCI device.
235 @param Qh The queue head to link into.
236
237 **/
238 VOID
239 UhciLinkQhToFrameList (
240 USB_HC_DEV *Uhc,
241 UHCI_QH_SW *Qh
242 )
243 {
244 UINTN Index;
245 UHCI_QH_SW *Prev;
246 UHCI_QH_SW *Next;
247 EFI_PHYSICAL_ADDRESS PhyAddr;
248 EFI_PHYSICAL_ADDRESS QhPciAddr;
249
250 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
251
252 QhPciAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Qh, sizeof (UHCI_QH_HW));
253
254 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
255 //
256 // First QH can't be NULL because we always keep static queue
257 // heads on the frame list
258 //
259 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
260 Next = (UHCI_QH_SW *)(UINTN)Uhc->FrameBaseHostAddr[Index];
261 Prev = NULL;
262
263 //
264 // Now, insert the queue head (Qh) into this frame:
265 // 1. Find a queue head with the same poll interval, just insert
266 // Qh after this queue head, then we are done.
267 //
268 // 2. Find the position to insert the queue head into:
269 // Previous head's interval is bigger than Qh's
270 // Next head's interval is less than Qh's
271 // Then, insert the Qh between then
272 //
273 // This method is very much the same as that used by EHCI.
274 // Because each QH's interval is round down to 2^n, poll
275 // rate is correct.
276 //
277 while (Next->Interval > Qh->Interval) {
278 Prev = Next;
279 Next = Next->NextQh;
280 ASSERT (Next != NULL);
281 }
282
283 //
284 // The entry may have been linked into the frame by early insertation.
285 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
286 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
287 // It isn't necessary to compare all the QH with the same interval to
288 // Qh. This is because if there is other QH with the same interval, Qh
289 // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
290 // impossible (Next == Qh)
291 //
292 if (Next == Qh) {
293 continue;
294 }
295
296 if (Next->Interval == Qh->Interval) {
297 //
298 // If there is a QH with the same interval, it locates at
299 // FrameBase[0], and we can simply insert it after this QH. We
300 // are all done.
301 //
302 ASSERT ((Index == 0) && (Qh->NextQh == NULL));
303
304 Prev = Next;
305 Next = Next->NextQh;
306
307 Qh->NextQh = Next;
308 Prev->NextQh = Qh;
309
310 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
311
312 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
313 break;
314 }
315
316 //
317 // OK, find the right position, insert it in. If Qh's next
318 // link has already been set, it is in position. This is
319 // guarranted by 2^n polling interval.
320 //
321 if (Qh->NextQh == NULL) {
322 Qh->NextQh = Next;
323 PhyAddr = UsbHcGetPciAddressForHostMem (Uhc->MemPool, Next, sizeof (UHCI_QH_HW));
324 Qh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
325 }
326
327 if (Prev == NULL) {
328 Uhc->FrameBase[Index] = QH_HLINK (QhPciAddr, FALSE);
329 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh;
330 } else {
331 Prev->NextQh = Qh;
332 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
333 }
334 }
335 }
336
337 /**
338 Unlink QH from the frame list is easier: find all
339 the precedence node, and pointer there next to QhSw's
340 next.
341
342 @param Uhc The UHCI device.
343 @param Qh The queue head to unlink.
344
345 **/
346 VOID
347 UhciUnlinkQhFromFrameList (
348 USB_HC_DEV *Uhc,
349 UHCI_QH_SW *Qh
350 )
351 {
352 UINTN Index;
353 UHCI_QH_SW *Prev;
354 UHCI_QH_SW *This;
355
356 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
357
358 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
359 //
360 // Frame link can't be NULL because we always keep static
361 // queue heads on the frame list
362 //
363 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
364 This = (UHCI_QH_SW *)(UINTN)Uhc->FrameBaseHostAddr[Index];
365 Prev = NULL;
366
367 //
368 // Walk through the frame's QH list to find the
369 // queue head to remove
370 //
371 while ((This != NULL) && (This != Qh)) {
372 Prev = This;
373 This = This->NextQh;
374 }
375
376 //
377 // Qh may have already been unlinked from this frame
378 // by early action.
379 //
380 if (This == NULL) {
381 continue;
382 }
383
384 if (Prev == NULL) {
385 //
386 // Qh is the first entry in the frame
387 //
388 Uhc->FrameBase[Index] = Qh->QhHw.HorizonLink;
389 Uhc->FrameBaseHostAddr[Index] = (UINT32)(UINTN)Qh->NextQh;
390 } else {
391 Prev->NextQh = Qh->NextQh;
392 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
393 }
394 }
395 }
396
397 /**
398 Check TDs Results.
399
400 @param Uhc This UHCI device.
401 @param Td UHCI_TD_SW to check.
402 @param IsLow Is Low Speed Device.
403 @param QhResult Return the result of this TD list.
404
405 @return Whether the TD's result is finialized.
406
407 **/
408 BOOLEAN
409 UhciCheckTdStatus (
410 IN USB_HC_DEV *Uhc,
411 IN UHCI_TD_SW *Td,
412 IN BOOLEAN IsLow,
413 OUT UHCI_QH_RESULT *QhResult
414 )
415 {
416 UINTN Len;
417 UINT8 State;
418 UHCI_TD_HW *TdHw;
419 BOOLEAN Finished;
420
421 Finished = TRUE;
422
423 //
424 // Initialize the data toggle to that of the first
425 // TD. The next toggle to use is either:
426 // 1. first TD's toggle if no TD is executed OK
427 // 2. the next toggle of last executed-OK TD
428 //
429 QhResult->Result = EFI_USB_NOERROR;
430 QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
431 QhResult->Complete = 0;
432
433 while (Td != NULL) {
434 TdHw = &Td->TdHw;
435 State = (UINT8)TdHw->Status;
436
437 //
438 // UHCI will set STALLED bit when it abort the execution
439 // of TD list. There are several reasons:
440 // 1. BABBLE error happened
441 // 2. Received a STALL response
442 // 3. Error count decreased to zero.
443 //
444 // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
445 // bits when corresponding conditions happen. But these
446 // conditions are not deadly, that is a TD can successfully
447 // completes even these bits are set. But it is likely that
448 // upper layer won't distinguish these condtions. So, only
449 // set these bits when TD is actually halted.
450 //
451 if ((State & USBTD_STALLED) != 0) {
452 if ((State & USBTD_BABBLE) != 0) {
453 QhResult->Result |= EFI_USB_ERR_BABBLE;
454 } else if (TdHw->ErrorCount != 0) {
455 QhResult->Result |= EFI_USB_ERR_STALL;
456 }
457
458 if ((State & USBTD_CRC) != 0) {
459 QhResult->Result |= EFI_USB_ERR_CRC;
460 }
461
462 if ((State & USBTD_BUFFERR) != 0) {
463 QhResult->Result |= EFI_USB_ERR_BUFFER;
464 }
465
466 if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) {
467 QhResult->Result |= EFI_USB_ERR_BITSTUFF;
468 }
469
470 if (TdHw->ErrorCount == 0) {
471 QhResult->Result |= EFI_USB_ERR_TIMEOUT;
472 }
473
474 Finished = TRUE;
475 goto ON_EXIT;
476 } else if ((State & USBTD_ACTIVE) != 0) {
477 //
478 // The TD is still active, no need to check further.
479 //
480 QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
481
482 Finished = FALSE;
483 goto ON_EXIT;
484 } else {
485 //
486 // Update the next data toggle, it is always the
487 // next to the last known-good TD's data toggle if
488 // any TD is executed OK
489 //
490 QhResult->NextToggle = (UINT8)(1 - (UINT8)TdHw->DataToggle);
491
492 //
493 // This TD is finished OK or met short packet read. Update the
494 // transfer length if it isn't a SETUP.
495 //
496 Len = (TdHw->ActualLen + 1) & 0x7FF;
497
498 if (TdHw->PidCode != SETUP_PACKET_ID) {
499 QhResult->Complete += Len;
500 }
501
502 //
503 // Short packet condition for full speed input TD, also
504 // terminate the transfer
505 //
506 if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
507 DEBUG ((DEBUG_VERBOSE, "UhciCheckTdStatus: short packet read occurred\n"));
508
509 Finished = TRUE;
510 goto ON_EXIT;
511 }
512 }
513
514 Td = Td->NextTd;
515 }
516
517 ON_EXIT:
518 //
519 // Check whether HC is halted. Don't move this up. It must be
520 // called after data toggle is successfully updated.
521 //
522 if (!UhciIsHcWorking (Uhc->PciIo)) {
523 QhResult->Result |= EFI_USB_ERR_SYSTEM;
524 Finished = TRUE;
525 }
526
527 if (Finished) {
528 Uhc->PciIo->Flush (Uhc->PciIo);
529 }
530
531 UhciAckAllInterrupt (Uhc);
532 return Finished;
533 }
534
535 /**
536 Check the result of the transfer.
537
538 @param Uhc The UHCI device.
539 @param Qh The queue head of the transfer.
540 @param Td The first TDs of the transfer.
541 @param TimeOut TimeOut value in milliseconds.
542 @param IsLow Is Low Speed Device.
543 @param QhResult The variable to return result.
544
545 @retval EFI_SUCCESS The transfer finished with success.
546 @retval EFI_DEVICE_ERROR Transfer failed.
547
548 **/
549 EFI_STATUS
550 UhciExecuteTransfer (
551 IN USB_HC_DEV *Uhc,
552 IN UHCI_QH_SW *Qh,
553 IN UHCI_TD_SW *Td,
554 IN UINTN TimeOut,
555 IN BOOLEAN IsLow,
556 OUT UHCI_QH_RESULT *QhResult
557 )
558 {
559 UINTN Index;
560 UINTN Delay;
561 BOOLEAN Finished;
562 EFI_STATUS Status;
563 BOOLEAN InfiniteLoop;
564
565 Finished = FALSE;
566 Status = EFI_SUCCESS;
567 Delay = TimeOut * UHC_1_MILLISECOND;
568 InfiniteLoop = FALSE;
569
570 //
571 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
572 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
573 // is returned.
574 //
575 if (TimeOut == 0) {
576 InfiniteLoop = TRUE;
577 }
578
579 for (Index = 0; InfiniteLoop || (Index < Delay); Index++) {
580 Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
581
582 //
583 // Transfer is OK or some error occurred (TD inactive)
584 //
585 if (Finished) {
586 break;
587 }
588
589 gBS->Stall (UHC_1_MICROSECOND);
590 }
591
592 if (!Finished) {
593 DEBUG ((DEBUG_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut));
594 UhciDumpQh (Qh);
595 UhciDumpTds (Td);
596
597 Status = EFI_TIMEOUT;
598 } else if (QhResult->Result != EFI_USB_NOERROR) {
599 DEBUG ((DEBUG_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
600 UhciDumpQh (Qh);
601 UhciDumpTds (Td);
602
603 Status = EFI_DEVICE_ERROR;
604 }
605
606 return Status;
607 }
608
609 /**
610 Update Async Request, QH and TDs.
611
612 @param Uhc The UHCI device.
613 @param AsyncReq The UHCI asynchronous transfer to update.
614 @param Result Transfer reslut.
615 @param NextToggle The toggle of next data.
616
617 **/
618 VOID
619 UhciUpdateAsyncReq (
620 IN USB_HC_DEV *Uhc,
621 IN UHCI_ASYNC_REQUEST *AsyncReq,
622 IN UINT32 Result,
623 IN UINT32 NextToggle
624 )
625 {
626 UHCI_QH_SW *Qh;
627 UHCI_TD_SW *FirstTd;
628 UHCI_TD_SW *Td;
629
630 Qh = AsyncReq->QhSw;
631 FirstTd = AsyncReq->FirstTd;
632
633 if (Result == EFI_USB_NOERROR) {
634 //
635 // The last transfer succeeds. Then we need to update
636 // the Qh and Td for next round of transfer.
637 // 1. Update the TD's data toggle
638 // 2. Activate all the TDs
639 // 3. Link the TD to the queue head again since during
640 // execution, queue head's TD pointer is changed by
641 // hardware.
642 //
643 for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
644 Td->TdHw.DataToggle = NextToggle;
645 NextToggle ^= 1;
646 Td->TdHw.Status |= USBTD_ACTIVE;
647 }
648
649 UhciLinkTdToQh (Uhc, Qh, FirstTd);
650 return;
651 }
652 }
653
654 /**
655 Create Async Request node, and Link to List.
656
657 @param Uhc The UHCI device.
658 @param Qh The queue head of the transfer.
659 @param FirstTd First TD of the transfer.
660 @param DevAddr Device Address.
661 @param EndPoint EndPoint Address.
662 @param DataLen Data length.
663 @param Interval Polling Interval when inserted to frame list.
664 @param Data Data buffer, unmapped.
665 @param Callback Callback after interrupt transfeer.
666 @param Context Callback Context passed as function parameter.
667 @param IsLow Is Low Speed.
668
669 @retval EFI_SUCCESS An asynchronous transfer is created.
670 @retval EFI_INVALID_PARAMETER Paremeter is error.
671 @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage.
672
673 **/
674 EFI_STATUS
675 UhciCreateAsyncReq (
676 IN USB_HC_DEV *Uhc,
677 IN UHCI_QH_SW *Qh,
678 IN UHCI_TD_SW *FirstTd,
679 IN UINT8 DevAddr,
680 IN UINT8 EndPoint,
681 IN UINTN DataLen,
682 IN UINTN Interval,
683 IN UINT8 *Data,
684 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
685 IN VOID *Context,
686 IN BOOLEAN IsLow
687 )
688 {
689 UHCI_ASYNC_REQUEST *AsyncReq;
690
691 AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
692
693 if (AsyncReq == NULL) {
694 return EFI_OUT_OF_RESOURCES;
695 }
696
697 //
698 // Fill Request field. Data is allocated host memory, not mapped
699 //
700 AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE;
701 AsyncReq->DevAddr = DevAddr;
702 AsyncReq->EndPoint = EndPoint;
703 AsyncReq->DataLen = DataLen;
704 AsyncReq->Interval = UhciConvertPollRate (Interval);
705 AsyncReq->Data = Data;
706 AsyncReq->Callback = Callback;
707 AsyncReq->Context = Context;
708 AsyncReq->QhSw = Qh;
709 AsyncReq->FirstTd = FirstTd;
710 AsyncReq->IsLow = IsLow;
711
712 //
713 // Insert the new interrupt transfer to the head of the list.
714 // The interrupt transfer's monitor function scans the whole
715 // list from head to tail. The new interrupt transfer MUST be
716 // added to the head of the list.
717 //
718 InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
719
720 return EFI_SUCCESS;
721 }
722
723 /**
724 Free an asynchronous request's resource such as memory.
725
726 @param Uhc The UHCI device.
727 @param AsyncReq The asynchronous request to free.
728
729 **/
730 VOID
731 UhciFreeAsyncReq (
732 IN USB_HC_DEV *Uhc,
733 IN UHCI_ASYNC_REQUEST *AsyncReq
734 )
735 {
736 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
737
738 UhciDestoryTds (Uhc, AsyncReq->FirstTd);
739 UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
740
741 if (AsyncReq->Data != NULL) {
742 UsbHcFreeMem (Uhc->MemPool, AsyncReq->Data, AsyncReq->DataLen);
743 }
744
745 gBS->FreePool (AsyncReq);
746 }
747
748 /**
749 Unlink an asynchronous request's from UHC's asynchronus list.
750 also remove the queue head from the frame list. If FreeNow,
751 release its resource also. Otherwise, add the request to the
752 UHC's recycle list to wait for a while before release the memory.
753 Until then, hardware won't hold point to the request.
754
755 @param Uhc The UHCI device.
756 @param AsyncReq The asynchronous request to free.
757 @param FreeNow If TRUE, free the resource immediately, otherwise
758 add the request to recycle wait list.
759
760 **/
761 VOID
762 UhciUnlinkAsyncReq (
763 IN USB_HC_DEV *Uhc,
764 IN UHCI_ASYNC_REQUEST *AsyncReq,
765 IN BOOLEAN FreeNow
766 )
767 {
768 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
769
770 RemoveEntryList (&(AsyncReq->Link));
771 UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw);
772
773 if (FreeNow) {
774 UhciFreeAsyncReq (Uhc, AsyncReq);
775 } else {
776 //
777 // To sychronize with hardware, mark the queue head as inactive
778 // then add AsyncReq to UHC's recycle list
779 //
780 AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
781 AsyncReq->Recycle = Uhc->RecycleWait;
782 Uhc->RecycleWait = AsyncReq;
783 }
784 }
785
786 /**
787 Delete Async Interrupt QH and TDs.
788
789 @param Uhc The UHCI device.
790 @param DevAddr Device Address.
791 @param EndPoint EndPoint Address.
792 @param Toggle The next data toggle to use.
793
794 @retval EFI_SUCCESS The request is deleted.
795 @retval EFI_INVALID_PARAMETER Paremeter is error.
796 @retval EFI_NOT_FOUND The asynchronous isn't found.
797
798 **/
799 EFI_STATUS
800 UhciRemoveAsyncReq (
801 IN USB_HC_DEV *Uhc,
802 IN UINT8 DevAddr,
803 IN UINT8 EndPoint,
804 OUT UINT8 *Toggle
805 )
806 {
807 EFI_STATUS Status;
808 UHCI_ASYNC_REQUEST *AsyncReq;
809 UHCI_QH_RESULT QhResult;
810 LIST_ENTRY *Link;
811 BOOLEAN Found;
812
813 Status = EFI_SUCCESS;
814
815 //
816 // If no asynchronous interrupt transaction exists
817 //
818 if (IsListEmpty (&(Uhc->AsyncIntList))) {
819 return EFI_SUCCESS;
820 }
821
822 //
823 // Find the asynchronous transfer to this device/endpoint pair
824 //
825 Found = FALSE;
826 Link = Uhc->AsyncIntList.ForwardLink;
827
828 do {
829 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
830 Link = Link->ForwardLink;
831
832 if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
833 Found = TRUE;
834 break;
835 }
836 } while (Link != &(Uhc->AsyncIntList));
837
838 if (!Found) {
839 return EFI_NOT_FOUND;
840 }
841
842 //
843 // Check the result of the async transfer then update it
844 // to get the next data toggle to use.
845 //
846 UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
847 *Toggle = QhResult.NextToggle;
848
849 //
850 // Don't release the request now, keep it to synchronize with hardware.
851 //
852 UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
853 return Status;
854 }
855
856 /**
857 Recycle the asynchronouse request. When a queue head
858 is unlinked from frame list, host controller hardware
859 may still hold a cached pointer to it. To synchronize
860 with hardware, the request is released in two steps:
861 first it is linked to the UHC's RecycleWait list. At
862 the next time UhciMonitorAsyncReqList is fired, it is
863 moved to UHC's Recylelist. Then, at another timer
864 activation, all the requests on Recycle list is freed.
865 This guarrantes that each unlink queue head keeps
866 existing for at least 50ms, far enough for the hardware
867 to clear its cache.
868
869 @param Uhc The UHCI device.
870
871 **/
872 VOID
873 UhciRecycleAsyncReq (
874 IN USB_HC_DEV *Uhc
875 )
876 {
877 UHCI_ASYNC_REQUEST *Req;
878 UHCI_ASYNC_REQUEST *Next;
879
880 Req = Uhc->Recycle;
881
882 while (Req != NULL) {
883 Next = Req->Recycle;
884 UhciFreeAsyncReq (Uhc, Req);
885 Req = Next;
886 }
887
888 Uhc->Recycle = Uhc->RecycleWait;
889 Uhc->RecycleWait = NULL;
890 }
891
892 /**
893 Release all the asynchronous transfers on the lsit.
894
895 @param Uhc The UHCI device.
896
897 **/
898 VOID
899 UhciFreeAllAsyncReq (
900 IN USB_HC_DEV *Uhc
901 )
902 {
903 LIST_ENTRY *Head;
904 UHCI_ASYNC_REQUEST *AsyncReq;
905
906 //
907 // Call UhciRecycleAsyncReq twice. The requests on Recycle
908 // will be released at the first call; The requests on
909 // RecycleWait will be released at the second call.
910 //
911 UhciRecycleAsyncReq (Uhc);
912 UhciRecycleAsyncReq (Uhc);
913
914 Head = &(Uhc->AsyncIntList);
915
916 if (IsListEmpty (Head)) {
917 return;
918 }
919
920 while (!IsListEmpty (Head)) {
921 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
922 UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
923 }
924 }
925
926 /**
927 Interrupt transfer periodic check handler.
928
929 @param Event The event of the time.
930 @param Context Context of the event, pointer to USB_HC_DEV.
931
932 **/
933 VOID
934 EFIAPI
935 UhciMonitorAsyncReqList (
936 IN EFI_EVENT Event,
937 IN VOID *Context
938 )
939 {
940 UHCI_ASYNC_REQUEST *AsyncReq;
941 LIST_ENTRY *Link;
942 USB_HC_DEV *Uhc;
943 VOID *Data;
944 BOOLEAN Finished;
945 UHCI_QH_RESULT QhResult;
946
947 Uhc = (USB_HC_DEV *)Context;
948
949 //
950 // Recycle the asynchronous requests expired, and promote
951 // requests waiting to be recycled the next time when this
952 // timer expires
953 //
954 UhciRecycleAsyncReq (Uhc);
955
956 if (IsListEmpty (&(Uhc->AsyncIntList))) {
957 return;
958 }
959
960 //
961 // This loop must be delete safe
962 //
963 Link = Uhc->AsyncIntList.ForwardLink;
964
965 do {
966 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
967 Link = Link->ForwardLink;
968
969 Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
970
971 if (!Finished) {
972 continue;
973 }
974
975 //
976 // Copy the data to temporary buffer if there are some
977 // data transferred. We may have zero-length packet.
978 // Make sure the data received from HW is no more than expected.
979 //
980 Data = NULL;
981
982 if ((QhResult.Complete != 0) && (QhResult.Complete <= AsyncReq->DataLen)) {
983 Data = AllocatePool (QhResult.Complete);
984
985 if (Data == NULL) {
986 return;
987 }
988
989 CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
990 }
991
992 UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle);
993
994 //
995 // Now, either transfer is SUCCESS or met errors since
996 // we have skipped to next transfer earlier if current
997 // transfer is still active.
998 //
999 if (QhResult.Result == EFI_USB_NOERROR) {
1000 AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
1001 } else {
1002 //
1003 // Leave error recovery to its related device driver.
1004 // A common case of the error recovery is to re-submit
1005 // the interrupt transfer. When an interrupt transfer
1006 // is re-submitted, its position in the linked list is
1007 // changed. It is inserted to the head of the linked
1008 // list, while this function scans the whole list from
1009 // head to tail. Thus, the re-submitted interrupt transfer's
1010 // callback function will not be called again in this round.
1011 //
1012 AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
1013 }
1014
1015 if (Data != NULL) {
1016 gBS->FreePool (Data);
1017 }
1018 } while (Link != &(Uhc->AsyncIntList));
1019 }