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