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