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