]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c
MdeModulePkg: Add PEI USB drivers and related PPIs
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciPei / EhciSched.c
1 /** @file
2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
4
5 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include "EhcPeim.h"
19
20 /**
21 Create helper QTD/QH for the EHCI device.
22
23 @param Ehc The EHCI device.
24
25 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
26 @retval EFI_SUCCESS Helper QH/QTD are created.
27
28 **/
29 EFI_STATUS
30 EhcCreateHelpQ (
31 IN PEI_USB2_HC_DEV *Ehc
32 )
33 {
34 USB_ENDPOINT Ep;
35 PEI_EHC_QH *Qh;
36 QH_HW *QhHw;
37 PEI_EHC_QTD *Qtd;
38
39 //
40 // Create an inactive Qtd to terminate the short packet read.
41 //
42 Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);
43
44 if (Qtd == NULL) {
45 return EFI_OUT_OF_RESOURCES;
46 }
47
48 Qtd->QtdHw.Status = QTD_STAT_HALTED;
49 Ehc->ShortReadStop = Qtd;
50
51 //
52 // Create a QH to act as the EHC reclamation header.
53 // Set the header to loopback to itself.
54 //
55 Ep.DevAddr = 0;
56 Ep.EpAddr = 1;
57 Ep.Direction = EfiUsbDataIn;
58 Ep.DevSpeed = EFI_USB_SPEED_HIGH;
59 Ep.MaxPacket = 64;
60 Ep.HubAddr = 0;
61 Ep.HubPort = 0;
62 Ep.Toggle = 0;
63 Ep.Type = EHC_BULK_TRANSFER;
64 Ep.PollRate = 1;
65
66 Qh = EhcCreateQh (Ehc, &Ep);
67
68 if (Qh == NULL) {
69 return EFI_OUT_OF_RESOURCES;
70 }
71
72 QhHw = &Qh->QhHw;
73 QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);
74 QhHw->Status = QTD_STAT_HALTED;
75 QhHw->ReclaimHead = 1;
76 Ehc->ReclaimHead = Qh;
77
78 //
79 // Create a dummy QH to act as the terminator for periodical schedule
80 //
81 Ep.EpAddr = 2;
82 Ep.Type = EHC_INT_TRANSFER_SYNC;
83
84 Qh = EhcCreateQh (Ehc, &Ep);
85
86 if (Qh == NULL) {
87 return EFI_OUT_OF_RESOURCES;
88 }
89
90 Qh->QhHw.Status = QTD_STAT_HALTED;
91 Ehc->PeriodOne = Qh;
92
93 return EFI_SUCCESS;
94 }
95
96 /**
97 Initialize the schedule data structure such as frame list.
98
99 @param Ehc The EHCI device to init schedule data for.
100
101 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
102 @retval EFI_SUCCESS The schedule data is initialized.
103
104 **/
105 EFI_STATUS
106 EhcInitSched (
107 IN PEI_USB2_HC_DEV *Ehc
108 )
109 {
110 EFI_PHYSICAL_ADDRESS PhyAddr;
111 VOID *Map;
112 UINTN Index;
113 UINT32 *Desc;
114 EFI_STATUS Status;
115
116 //
117 // First initialize the periodical schedule data:
118 // 1. Allocate and map the memory for the frame list
119 // 2. Create the help QTD/QH
120 // 3. Initialize the frame entries
121 // 4. Set the frame list register
122 //
123 //
124 // The Frame List ocupies 4K bytes,
125 // and must be aligned on 4-Kbyte boundaries.
126 //
127 Status = PeiServicesAllocatePages (
128 EfiBootServicesCode,
129 1,
130 &PhyAddr
131 );
132
133 Map = NULL;
134 Ehc->PeriodFrameHost = (VOID *)(UINTN)PhyAddr;
135 Ehc->PeriodFrame = (VOID *)(UINTN)PhyAddr;
136 Ehc->PeriodFrameMap = Map;
137 Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);
138
139 //
140 // Init memory pool management then create the helper
141 // QTD/QH. If failed, previously allocated resources
142 // will be freed by EhcFreeSched
143 //
144 Ehc->MemPool = UsbHcInitMemPool (
145 Ehc,
146 EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),
147 Ehc->High32bitAddr
148 );
149
150 if (Ehc->MemPool == NULL) {
151 return EFI_OUT_OF_RESOURCES;
152 }
153
154 Status = EhcCreateHelpQ (Ehc);
155
156 if (EFI_ERROR (Status)) {
157 return Status;
158 }
159
160 //
161 // Initialize the frame list entries then set the registers
162 //
163 Desc = (UINT32 *) Ehc->PeriodFrame;
164
165 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {
166 Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);
167 }
168
169 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));
170
171 //
172 // Second initialize the asynchronous schedule:
173 // Only need to set the AsynListAddr register to
174 // the reclamation header
175 //
176 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead));
177 return EFI_SUCCESS;
178 }
179
180 /**
181 Free the schedule data. It may be partially initialized.
182
183 @param Ehc The EHCI device.
184
185 **/
186 VOID
187 EhcFreeSched (
188 IN PEI_USB2_HC_DEV *Ehc
189 )
190 {
191 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);
192 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);
193
194 if (Ehc->PeriodOne != NULL) {
195 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));
196 Ehc->PeriodOne = NULL;
197 }
198
199 if (Ehc->ReclaimHead != NULL) {
200 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));
201 Ehc->ReclaimHead = NULL;
202 }
203
204 if (Ehc->ShortReadStop != NULL) {
205 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));
206 Ehc->ShortReadStop = NULL;
207 }
208
209 if (Ehc->MemPool != NULL) {
210 UsbHcFreeMemPool (Ehc->MemPool);
211 Ehc->MemPool = NULL;
212 }
213
214 if (Ehc->PeriodFrame != NULL) {
215 Ehc->PeriodFrame = NULL;
216 }
217 }
218
219 /**
220 Link the queue head to the asynchronous schedule list.
221 UEFI only supports one CTRL/BULK transfer at a time
222 due to its interfaces. This simplifies the AsynList
223 management: A reclamation header is always linked to
224 the AsyncListAddr, the only active QH is appended to it.
225
226 @param Ehc The EHCI device.
227 @param Qh The queue head to link.
228
229 **/
230 VOID
231 EhcLinkQhToAsync (
232 IN PEI_USB2_HC_DEV *Ehc,
233 IN PEI_EHC_QH *Qh
234 )
235 {
236 PEI_EHC_QH *Head;
237
238 //
239 // Append the queue head after the reclaim header, then
240 // fix the hardware visiable parts (EHCI R1.0 page 72).
241 // ReclaimHead is always linked to the EHCI's AsynListAddr.
242 //
243 Head = Ehc->ReclaimHead;
244
245 Qh->NextQh = Head->NextQh;
246 Head->NextQh = Qh;
247
248 Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);;
249 Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
250 }
251
252 /**
253 Unlink a queue head from the asynchronous schedule list.
254 Need to synchronize with hardware.
255
256 @param Ehc The EHCI device.
257 @param Qh The queue head to unlink.
258
259 **/
260 VOID
261 EhcUnlinkQhFromAsync (
262 IN PEI_USB2_HC_DEV *Ehc,
263 IN PEI_EHC_QH *Qh
264 )
265 {
266 PEI_EHC_QH *Head;
267 EFI_STATUS Status;
268
269 ASSERT (Ehc->ReclaimHead->NextQh == Qh);
270
271 //
272 // Remove the QH from reclamation head, then update the hardware
273 // visiable part: Only need to loopback the ReclaimHead. The Qh
274 // is pointing to ReclaimHead (which is staill in the list).
275 //
276 Head = Ehc->ReclaimHead;
277
278 Head->NextQh = Qh->NextQh;
279 Qh->NextQh = NULL;
280
281 Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);
282
283 //
284 // Set and wait the door bell to synchronize with the hardware
285 //
286 Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);
287
288 return;
289 }
290
291 /**
292 Link a queue head for interrupt transfer to the periodic
293 schedule frame list. This code is very much the same as
294 that in UHCI.
295
296 @param Ehc The EHCI device.
297 @param Qh The queue head to link.
298
299 **/
300 VOID
301 EhcLinkQhToPeriod (
302 IN PEI_USB2_HC_DEV *Ehc,
303 IN PEI_EHC_QH *Qh
304 )
305 {
306 UINT32 *Frames;
307 UINTN Index;
308 PEI_EHC_QH *Prev;
309 PEI_EHC_QH *Next;
310
311 Frames = Ehc->PeriodFrame;
312
313 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
314 //
315 // First QH can't be NULL because we always keep PeriodOne
316 // heads on the frame list
317 //
318 ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));
319 Next = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);
320 Prev = NULL;
321
322 //
323 // Now, insert the queue head (Qh) into this frame:
324 // 1. Find a queue head with the same poll interval, just insert
325 // Qh after this queue head, then we are done.
326 //
327 // 2. Find the position to insert the queue head into:
328 // Previous head's interval is bigger than Qh's
329 // Next head's interval is less than Qh's
330 // Then, insert the Qh between then
331 //
332 while (Next->Interval > Qh->Interval) {
333 Prev = Next;
334 Next = Next->NextQh;
335 }
336
337 ASSERT (Next != NULL);
338
339 //
340 // The entry may have been linked into the frame by early insertation.
341 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
342 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
343 // It isn't necessary to compare all the QH with the same interval to
344 // Qh. This is because if there is other QH with the same interval, Qh
345 // should has been inserted after that at Frames[0] and at Frames[0] it is
346 // impossible for (Next == Qh)
347 //
348 if (Next == Qh) {
349 continue;
350 }
351
352 if (Next->Interval == Qh->Interval) {
353 //
354 // If there is a QH with the same interval, it locates at
355 // Frames[0], and we can simply insert it after this QH. We
356 // are all done.
357 //
358 ASSERT ((Index == 0) && (Qh->NextQh == NULL));
359
360 Prev = Next;
361 Next = Next->NextQh;
362
363 Qh->NextQh = Next;
364 Prev->NextQh = Qh;
365
366 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
367 Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
368 break;
369 }
370
371 //
372 // OK, find the right position, insert it in. If Qh's next
373 // link has already been set, it is in position. This is
374 // guarranted by 2^n polling interval.
375 //
376 if (Qh->NextQh == NULL) {
377 Qh->NextQh = Next;
378 Qh->QhHw.HorizonLink = QH_LINK (Next, EHC_TYPE_QH, FALSE);
379 }
380
381 if (Prev == NULL) {
382 Frames[Index] = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
383 } else {
384 Prev->NextQh = Qh;
385 Prev->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);
386 }
387 }
388 }
389
390 /**
391 Unlink an interrupt queue head from the periodic
392 schedule frame list.
393
394 @param Ehc The EHCI device.
395 @param Qh The queue head to unlink.
396
397 **/
398 VOID
399 EhcUnlinkQhFromPeriod (
400 IN PEI_USB2_HC_DEV *Ehc,
401 IN PEI_EHC_QH *Qh
402 )
403 {
404 UINT32 *Frames;
405 UINTN Index;
406 PEI_EHC_QH *Prev;
407 PEI_EHC_QH *This;
408
409 Frames = Ehc->PeriodFrame;
410
411 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {
412 //
413 // Frame link can't be NULL because we always keep PeroidOne
414 // on the frame list
415 //
416 ASSERT (!EHC_LINK_TERMINATED (Frames[Index]));
417 This = EHC_ADDR (Ehc->High32bitAddr, Frames[Index]);
418 Prev = NULL;
419
420 //
421 // Walk through the frame's QH list to find the
422 // queue head to remove
423 //
424 while ((This != NULL) && (This != Qh)) {
425 Prev = This;
426 This = This->NextQh;
427 }
428
429 //
430 // Qh may have already been unlinked from this frame
431 // by early action. See the comments in EhcLinkQhToPeriod.
432 //
433 if (This == NULL) {
434 continue;
435 }
436
437 if (Prev == NULL) {
438 //
439 // Qh is the first entry in the frame
440 //
441 Frames[Index] = Qh->QhHw.HorizonLink;
442 } else {
443 Prev->NextQh = Qh->NextQh;
444 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
445 }
446 }
447 }
448
449 /**
450 Check the URB's execution result and update the URB's
451 result accordingly.
452
453 @param Ehc The EHCI device.
454 @param Urb The URB to check result.
455
456 @retval TRUE URB transfer is finialized.
457 @retval FALSE URB transfer is not finialized.
458
459 **/
460 BOOLEAN
461 EhcCheckUrbResult (
462 IN PEI_USB2_HC_DEV *Ehc,
463 IN PEI_URB *Urb
464 )
465 {
466 EFI_LIST_ENTRY *Entry;
467 PEI_EHC_QTD *Qtd;
468 QTD_HW *QtdHw;
469 UINT8 State;
470 BOOLEAN Finished;
471
472 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));
473
474 Finished = TRUE;
475 Urb->Completed = 0;
476
477 Urb->Result = EFI_USB_NOERROR;
478
479 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {
480 Urb->Result |= EFI_USB_ERR_SYSTEM;
481 goto ON_EXIT;
482 }
483
484 EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
485 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
486 QtdHw = &Qtd->QtdHw;
487 State = (UINT8) QtdHw->Status;
488
489 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {
490 //
491 // EHCI will halt the queue head when met some error.
492 // If it is halted, the result of URB is finialized.
493 //
494 if ((State & QTD_STAT_ERR_MASK) == 0) {
495 Urb->Result |= EFI_USB_ERR_STALL;
496 }
497
498 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {
499 Urb->Result |= EFI_USB_ERR_BABBLE;
500 }
501
502 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {
503 Urb->Result |= EFI_USB_ERR_BUFFER;
504 }
505
506 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {
507 Urb->Result |= EFI_USB_ERR_TIMEOUT;
508 }
509
510 Finished = TRUE;
511 goto ON_EXIT;
512
513 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {
514 //
515 // The QTD is still active, no need to check furthur.
516 //
517 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;
518
519 Finished = FALSE;
520 goto ON_EXIT;
521
522 } else {
523 //
524 // This QTD is finished OK or met short packet read. Update the
525 // transfer length if it isn't a setup.
526 //
527 if (QtdHw->Pid != QTD_PID_SETUP) {
528 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;
529 }
530
531 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {
532 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
533
534 //
535 // Short packet read condition. If it isn't a setup transfer,
536 // no need to check furthur: the queue head will halt at the
537 // ShortReadStop. If it is a setup transfer, need to check the
538 // Status Stage of the setup transfer to get the finial result
539 //
540 if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {
541
542 Finished = TRUE;
543 goto ON_EXIT;
544 }
545 }
546 }
547 }
548
549 ON_EXIT:
550 //
551 // Return the data toggle set by EHCI hardware, bulk and interrupt
552 // transfer will use this to initialize the next transaction. For
553 // Control transfer, it always start a new data toggle sequence for
554 // new transfer.
555 //
556 // NOTICE: don't move DT update before the loop, otherwise there is
557 // a race condition that DT is wrong.
558 //
559 Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;
560
561 return Finished;
562 }
563
564 /**
565 Execute the transfer by polling the URB. This is a synchronous operation.
566
567 @param Ehc The EHCI device.
568 @param Urb The URB to execute.
569 @param TimeOut The time to wait before abort, in millisecond.
570
571 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
572 @retval EFI_TIMEOUT The transfer failed due to time out.
573 @retval EFI_SUCCESS The transfer finished OK.
574
575 **/
576 EFI_STATUS
577 EhcExecTransfer (
578 IN PEI_USB2_HC_DEV *Ehc,
579 IN PEI_URB *Urb,
580 IN UINTN TimeOut
581 )
582 {
583 EFI_STATUS Status;
584 UINTN Index;
585 UINTN Loop;
586 BOOLEAN Finished;
587
588 Status = EFI_SUCCESS;
589 Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;
590 Finished = FALSE;
591
592 for (Index = 0; Index < Loop; Index++) {
593 Finished = EhcCheckUrbResult (Ehc, Urb);
594
595 if (Finished) {
596 break;
597 }
598
599 MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);
600 }
601
602 if (!Finished) {
603 Status = EFI_TIMEOUT;
604 } else if (Urb->Result != EFI_USB_NOERROR) {
605 Status = EFI_DEVICE_ERROR;
606 }
607
608 return Status;
609 }
610
611 /**
612 Delete a single asynchronous interrupt transfer for
613 the device and endpoint.
614
615 @param Ehc The EHCI device.
616 @param DevAddr The address of the target device.
617 @param EpNum The endpoint of the target.
618 @param DataToggle Return the next data toggle to use.
619
620 @retval EFI_NOT_FOUND No transfer for the device is found.
621 @retval EFI_SUCCESS An asynchronous transfer is removed.
622
623 **/
624 EFI_STATUS
625 EhciDelAsyncIntTransfer (
626 IN PEI_USB2_HC_DEV *Ehc,
627 IN UINT8 DevAddr,
628 IN UINT8 EpNum,
629 OUT UINT8 *DataToggle
630 )
631 {
632 EFI_LIST_ENTRY *Entry;
633 EFI_LIST_ENTRY *Next;
634 PEI_URB *Urb;
635 EFI_USB_DATA_DIRECTION Direction;
636
637 Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
638 EpNum &= 0x0F;
639
640 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
641 Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);
642
643 if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) &&
644 (Urb->Ep.Direction == Direction)) {
645 //
646 // Check the URB status to retrieve the next data toggle
647 // from the associated queue head.
648 //
649 EhcCheckUrbResult (Ehc, Urb);
650 *DataToggle = Urb->DataToggle;
651
652 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
653 RemoveEntryList (&Urb->UrbList);
654
655 EhcFreeUrb (Ehc, Urb);
656 return EFI_SUCCESS;
657 }
658 }
659
660 return EFI_NOT_FOUND;
661 }
662
663 /**
664 Remove all the asynchronous interrutp transfers.
665
666 @param Ehc The EHCI device.
667
668 **/
669 VOID
670 EhciDelAllAsyncIntTransfers (
671 IN PEI_USB2_HC_DEV *Ehc
672 )
673 {
674 EFI_LIST_ENTRY *Entry;
675 EFI_LIST_ENTRY *Next;
676 PEI_URB *Urb;
677
678 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
679 Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);
680
681 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);
682 RemoveEntryList (&Urb->UrbList);
683
684 EhcFreeUrb (Ehc, Urb);
685 }
686 }
687
688 /**
689 Flush data from PCI controller specific address to mapped system
690 memory address.
691
692 @param Ehc The EHCI device.
693 @param Urb The URB to unmap.
694
695 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
696 @retval EFI_SUCCESS Success to flush data to mapped system memory.
697
698 **/
699 EFI_STATUS
700 EhcFlushAsyncIntMap (
701 IN PEI_USB2_HC_DEV *Ehc,
702 IN PEI_URB *Urb
703 )
704 {
705 EFI_PHYSICAL_ADDRESS PhyAddr;
706
707 Urb->DataMap = NULL;
708 PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Urb->Data;
709 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
710 return EFI_SUCCESS;
711 }
712
713 /**
714 Update the queue head for next round of asynchronous transfer.
715
716 @param Urb The URB to update.
717
718 **/
719 VOID
720 EhcUpdateAsyncRequest (
721 IN PEI_URB *Urb
722 )
723 {
724 EFI_LIST_ENTRY *Entry;
725 PEI_EHC_QTD *FirstQtd;
726 QH_HW *QhHw;
727 PEI_EHC_QTD *Qtd;
728 QTD_HW *QtdHw;
729 UINTN Index;
730
731 Qtd = NULL;
732
733 if (Urb->Result == EFI_USB_NOERROR) {
734 FirstQtd = NULL;
735
736 EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {
737 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
738
739 if (FirstQtd == NULL) {
740 FirstQtd = Qtd;
741 }
742
743 //
744 // Update the QTD for next round of transfer. Host control
745 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
746 // Current Offset. These fields need to be updated. DT isn't
747 // used by interrupt transfer. It uses DT in queue head.
748 // Current Offset is in Page[0], only need to reset Page[0]
749 // to initial data buffer.
750 //
751 QtdHw = &Qtd->QtdHw;
752 QtdHw->Status = QTD_STAT_ACTIVE;
753 QtdHw->ErrCnt = QTD_MAX_ERR;
754 QtdHw->CurPage = 0;
755 QtdHw->TotalBytes = (UINT32) Qtd->DataLen;
756 QtdHw->Page[0] = EHC_LOW_32BIT (Qtd->Data);
757 }
758
759 //
760 // Update QH for next round of transfer. Host control only
761 // touch the fields in transfer overlay area. Only need to
762 // zero out the overlay area and set NextQtd to the first
763 // QTD. DateToggle bit is left untouched.
764 //
765 QhHw = &Urb->Qh->QhHw;
766 QhHw->CurQtd = QTD_LINK (0, TRUE);
767 QhHw->AltQtd = 0;
768
769 QhHw->Status = 0;
770 QhHw->Pid = 0;
771 QhHw->ErrCnt = 0;
772 QhHw->CurPage = 0;
773 QhHw->Ioc = 0;
774 QhHw->TotalBytes = 0;
775
776 for (Index = 0; Index < 5; Index++) {
777 QhHw->Page[Index] = 0;
778 QhHw->PageHigh[Index] = 0;
779 }
780
781 QhHw->NextQtd = QTD_LINK (FirstQtd, FALSE);
782 }
783
784 return ;
785 }
786
787 /**
788 Remove all the asynchronous interrutp transfers.
789
790 @param Event Interrupt event.
791 @param Context Pointer to PEI_USB2_HC_DEV.
792
793 **/
794 VOID
795 EFIAPI
796 EhcMoniteAsyncRequests (
797 IN EFI_EVENT Event,
798 IN VOID *Context
799 )
800 {
801 PEI_USB2_HC_DEV *Ehc;
802 EFI_LIST_ENTRY *Entry;
803 EFI_LIST_ENTRY *Next;
804 BOOLEAN Finished;
805 UINT8 *ProcBuf;
806 PEI_URB *Urb;
807 EFI_STATUS Status;
808 UINTN PageNumber;
809
810 Ehc = (PEI_USB2_HC_DEV *) Context;
811
812 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {
813 Urb = EFI_LIST_CONTAINER (Entry, PEI_URB, UrbList);
814
815 //
816 // Check the result of URB execution. If it is still
817 // active, check the next one.
818 //
819 Finished = EhcCheckUrbResult (Ehc, Urb);
820
821 if (!Finished) {
822 continue;
823 }
824
825 //
826 // Flush any PCI posted write transactions from a PCI host
827 // bridge to system memory.
828 //
829 Status = EhcFlushAsyncIntMap (Ehc, Urb);
830
831 //
832 // Allocate a buffer then copy the transferred data for user.
833 // If failed to allocate the buffer, update the URB for next
834 // round of transfer. Ignore the data of this round.
835 //
836 ProcBuf = NULL;
837
838 if (Urb->Result == EFI_USB_NOERROR) {
839 ASSERT (Urb->Completed <= Urb->DataLen);
840 PageNumber = Urb->Completed/PAGESIZE +1;
841 Status = PeiServicesAllocatePages (
842 EfiBootServicesCode,
843 PageNumber,
844 (EFI_PHYSICAL_ADDRESS *)ProcBuf
845 );
846 if (ProcBuf == NULL) {
847 EhcUpdateAsyncRequest (Urb);
848 continue;
849 }
850
851 CopyMem (ProcBuf, Urb->Data, Urb->Completed);
852 }
853
854 EhcUpdateAsyncRequest (Urb);
855
856 //
857 // Leave error recovery to its related device driver. A
858 // common case of the error recovery is to re-submit the
859 // interrupt transfer which is linked to the head of the
860 // list. This function scans from head to tail. So the
861 // re-submitted interrupt transfer's callback function
862 // will not be called again in this round. Don't touch this
863 // URB after the callback, it may have been removed by the
864 // callback.
865 //
866 if (Urb->Callback != NULL) {
867 (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);
868 }
869
870 if (ProcBuf != NULL) {
871 }
872 }
873 }