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