]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciDxe / EhciSched.c
1 /** @file
2
3 EHCI transfer scheduling routines.
4
5 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "Ehci.h"
12
13
14 /**
15 Create helper QTD/QH for the EHCI device.
16
17 @param Ehc The EHCI device.
18
19 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
20 @retval EFI_SUCCESS Helper QH/QTD are created.
21
22 **/
23 EFI_STATUS
24 EhcCreateHelpQ (
25 IN USB2_HC_DEV *Ehc
26 )
27 {
28 USB_ENDPOINT Ep;
29 EHC_QH *Qh;
30 QH_HW *QhHw;
31 EHC_QTD *Qtd;
32 EFI_PHYSICAL_ADDRESS PciAddr;
33
34 //
35 // Create an inactive Qtd to terminate the short packet read.
36 //
37 Qtd = EhcCreateQtd (Ehc, NULL, NULL, 0, QTD_PID_INPUT, 0, 64);
38
39 if (Qtd == NULL) {
40 return EFI_OUT_OF_RESOURCES;
41 }
42
43 Qtd->QtdHw.Status = QTD_STAT_HALTED;
44 Ehc->ShortReadStop = Qtd;
45
46 //
47 // Create a QH to act as the EHC reclamation header.
48 // Set the header to loopback to itself.
49 //
50 Ep.DevAddr = 0;
51 Ep.EpAddr = 1;
52 Ep.Direction = EfiUsbDataIn;
53 Ep.DevSpeed = EFI_USB_SPEED_HIGH;
54 Ep.MaxPacket = 64;
55 Ep.HubAddr = 0;
56 Ep.HubPort = 0;
57 Ep.Toggle = 0;
58 Ep.Type = EHC_BULK_TRANSFER;
59 Ep.PollRate = 1;
60
61 Qh = EhcCreateQh (Ehc, &Ep);
62
63 if (Qh == NULL) {
64 return EFI_OUT_OF_RESOURCES;
65 }
66
67 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));
68 QhHw = &Qh->QhHw;
69 QhHw->HorizonLink = QH_LINK (PciAddr + OFFSET_OF(EHC_QH, QhHw), EHC_TYPE_QH, FALSE);
70 QhHw->Status = QTD_STAT_HALTED;
71 QhHw->ReclaimHead = 1;
72 Qh->NextQh = Qh;
73 Ehc->ReclaimHead = Qh;
74
75 //
76 // Create a dummy QH to act as the terminator for periodical schedule
77 //
78 Ep.EpAddr = 2;
79 Ep.Type = EHC_INT_TRANSFER_SYNC;
80
81 Qh = EhcCreateQh (Ehc, &Ep);
82
83 if (Qh == NULL) {
84 return EFI_OUT_OF_RESOURCES;
85 }
86
87 Qh->QhHw.Status = QTD_STAT_HALTED;
88 Ehc->PeriodOne = Qh;
89
90 return EFI_SUCCESS;
91 }
92
93
94 /**
95 Initialize the schedule data structure such as frame list.
96
97 @param Ehc The EHCI device to init schedule data.
98
99 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
100 @retval EFI_SUCCESS The schedule data is initialized.
101
102 **/
103 EFI_STATUS
104 EhcInitSched (
105 IN USB2_HC_DEV *Ehc
106 )
107 {
108 EFI_PCI_IO_PROTOCOL *PciIo;
109 VOID *Buf;
110 EFI_PHYSICAL_ADDRESS PhyAddr;
111 VOID *Map;
112 UINTN Pages;
113 UINTN Bytes;
114 UINTN Index;
115 EFI_STATUS Status;
116 EFI_PHYSICAL_ADDRESS PciAddr;
117
118 //
119 // First initialize the periodical schedule data:
120 // 1. Allocate and map the memory for the frame list
121 // 2. Create the help QTD/QH
122 // 3. Initialize the frame entries
123 // 4. Set the frame list register
124 //
125 PciIo = Ehc->PciIo;
126
127 Bytes = 4096;
128 Pages = EFI_SIZE_TO_PAGES (Bytes);
129
130 Status = PciIo->AllocateBuffer (
131 PciIo,
132 AllocateAnyPages,
133 EfiBootServicesData,
134 Pages,
135 &Buf,
136 0
137 );
138
139 if (EFI_ERROR (Status)) {
140 return EFI_OUT_OF_RESOURCES;
141 }
142
143 Status = PciIo->Map (
144 PciIo,
145 EfiPciIoOperationBusMasterCommonBuffer,
146 Buf,
147 &Bytes,
148 &PhyAddr,
149 &Map
150 );
151
152 if (EFI_ERROR (Status) || (Bytes != 4096)) {
153 PciIo->FreeBuffer (PciIo, Pages, Buf);
154 return EFI_OUT_OF_RESOURCES;
155 }
156
157 Ehc->PeriodFrame = Buf;
158 Ehc->PeriodFrameMap = Map;
159
160 //
161 // Program the FRAMELISTBASE register with the low 32 bit addr
162 //
163 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));
164 //
165 // Program the CTRLDSSEGMENT register with the high 32 bit addr
166 //
167 EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, EHC_HIGH_32BIT (PhyAddr));
168
169 //
170 // Init memory pool management then create the helper
171 // QTD/QH. If failed, previously allocated resources
172 // will be freed by EhcFreeSched
173 //
174 Ehc->MemPool = UsbHcInitMemPool (
175 PciIo,
176 Ehc->Support64BitDma,
177 EHC_HIGH_32BIT (PhyAddr)
178 );
179
180 if (Ehc->MemPool == NULL) {
181 Status = EFI_OUT_OF_RESOURCES;
182 goto ErrorExit1;
183 }
184
185 Status = EhcCreateHelpQ (Ehc);
186
187 if (EFI_ERROR (Status)) {
188 goto ErrorExit;
189 }
190
191 //
192 // Initialize the frame list entries then set the registers
193 //
194 Ehc->PeriodFrameHost = AllocateZeroPool (EHC_FRAME_LEN * sizeof (UINTN));
195 if (Ehc->PeriodFrameHost == NULL) {
196 Status = EFI_OUT_OF_RESOURCES;
197 goto ErrorExit;
198 }
199
200 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));
201
202 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
203 //
204 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.
205 //
206 ((UINT32 *)(Ehc->PeriodFrame))[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
207 //
208 // Store the host address of the QH in period frame list which will be accessed by host.
209 //
210 ((UINTN *)(Ehc->PeriodFrameHost))[Index] = (UINTN)Ehc->PeriodOne;
211 }
212
213 //
214 // Second initialize the asynchronous schedule:
215 // Only need to set the AsynListAddr register to
216 // the reclamation header
217 //
218 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));
219 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));
220 return EFI_SUCCESS;
221
222 ErrorExit:
223 if (Ehc->PeriodOne != NULL) {
224 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));
225 Ehc->PeriodOne = NULL;
226 }
227
228 if (Ehc->ReclaimHead != NULL) {
229 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));
230 Ehc->ReclaimHead = NULL;
231 }
232
233 if (Ehc->ShortReadStop != NULL) {
234 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));
235 Ehc->ShortReadStop = NULL;
236 }
237
238 ErrorExit1:
239 PciIo->FreeBuffer (PciIo, Pages, Buf);
240 PciIo->Unmap (PciIo, Map);
241
242 return Status;
243 }
244
245
246 /**
247 Free the schedule data. It may be partially initialized.
248
249 @param Ehc The EHCI device.
250
251 **/
252 VOID
253 EhcFreeSched (
254 IN USB2_HC_DEV *Ehc
255 )
256 {
257 EFI_PCI_IO_PROTOCOL *PciIo;
258
259 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
260 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
261
262 if (Ehc->PeriodOne != NULL) {
263 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));
264 Ehc->PeriodOne = NULL;
265 }
266
267 if (Ehc->ReclaimHead != NULL) {
268 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));
269 Ehc->ReclaimHead = NULL;
270 }
271
272 if (Ehc->ShortReadStop != NULL) {
273 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));
274 Ehc->ShortReadStop = NULL;
275 }
276
277 if (Ehc->MemPool != NULL) {
278 UsbHcFreeMemPool (Ehc->MemPool);
279 Ehc->MemPool = NULL;
280 }
281
282 if (Ehc->PeriodFrame != NULL) {
283 PciIo = Ehc->PciIo;
284 ASSERT (PciIo != NULL);
285
286 PciIo->Unmap (PciIo, Ehc->PeriodFrameMap);
287
288 PciIo->FreeBuffer (
289 PciIo,
290 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE),
291 Ehc->PeriodFrame
292 );
293
294 Ehc->PeriodFrame = NULL;
295 }
296
297 if (Ehc->PeriodFrameHost != NULL) {
298 FreePool (Ehc->PeriodFrameHost);
299 Ehc->PeriodFrameHost = NULL;
300 }
301 }
302
303
304 /**
305 Link the queue head to the asynchronous schedule list.
306 UEFI only supports one CTRL/BULK transfer at a time
307 due to its interfaces. This simplifies the AsynList
308 management: A reclamation header is always linked to
309 the AsyncListAddr, the only active QH is appended to it.
310
311 @param Ehc The EHCI device.
312 @param Qh The queue head to link.
313
314 **/
315 VOID
316 EhcLinkQhToAsync (
317 IN USB2_HC_DEV *Ehc,
318 IN EHC_QH *Qh
319 )
320 {
321 EHC_QH *Head;
322 EFI_PHYSICAL_ADDRESS PciAddr;
323
324 //
325 // Append the queue head after the reclaim header, then
326 // fix the hardware visiable parts (EHCI R1.0 page 72).
327 // ReclaimHead is always linked to the EHCI's AsynListAddr.
328 //
329 Head = Ehc->ReclaimHead;
330
331 Qh->NextQh = Head->NextQh;
332 Head->NextQh = Qh;
333
334 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh->NextQh, sizeof (EHC_QH));
335 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
336 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));
337 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
338 }
339
340
341 /**
342 Unlink a queue head from the asynchronous schedule list.
343 Need to synchronize with hardware.
344
345 @param Ehc The EHCI device.
346 @param Qh The queue head to unlink.
347
348 **/
349 VOID
350 EhcUnlinkQhFromAsync (
351 IN USB2_HC_DEV *Ehc,
352 IN EHC_QH *Qh
353 )
354 {
355 EHC_QH *Head;
356 EFI_STATUS Status;
357 EFI_PHYSICAL_ADDRESS PciAddr;
358
359 ASSERT (Ehc->ReclaimHead->NextQh == Qh);
360
361 //
362 // Remove the QH from reclamation head, then update the hardware
363 // visiable part: Only need to loopback the ReclaimHead. The Qh
364 // is pointing to ReclaimHead (which is staill in the list).
365 //
366 Head = Ehc->ReclaimHead;
367
368 Head->NextQh = Qh->NextQh;
369 Qh->NextQh = NULL;
370
371 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head->NextQh, sizeof (EHC_QH));
372 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
373
374 //
375 // Set and wait the door bell to synchronize with the hardware
376 //
377 Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
378
379 if (EFI_ERROR (Status)) {
380 DEBUG ((EFI_D_ERROR, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));
381 }
382 }
383
384
385 /**
386 Link a queue head for interrupt transfer to the periodic
387 schedule frame list. This code is very much the same as
388 that in UHCI.
389
390 @param Ehc The EHCI device.
391 @param Qh The queue head to link.
392
393 **/
394 VOID
395 EhcLinkQhToPeriod (
396 IN USB2_HC_DEV *Ehc,
397 IN EHC_QH *Qh
398 )
399 {
400 UINTN Index;
401 EHC_QH *Prev;
402 EHC_QH *Next;
403 EFI_PHYSICAL_ADDRESS PciAddr;
404
405 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
406 //
407 // First QH can't be NULL because we always keep PeriodOne
408 // heads on the frame list
409 //
410 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));
411 Next = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];
412 Prev = NULL;
413
414 //
415 // Now, insert the queue head (Qh) into this frame:
416 // 1. Find a queue head with the same poll interval, just insert
417 // Qh after this queue head, then we are done.
418 //
419 // 2. Find the position to insert the queue head into:
420 // Previous head's interval is bigger than Qh's
421 // Next head's interval is less than Qh's
422 // Then, insert the Qh between then
423 //
424 while (Next->Interval > Qh->Interval) {
425 Prev = Next;
426 Next = Next->NextQh;
427 }
428
429 ASSERT (Next != NULL);
430
431 //
432 // The entry may have been linked into the frame by early insertation.
433 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
434 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
435 // It isn't necessary to compare all the QH with the same interval to
436 // Qh. This is because if there is other QH with the same interval, Qh
437 // should has been inserted after that at Frames[0] and at Frames[0] it is
438 // impossible for (Next == Qh)
439 //
440 if (Next == Qh) {
441 continue;
442 }
443
444 if (Next->Interval == Qh->Interval) {
445 //
446 // If there is a QH with the same interval, it locates at
447 // Frames[0], and we can simply insert it after this QH. We
448 // are all done.
449 //
450 ASSERT ((Index == 0) && (Qh->NextQh == NULL));
451
452 Prev = Next;
453 Next = Next->NextQh;
454
455 Qh->NextQh = Next;
456 Prev->NextQh = Qh;
457
458 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
459 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));
460 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
461 break;
462 }
463
464 //
465 // OK, find the right position, insert it in. If Qh's next
466 // link has already been set, it is in position. This is
467 // guarranted by 2^n polling interval.
468 //
469 if (Qh->NextQh == NULL) {
470 Qh->NextQh = Next;
471 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Next, sizeof (EHC_QH));
472 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
473 }
474
475 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));
476
477 if (Prev == NULL) {
478 ((UINT32*)Ehc->PeriodFrame)[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
479 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh;
480 } else {
481 Prev->NextQh = Qh;
482 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);
483 }
484 }
485 }
486
487
488 /**
489 Unlink an interrupt queue head from the periodic
490 schedule frame list.
491
492 @param Ehc The EHCI device.
493 @param Qh The queue head to unlink.
494
495 **/
496 VOID
497 EhcUnlinkQhFromPeriod (
498 IN USB2_HC_DEV *Ehc,
499 IN EHC_QH *Qh
500 )
501 {
502 UINTN Index;
503 EHC_QH *Prev;
504 EHC_QH *This;
505
506 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
507 //
508 // Frame link can't be NULL because we always keep PeroidOne
509 // on the frame list
510 //
511 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));
512 This = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];
513 Prev = NULL;
514
515 //
516 // Walk through the frame's QH list to find the
517 // queue head to remove
518 //
519 while ((This != NULL) && (This != Qh)) {
520 Prev = This;
521 This = This->NextQh;
522 }
523
524 //
525 // Qh may have already been unlinked from this frame
526 // by early action. See the comments in EhcLinkQhToPeriod.
527 //
528 if (This == NULL) {
529 continue;
530 }
531
532 if (Prev == NULL) {
533 //
534 // Qh is the first entry in the frame
535 //
536 ((UINT32*)Ehc->PeriodFrame)[Index] = Qh->QhHw.HorizonLink;
537 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh->NextQh;
538 } else {
539 Prev->NextQh = Qh->NextQh;
540 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
541 }
542 }
543 }
544
545
546 /**
547 Check the URB's execution result and update the URB's
548 result accordingly.
549
550 @param Ehc The EHCI device.
551 @param Urb The URB to check result.
552
553 @return Whether the result of URB transfer is finialized.
554
555 **/
556 BOOLEAN
557 EhcCheckUrbResult (
558 IN USB2_HC_DEV *Ehc,
559 IN URB *Urb
560 )
561 {
562 LIST_ENTRY *Entry;
563 EHC_QTD *Qtd;
564 QTD_HW *QtdHw;
565 UINT8 State;
566 BOOLEAN Finished;
567 EFI_PHYSICAL_ADDRESS PciAddr;
568
569 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
570
571 Finished = TRUE;
572 Urb->Completed = 0;
573
574 Urb->Result = EFI_USB_NOERROR;
575
576 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
577 Urb->Result |= EFI_USB_ERR_SYSTEM;
578 goto ON_EXIT;
579 }
580
581 BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
582 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
583 QtdHw = &Qtd->QtdHw;
584 State = (UINT8) QtdHw->Status;
585
586 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
587 //
588 // EHCI will halt the queue head when met some error.
589 // If it is halted, the result of URB is finialized.
590 //
591 if ((State & QTD_STAT_ERR_MASK) == 0) {
592 Urb->Result |= EFI_USB_ERR_STALL;
593 }
594
595 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
596 Urb->Result |= EFI_USB_ERR_BABBLE;
597 }
598
599 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
600 Urb->Result |= EFI_USB_ERR_BUFFER;
601 }
602
603 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
604 Urb->Result |= EFI_USB_ERR_TIMEOUT;
605 }
606
607 Finished = TRUE;
608 goto ON_EXIT;
609
610 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
611 //
612 // The QTD is still active, no need to check furthur.
613 //
614 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
615
616 Finished = FALSE;
617 goto ON_EXIT;
618
619 } else {
620 //
621 // This QTD is finished OK or met short packet read. Update the
622 // transfer length if it isn't a setup.
623 //
624 if (QtdHw->Pid != QTD_PID_SETUP) {
625 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
626 }
627
628 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
629 EhcDumpQh (Urb->Qh, "Short packet read", FALSE);
630
631 //
632 // Short packet read condition. If it isn't a setup transfer,
633 // no need to check furthur: the queue head will halt at the
634 // ShortReadStop. If it is a setup transfer, need to check the
635 // Status Stage of the setup transfer to get the finial result
636 //
637 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));
638 if (QtdHw->AltNext == QTD_LINK (PciAddr, FALSE)) {
639 DEBUG ((EFI_D_VERBOSE, "EhcCheckUrbResult: Short packet read, break\n"));
640
641 Finished = TRUE;
642 goto ON_EXIT;
643 }
644
645 DEBUG ((EFI_D_VERBOSE, "EhcCheckUrbResult: Short packet read, continue\n"));
646 }
647 }
648 }
649
650 ON_EXIT:
651 //
652 // Return the data toggle set by EHCI hardware, bulk and interrupt
653 // transfer will use this to initialize the next transaction. For
654 // Control transfer, it always start a new data toggle sequence for
655 // new transfer.
656 //
657 // NOTICE: don't move DT update before the loop, otherwise there is
658 // a race condition that DT is wrong.
659 //
660 Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;
661
662 return Finished;
663 }
664
665
666 /**
667 Execute the transfer by polling the URB. This is a synchronous operation.
668
669 @param Ehc The EHCI device.
670 @param Urb The URB to execute.
671 @param TimeOut The time to wait before abort, in millisecond.
672
673 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.
674 @return EFI_TIMEOUT The transfer failed due to time out.
675 @return EFI_SUCCESS The transfer finished OK.
676
677 **/
678 EFI_STATUS
679 EhcExecTransfer (
680 IN USB2_HC_DEV *Ehc,
681 IN URB *Urb,
682 IN UINTN TimeOut
683 )
684 {
685 EFI_STATUS Status;
686 UINTN Index;
687 UINTN Loop;
688 BOOLEAN Finished;
689 BOOLEAN InfiniteLoop;
690
691 Status = EFI_SUCCESS;
692 Loop = TimeOut * EHC_1_MILLISECOND;
693 Finished = FALSE;
694 InfiniteLoop = FALSE;
695
696 //
697 // According to UEFI spec section 16.2.4, If Timeout is 0, then the caller
698 // must wait for the function to be completed until EFI_SUCCESS or EFI_DEVICE_ERROR
699 // is returned.
700 //
701 if (TimeOut == 0) {
702 InfiniteLoop = TRUE;
703 }
704
705 for (Index = 0; InfiniteLoop || (Index < Loop); Index++) {
706 Finished = EhcCheckUrbResult (Ehc, Urb);
707
708 if (Finished) {
709 break;
710 }
711
712 gBS->Stall (EHC_1_MICROSECOND);
713 }
714
715 if (!Finished) {
716 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32)TimeOut));
717 EhcDumpQh (Urb->Qh, NULL, FALSE);
718
719 Status = EFI_TIMEOUT;
720
721 } else if (Urb->Result != EFI_USB_NOERROR) {
722 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer failed with %x\n", Urb->Result));
723 EhcDumpQh (Urb->Qh, NULL, FALSE);
724
725 Status = EFI_DEVICE_ERROR;
726 }
727
728 return Status;
729 }
730
731
732 /**
733 Delete a single asynchronous interrupt transfer for
734 the device and endpoint.
735
736 @param Ehc The EHCI device.
737 @param DevAddr The address of the target device.
738 @param EpNum The endpoint of the target.
739 @param DataToggle Return the next data toggle to use.
740
741 @retval EFI_SUCCESS An asynchronous transfer is removed.
742 @retval EFI_NOT_FOUND No transfer for the device is found.
743
744 **/
745 EFI_STATUS
746 EhciDelAsyncIntTransfer (
747 IN USB2_HC_DEV *Ehc,
748 IN UINT8 DevAddr,
749 IN UINT8 EpNum,
750 OUT UINT8 *DataToggle
751 )
752 {
753 LIST_ENTRY *Entry;
754 LIST_ENTRY *Next;
755 URB *Urb;
756 EFI_USB_DATA_DIRECTION Direction;
757
758 Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
759 EpNum &= 0x0F;
760
761 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
762 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
763
764 if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) &&
765 (Urb->Ep.Direction == Direction)) {
766 //
767 // Check the URB status to retrieve the next data toggle
768 // from the associated queue head.
769 //
770 EhcCheckUrbResult (Ehc, Urb);
771 *DataToggle = Urb->DataToggle;
772
773 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
774 RemoveEntryList (&Urb->UrbList);
775
776 gBS->FreePool (Urb->Data);
777 EhcFreeUrb (Ehc, Urb);
778 return EFI_SUCCESS;
779 }
780 }
781
782 return EFI_NOT_FOUND;
783 }
784
785
786 /**
787 Remove all the asynchronous interrutp transfers.
788
789 @param Ehc The EHCI device.
790
791 **/
792 VOID
793 EhciDelAllAsyncIntTransfers (
794 IN USB2_HC_DEV *Ehc
795 )
796 {
797 LIST_ENTRY *Entry;
798 LIST_ENTRY *Next;
799 URB *Urb;
800
801 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
802 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
803
804 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
805 RemoveEntryList (&Urb->UrbList);
806
807 gBS->FreePool (Urb->Data);
808 EhcFreeUrb (Ehc, Urb);
809 }
810 }
811
812 /**
813 Insert a single asynchronous interrupt transfer for
814 the device and endpoint.
815
816 @param Ehc The EHCI device.
817 @param DevAddr The device address.
818 @param EpAddr Endpoint addrress & its direction.
819 @param DevSpeed The device speed.
820 @param Toggle Initial data toggle to use.
821 @param MaxPacket The max packet length of the endpoint.
822 @param Hub The transaction translator to use.
823 @param DataLen The length of data buffer.
824 @param Callback The function to call when data is transferred.
825 @param Context The context to the callback.
826 @param Interval The interval for interrupt transfer.
827
828 @return Created URB or NULL.
829
830 **/
831 URB *
832 EhciInsertAsyncIntTransfer (
833 IN USB2_HC_DEV *Ehc,
834 IN UINT8 DevAddr,
835 IN UINT8 EpAddr,
836 IN UINT8 DevSpeed,
837 IN UINT8 Toggle,
838 IN UINTN MaxPacket,
839 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
840 IN UINTN DataLen,
841 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
842 IN VOID *Context,
843 IN UINTN Interval
844 )
845 {
846 VOID *Data;
847 URB *Urb;
848
849 Data = AllocatePool (DataLen);
850
851 if (Data == NULL) {
852 DEBUG ((DEBUG_ERROR, "%a: failed to allocate buffer\n", __FUNCTION__));
853 return NULL;
854 }
855
856 Urb = EhcCreateUrb (
857 Ehc,
858 DevAddr,
859 EpAddr,
860 DevSpeed,
861 Toggle,
862 MaxPacket,
863 Hub,
864 EHC_INT_TRANSFER_ASYNC,
865 NULL,
866 Data,
867 DataLen,
868 Callback,
869 Context,
870 Interval
871 );
872
873 if (Urb == NULL) {
874 DEBUG ((DEBUG_ERROR, "%a: failed to create URB\n", __FUNCTION__));
875 gBS->FreePool (Data);
876 return NULL;
877 }
878
879 //
880 // New asynchronous transfer must inserted to the head.
881 // Check the comments in EhcMoniteAsyncRequests
882 //
883 EhcLinkQhToPeriod (Ehc, Urb->Qh);
884 InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);
885
886 return Urb;
887 }
888
889 /**
890 Flush data from PCI controller specific address to mapped system
891 memory address.
892
893 @param Ehc The EHCI device.
894 @param Urb The URB to unmap.
895
896 @retval EFI_SUCCESS Success to flush data to mapped system memory.
897 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
898
899 **/
900 EFI_STATUS
901 EhcFlushAsyncIntMap (
902 IN USB2_HC_DEV *Ehc,
903 IN URB *Urb
904 )
905 {
906 EFI_STATUS Status;
907 EFI_PHYSICAL_ADDRESS PhyAddr;
908 EFI_PCI_IO_PROTOCOL_OPERATION MapOp;
909 EFI_PCI_IO_PROTOCOL *PciIo;
910 UINTN Len;
911 VOID *Map;
912
913 PciIo = Ehc->PciIo;
914 Len = Urb->DataLen;
915
916 if (Urb->Ep.Direction == EfiUsbDataIn) {
917 MapOp = EfiPciIoOperationBusMasterWrite;
918 } else {
919 MapOp = EfiPciIoOperationBusMasterRead;
920 }
921
922 Status = PciIo->Unmap (PciIo, Urb->DataMap);
923 if (EFI_ERROR (Status)) {
924 goto ON_ERROR;
925 }
926
927 Urb->DataMap = NULL;
928
929 Status = PciIo->Map (PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);
930 if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {
931 goto ON_ERROR;
932 }
933
934 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
935 Urb->DataMap = Map;
936 return EFI_SUCCESS;
937
938 ON_ERROR:
939 return EFI_DEVICE_ERROR;
940 }
941
942
943 /**
944 Update the queue head for next round of asynchronous transfer.
945
946 @param Ehc The EHCI device.
947 @param Urb The URB to update.
948
949 **/
950 VOID
951 EhcUpdateAsyncRequest (
952 IN USB2_HC_DEV *Ehc,
953 IN URB *Urb
954 )
955 {
956 LIST_ENTRY *Entry;
957 EHC_QTD *FirstQtd;
958 QH_HW *QhHw;
959 EHC_QTD *Qtd;
960 QTD_HW *QtdHw;
961 UINTN Index;
962 EFI_PHYSICAL_ADDRESS PciAddr;
963
964 Qtd = NULL;
965
966 if (Urb->Result == EFI_USB_NOERROR) {
967 FirstQtd = NULL;
968
969 BASE_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
970 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);
971
972 if (FirstQtd == NULL) {
973 FirstQtd = Qtd;
974 }
975
976 //
977 // Update the QTD for next round of transfer. Host control
978 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
979 // Current Offset. These fields need to be updated. DT isn't
980 // used by interrupt transfer. It uses DT in queue head.
981 // Current Offset is in Page[0], only need to reset Page[0]
982 // to initial data buffer.
983 //
984 QtdHw = &Qtd->QtdHw;
985 QtdHw->Status = QTD_STAT_ACTIVE;
986 QtdHw->ErrCnt = QTD_MAX_ERR;
987 QtdHw->CurPage = 0;
988 QtdHw->TotalBytes = (UINT32) Qtd->DataLen;
989 //
990 // calculate physical address by offset.
991 //
992 PciAddr = (UINTN)Urb->DataPhy + ((UINTN)Qtd->Data - (UINTN)Urb->Data);
993 QtdHw->Page[0] = EHC_LOW_32BIT (PciAddr);
994 QtdHw->PageHigh[0]= EHC_HIGH_32BIT (PciAddr);
995 }
996
997 //
998 // Update QH for next round of transfer. Host control only
999 // touch the fields in transfer overlay area. Only need to
1000 // zero out the overlay area and set NextQtd to the first
1001 // QTD. DateToggle bit is left untouched.
1002 //
1003 QhHw = &Urb->Qh->QhHw;
1004 QhHw->CurQtd = QTD_LINK (0, TRUE);
1005 QhHw->AltQtd = 0;
1006
1007 QhHw->Status = 0;
1008 QhHw->Pid = 0;
1009 QhHw->ErrCnt = 0;
1010 QhHw->CurPage = 0;
1011 QhHw->Ioc = 0;
1012 QhHw->TotalBytes = 0;
1013
1014 for (Index = 0; Index < 5; Index++) {
1015 QhHw->Page[Index] = 0;
1016 QhHw->PageHigh[Index] = 0;
1017 }
1018
1019 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, FirstQtd, sizeof (EHC_QTD));
1020 QhHw->NextQtd = QTD_LINK (PciAddr, FALSE);
1021 }
1022
1023 return ;
1024 }
1025
1026
1027 /**
1028 Interrupt transfer periodic check handler.
1029
1030 @param Event Interrupt event.
1031 @param Context Pointer to USB2_HC_DEV.
1032
1033 **/
1034 VOID
1035 EFIAPI
1036 EhcMonitorAsyncRequests (
1037 IN EFI_EVENT Event,
1038 IN VOID *Context
1039 )
1040 {
1041 USB2_HC_DEV *Ehc;
1042 EFI_TPL OldTpl;
1043 LIST_ENTRY *Entry;
1044 LIST_ENTRY *Next;
1045 BOOLEAN Finished;
1046 UINT8 *ProcBuf;
1047 URB *Urb;
1048 EFI_STATUS Status;
1049
1050 OldTpl = gBS->RaiseTPL (EHC_TPL);
1051 Ehc = (USB2_HC_DEV *) Context;
1052
1053 BASE_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
1054 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);
1055
1056 //
1057 // Check the result of URB execution. If it is still
1058 // active, check the next one.
1059 //
1060 Finished = EhcCheckUrbResult (Ehc, Urb);
1061
1062 if (!Finished) {
1063 continue;
1064 }
1065
1066 //
1067 // Flush any PCI posted write transactions from a PCI host
1068 // bridge to system memory.
1069 //
1070 Status = EhcFlushAsyncIntMap (Ehc, Urb);
1071 if (EFI_ERROR (Status)) {
1072 DEBUG ((EFI_D_ERROR, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));
1073 }
1074
1075 //
1076 // Allocate a buffer then copy the transferred data for user.
1077 // If failed to allocate the buffer, update the URB for next
1078 // round of transfer. Ignore the data of this round.
1079 //
1080 ProcBuf = NULL;
1081
1082 if (Urb->Result == EFI_USB_NOERROR) {
1083 //
1084 // Make sure the data received from HW is no more than expected.
1085 //
1086 if (Urb->Completed <= Urb->DataLen) {
1087 ProcBuf = AllocatePool (Urb->Completed);
1088 }
1089
1090 if (ProcBuf == NULL) {
1091 EhcUpdateAsyncRequest (Ehc, Urb);
1092 continue;
1093 }
1094
1095 CopyMem (ProcBuf, Urb->Data, Urb->Completed);
1096 }
1097
1098 EhcUpdateAsyncRequest (Ehc, Urb);
1099
1100 //
1101 // Leave error recovery to its related device driver. A
1102 // common case of the error recovery is to re-submit the
1103 // interrupt transfer which is linked to the head of the
1104 // list. This function scans from head to tail. So the
1105 // re-submitted interrupt transfer's callback function
1106 // will not be called again in this round. Don't touch this
1107 // URB after the callback, it may have been removed by the
1108 // callback.
1109 //
1110 if (Urb->Callback != NULL) {
1111 //
1112 // Restore the old TPL, USB bus maybe connect device in
1113 // his callback. Some drivers may has a lower TPL restriction.
1114 //
1115 gBS->RestoreTPL (OldTpl);
1116 (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);
1117 OldTpl = gBS->RaiseTPL (EHC_TPL);
1118 }
1119
1120 if (ProcBuf != NULL) {
1121 FreePool (ProcBuf);
1122 }
1123 }
1124
1125 gBS->RestoreTPL (OldTpl);
1126 }