]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/UhciDxe/UhciSched.c
In various archs, Processor memory address may not be same with Pci memory address...
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / UhciDxe / UhciSched.c
1 /** @file
2
3 The EHCI register operation routines.
4
5 Copyright (c) 2007 - 2009, 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 "Uhci.h"
17
18
19 /**
20 Create Frame List Structure.
21
22 @param Uhc UHCI device.
23
24 @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources.
25 @retval EFI_UNSUPPORTED Map memory fail.
26 @retval EFI_SUCCESS Success.
27
28 **/
29 EFI_STATUS
30 UhciInitFrameList (
31 IN USB_HC_DEV *Uhc
32 )
33 {
34 EFI_PHYSICAL_ADDRESS MappedAddr;
35 EFI_STATUS Status;
36 VOID *Buffer;
37 VOID *Mapping;
38 UINTN Pages;
39 UINTN Bytes;
40 UINTN Index;
41 UINTN Len;
42 EFI_PHYSICAL_ADDRESS PhyAddr;
43
44 //
45 // The Frame List is a common buffer that will be
46 // accessed by both the cpu and the usb bus master
47 // at the same time. The Frame List ocupies 4K bytes,
48 // and must be aligned on 4-Kbyte boundaries.
49 //
50 Bytes = 4096;
51 Pages = EFI_SIZE_TO_PAGES (Bytes);
52
53 Status = Uhc->PciIo->AllocateBuffer (
54 Uhc->PciIo,
55 AllocateAnyPages,
56 EfiBootServicesData,
57 Pages,
58 &Buffer,
59 0
60 );
61
62 if (EFI_ERROR (Status)) {
63 return EFI_OUT_OF_RESOURCES;
64 }
65
66 Status = Uhc->PciIo->Map (
67 Uhc->PciIo,
68 EfiPciIoOperationBusMasterCommonBuffer,
69 Buffer,
70 &Bytes,
71 &MappedAddr,
72 &Mapping
73 );
74
75 if (EFI_ERROR (Status) || (Bytes != 4096)) {
76 Status = EFI_UNSUPPORTED;
77 goto ON_ERROR;
78 }
79
80 Uhc->FrameBase = (UINT32 *) (UINTN) Buffer; // Cpu memory address
81 Uhc->FrameBasePciMemAddr = (UINT32 *) (UINTN) MappedAddr; // Pci memory address
82 Uhc->FrameMapping = Mapping;
83
84 //
85 // Allocate the QH used by sync interrupt/control/bulk transfer.
86 // FS ctrl/bulk queue head is set to loopback so additional BW
87 // can be reclaimed. Notice, LS don't support bulk transfer and
88 // also doesn't support BW reclamation.
89 //
90 Uhc->SyncIntQh = UhciCreateQh (Uhc, 1);
91 Uhc->CtrlQh = UhciCreateQh (Uhc, 1);
92 Uhc->BulkQh = UhciCreateQh (Uhc, 1);
93
94 if ((Uhc->SyncIntQh == NULL) || (Uhc->CtrlQh == NULL) || (Uhc->BulkQh == NULL)) {
95 Uhc->PciIo->Unmap (Uhc->PciIo, Mapping);
96 Status = EFI_OUT_OF_RESOURCES;
97 goto ON_ERROR;
98 }
99
100 //
101 // +-------------+
102 // | |
103 // Link the three together: SyncIntQh->CtrlQh->BulkQh <---------+
104 // Each frame entry is linked to this sequence of QH. These QH
105 // will remain on the schedul, never got removed
106 //
107 Len = sizeof (UHCI_QH_HW);
108 Status = Uhc->PciIo->Map (
109 Uhc->PciIo,
110 EfiPciIoOperationBusMasterRead,
111 Uhc->CtrlQh,
112 &Len,
113 &PhyAddr,
114 &Mapping
115 );
116 ASSERT (!EFI_ERROR (Status));
117
118 Uhc->SyncIntQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
119 Uhc->SyncIntQh->NextQh = Uhc->CtrlQh;
120
121 Status = Uhc->PciIo->Map (
122 Uhc->PciIo,
123 EfiPciIoOperationBusMasterRead,
124 Uhc->BulkQh,
125 &Len,
126 &PhyAddr,
127 &Mapping
128 );
129 ASSERT (!EFI_ERROR (Status));
130
131 Uhc->CtrlQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
132 Uhc->CtrlQh->NextQh = Uhc->BulkQh;
133
134 //
135 // Some old platform such as Intel's Tiger 4 has a difficult time
136 // in supporting the full speed bandwidth reclamation in the previous
137 // mentioned form. Most new platforms don't suffer it.
138 //
139 Uhc->BulkQh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
140
141 Uhc->BulkQh->NextQh = NULL;
142
143 Len = sizeof (UHCI_QH_HW);
144 Status = Uhc->PciIo->Map (
145 Uhc->PciIo,
146 EfiPciIoOperationBusMasterRead,
147 Uhc->SyncIntQh,
148 &Len,
149 &PhyAddr,
150 &Mapping
151 );
152 ASSERT (!EFI_ERROR (Status));
153
154 for (Index = 0; Index < UHCI_FRAME_NUM; Index++) {
155 Uhc->FrameBase[Index] = QH_HLINK (Uhc->SyncIntQh, FALSE);
156 Uhc->FrameBasePciMemAddr[Index] = QH_HLINK (PhyAddr, FALSE);
157 }
158
159 //
160 // Tell the Host Controller where the Frame List lies,
161 // by set the Frame List Base Address Register.
162 //
163 UhciSetFrameListBaseAddr (Uhc->PciIo, (VOID *) (Uhc->FrameBasePciMemAddr));
164 return EFI_SUCCESS;
165
166 ON_ERROR:
167 if (Uhc->SyncIntQh != NULL) {
168 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
169 }
170
171 if (Uhc->CtrlQh != NULL) {
172 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
173 }
174
175 if (Uhc->BulkQh != NULL) {
176 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
177 }
178
179 Uhc->PciIo->FreeBuffer (Uhc->PciIo, Pages, Buffer);
180 return Status;
181 }
182
183
184 /**
185 Destory FrameList buffer.
186
187 @param Uhc The UHCI device.
188
189 **/
190 VOID
191 UhciDestoryFrameList (
192 IN USB_HC_DEV *Uhc
193 )
194 {
195 //
196 // Unmap the common buffer for framelist entry,
197 // and free the common buffer.
198 // Uhci's frame list occupy 4k memory.
199 //
200 Uhc->PciIo->Unmap (Uhc->PciIo, Uhc->FrameMapping);
201
202 Uhc->PciIo->FreeBuffer (
203 Uhc->PciIo,
204 EFI_SIZE_TO_PAGES (4096),
205 (VOID *) Uhc->FrameBase
206 );
207
208 if (Uhc->SyncIntQh != NULL) {
209 UsbHcFreeMem (Uhc->MemPool, Uhc->SyncIntQh, sizeof (UHCI_QH_SW));
210 }
211
212 if (Uhc->CtrlQh != NULL) {
213 UsbHcFreeMem (Uhc->MemPool, Uhc->CtrlQh, sizeof (UHCI_QH_SW));
214 }
215
216 if (Uhc->BulkQh != NULL) {
217 UsbHcFreeMem (Uhc->MemPool, Uhc->BulkQh, sizeof (UHCI_QH_SW));
218 }
219
220 Uhc->FrameBase = NULL;
221 Uhc->FrameBasePciMemAddr = NULL;
222 Uhc->SyncIntQh = NULL;
223 Uhc->CtrlQh = NULL;
224 Uhc->BulkQh = NULL;
225 }
226
227
228 /**
229 Convert the poll rate to the maxium 2^n that is smaller
230 than Interval.
231
232 @param Interval The poll rate to convert.
233
234 @return The converted poll rate.
235
236 **/
237 UINTN
238 UhciConvertPollRate (
239 IN UINTN Interval
240 )
241 {
242 UINTN BitCount;
243
244 ASSERT (Interval != 0);
245
246 //
247 // Find the index (1 based) of the highest non-zero bit
248 //
249 BitCount = 0;
250
251 while (Interval != 0) {
252 Interval >>= 1;
253 BitCount++;
254 }
255
256 return (UINTN)1 << (BitCount - 1);
257 }
258
259
260 /**
261 Link a queue head (for asynchronous interrupt transfer) to
262 the frame list.
263
264 @param Uhc The UHCI device.
265 @param Qh The queue head to link into.
266
267 **/
268 VOID
269 UhciLinkQhToFrameList (
270 USB_HC_DEV *Uhc,
271 UHCI_QH_SW *Qh
272 )
273 {
274 UINTN Index;
275 UHCI_QH_SW *Prev;
276 UHCI_QH_SW *Next;
277 UINTN Len;
278 EFI_PHYSICAL_ADDRESS PhyAddr;
279 EFI_PHYSICAL_ADDRESS QhPciAddr;
280 VOID* Map;
281 EFI_STATUS Status;
282
283 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
284
285 Len = sizeof (UHCI_QH_HW);
286 Status = Uhc->PciIo->Map (
287 Uhc->PciIo,
288 EfiPciIoOperationBusMasterRead,
289 Qh,
290 &Len,
291 &QhPciAddr,
292 &Map
293 );
294 ASSERT (!EFI_ERROR (Status));
295
296 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
297 //
298 // First QH can't be NULL because we always keep static queue
299 // heads on the frame list
300 //
301 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
302 Next = UHCI_ADDR (Uhc->FrameBase[Index]);
303 Prev = NULL;
304
305 //
306 // Now, insert the queue head (Qh) into this frame:
307 // 1. Find a queue head with the same poll interval, just insert
308 // Qh after this queue head, then we are done.
309 //
310 // 2. Find the position to insert the queue head into:
311 // Previous head's interval is bigger than Qh's
312 // Next head's interval is less than Qh's
313 // Then, insert the Qh between then
314 //
315 // This method is very much the same as that used by EHCI.
316 // Because each QH's interval is round down to 2^n, poll
317 // rate is correct.
318 //
319 while (Next->Interval > Qh->Interval) {
320 Prev = Next;
321 Next = Next->NextQh;
322 ASSERT (Next != NULL);
323 }
324
325 //
326 // The entry may have been linked into the frame by early insertation.
327 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
328 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
329 // It isn't necessary to compare all the QH with the same interval to
330 // Qh. This is because if there is other QH with the same interval, Qh
331 // should has been inserted after that at FrameBase[0] and at FrameBase[0] it is
332 // impossible (Next == Qh)
333 //
334 if (Next == Qh) {
335 continue;
336 }
337
338 if (Next->Interval == Qh->Interval) {
339 //
340 // If there is a QH with the same interval, it locates at
341 // FrameBase[0], and we can simply insert it after this QH. We
342 // are all done.
343 //
344 ASSERT ((Index == 0) && (Qh->NextQh == NULL));
345
346 Prev = Next;
347 Next = Next->NextQh;
348
349 Qh->NextQh = Next;
350 Prev->NextQh = Qh;
351
352 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;
353
354 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
355 break;
356 }
357
358 //
359 // OK, find the right position, insert it in. If Qh's next
360 // link has already been set, it is in position. This is
361 // guarranted by 2^n polling interval.
362 //
363 if (Qh->NextQh == NULL) {
364 Qh->NextQh = Next;
365
366 Len = sizeof (UHCI_QH_HW);
367 Status = Uhc->PciIo->Map (
368 Uhc->PciIo,
369 EfiPciIoOperationBusMasterRead,
370 Next,
371 &Len,
372 &PhyAddr,
373 &Map
374 );
375 ASSERT (!EFI_ERROR (Status));
376
377 Qh->QhHw.HorizonLink = QH_HLINK (PhyAddr, FALSE);
378 }
379
380 if (Prev == NULL) {
381 Uhc->FrameBase[Index] = QH_HLINK (Qh, FALSE);
382 Uhc->FrameBasePciMemAddr[Index] = QH_HLINK (QhPciAddr, FALSE);
383 } else {
384 Prev->NextQh = Qh;
385 Prev->QhHw.HorizonLink = QH_HLINK (QhPciAddr, FALSE);
386 }
387 }
388 }
389
390
391 /**
392 Unlink QH from the frame list is easier: find all
393 the precedence node, and pointer there next to QhSw's
394 next.
395
396 @param Uhc The UHCI device.
397 @param Qh The queue head to unlink.
398
399 **/
400 VOID
401 UhciUnlinkQhFromFrameList (
402 USB_HC_DEV *Uhc,
403 UHCI_QH_SW *Qh
404 )
405 {
406 UINTN Index;
407 UHCI_QH_SW *Prev;
408 UHCI_QH_SW *This;
409
410 ASSERT ((Uhc->FrameBase != NULL) && (Qh != NULL));
411
412 for (Index = 0; Index < UHCI_FRAME_NUM; Index += Qh->Interval) {
413 //
414 // Frame link can't be NULL because we always keep static
415 // queue heads on the frame list
416 //
417 ASSERT (!LINK_TERMINATED (Uhc->FrameBase[Index]));
418 This = UHCI_ADDR (Uhc->FrameBase[Index]);
419 Prev = NULL;
420
421 //
422 // Walk through the frame's QH list to find the
423 // queue head to remove
424 //
425 while ((This != NULL) && (This != Qh)) {
426 Prev = This;
427 This = This->NextQh;
428 }
429
430 //
431 // Qh may have already been unlinked from this frame
432 // by early action.
433 //
434 if (This == NULL) {
435 continue;
436 }
437
438 if (Prev == NULL) {
439 //
440 // Qh is the first entry in the frame
441 //
442 Uhc->FrameBase[Index] = (UINT32)(UINTN)Qh->NextQh;
443 Uhc->FrameBasePciMemAddr[Index] = Qh->QhHw.HorizonLink;
444 } else {
445 Prev->NextQh = Qh->NextQh;
446 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;
447 }
448 }
449 }
450
451
452 /**
453 Check TDs Results.
454
455 @param Uhc This UHCI device.
456 @param Td UHCI_TD_SW to check.
457 @param IsLow Is Low Speed Device.
458 @param QhResult Return the result of this TD list.
459
460 @return Whether the TD's result is finialized.
461
462 **/
463 BOOLEAN
464 UhciCheckTdStatus (
465 IN USB_HC_DEV *Uhc,
466 IN UHCI_TD_SW *Td,
467 IN BOOLEAN IsLow,
468 OUT UHCI_QH_RESULT *QhResult
469 )
470 {
471 UINTN Len;
472 UINT8 State;
473 UHCI_TD_HW *TdHw;
474 BOOLEAN Finished;
475
476 Finished = TRUE;
477
478 //
479 // Initialize the data toggle to that of the first
480 // TD. The next toggle to use is either:
481 // 1. first TD's toggle if no TD is executed OK
482 // 2. the next toggle of last executed-OK TD
483 //
484 QhResult->Result = EFI_USB_NOERROR;
485 QhResult->NextToggle = (UINT8)Td->TdHw.DataToggle;
486 QhResult->Complete = 0;
487
488 while (Td != NULL) {
489 TdHw = &Td->TdHw;
490 State = (UINT8)TdHw->Status;
491
492 //
493 // UHCI will set STALLED bit when it abort the execution
494 // of TD list. There are several reasons:
495 // 1. BABBLE error happened
496 // 2. Received a STALL response
497 // 3. Error count decreased to zero.
498 //
499 // It also set CRC/Timeout/NAK/Buffer Error/BitStuff Error
500 // bits when corresponding conditions happen. But these
501 // conditions are not deadly, that is a TD can successfully
502 // completes even these bits are set. But it is likely that
503 // upper layer won't distinguish these condtions. So, only
504 // set these bits when TD is actually halted.
505 //
506 if ((State & USBTD_STALLED) != 0) {
507 if ((State & USBTD_BABBLE) != 0) {
508 QhResult->Result |= EFI_USB_ERR_BABBLE;
509
510 } else if (TdHw->ErrorCount != 0) {
511 QhResult->Result |= EFI_USB_ERR_STALL;
512 }
513
514 if ((State & USBTD_CRC) != 0) {
515 QhResult->Result |= EFI_USB_ERR_CRC;
516 }
517
518 if ((State & USBTD_BUFFERR) != 0) {
519 QhResult->Result |= EFI_USB_ERR_BUFFER;
520 }
521
522 if ((Td->TdHw.Status & USBTD_BITSTUFF) != 0) {
523 QhResult->Result |= EFI_USB_ERR_BITSTUFF;
524 }
525
526 if (TdHw->ErrorCount == 0) {
527 QhResult->Result |= EFI_USB_ERR_TIMEOUT;
528 }
529
530 Finished = TRUE;
531 goto ON_EXIT;
532
533 } else if ((State & USBTD_ACTIVE) != 0) {
534 //
535 // The TD is still active, no need to check further.
536 //
537 QhResult->Result |= EFI_USB_ERR_NOTEXECUTE;
538
539 Finished = FALSE;
540 goto ON_EXIT;
541
542 } else {
543 //
544 // Update the next data toggle, it is always the
545 // next to the last known-good TD's data toggle if
546 // any TD is executed OK
547 //
548 QhResult->NextToggle = (UINT8) (1 - (UINT8)TdHw->DataToggle);
549
550 //
551 // This TD is finished OK or met short packet read. Update the
552 // transfer length if it isn't a SETUP.
553 //
554 Len = (TdHw->ActualLen + 1) & 0x7FF;
555
556 if (TdHw->PidCode != SETUP_PACKET_ID) {
557 QhResult->Complete += Len;
558 }
559
560 //
561 // Short packet condition for full speed input TD, also
562 // terminate the transfer
563 //
564 if (!IsLow && (TdHw->ShortPacket == 1) && (Len < Td->DataLen)) {
565 DEBUG ((EFI_D_INFO, "UhciCheckTdStatus: short packet read occured\n"));
566
567 Finished = TRUE;
568 goto ON_EXIT;
569 }
570 }
571
572 Td = Td->NextTd;
573 }
574
575 ON_EXIT:
576 //
577 // Check whether HC is halted. Don't move this up. It must be
578 // called after data toggle is successfully updated.
579 //
580 if (!UhciIsHcWorking (Uhc->PciIo)) {
581 QhResult->Result |= EFI_USB_ERR_SYSTEM;
582 Finished = TRUE;
583 }
584
585 if (Finished) {
586 Uhc->PciIo->Flush (Uhc->PciIo);
587 }
588
589 UhciAckAllInterrupt (Uhc);
590 return Finished;
591 }
592
593
594 /**
595 Check the result of the transfer.
596
597 @param Uhc The UHCI device.
598 @param Qh The queue head of the transfer.
599 @param Td The first TDs of the transfer.
600 @param TimeOut TimeOut value in milliseconds.
601 @param IsLow Is Low Speed Device.
602 @param QhResult The variable to return result.
603
604 @retval EFI_SUCCESS The transfer finished with success.
605 @retval EFI_DEVICE_ERROR Transfer failed.
606
607 **/
608 EFI_STATUS
609 UhciExecuteTransfer (
610 IN USB_HC_DEV *Uhc,
611 IN UHCI_QH_SW *Qh,
612 IN UHCI_TD_SW *Td,
613 IN UINTN TimeOut,
614 IN BOOLEAN IsLow,
615 OUT UHCI_QH_RESULT *QhResult
616 )
617 {
618 UINTN Index;
619 UINTN Delay;
620 BOOLEAN Finished;
621 EFI_STATUS Status;
622
623 Finished = FALSE;
624 Status = EFI_SUCCESS;
625 Delay = (TimeOut * UHC_1_MILLISECOND / UHC_SYNC_POLL_INTERVAL) + 1;
626
627 for (Index = 0; Index < Delay; Index++) {
628 Finished = UhciCheckTdStatus (Uhc, Td, IsLow, QhResult);
629
630 //
631 // Transfer is OK or some error occured (TD inactive)
632 //
633 if (Finished) {
634 break;
635 }
636
637 gBS->Stall (UHC_SYNC_POLL_INTERVAL);
638 }
639
640 if (!Finished) {
641 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution not finished for %dms\n", (UINT32)TimeOut));
642 UhciDumpQh (Qh);
643 UhciDumpTds (Td);
644
645 Status = EFI_TIMEOUT;
646
647 } else if (QhResult->Result != EFI_USB_NOERROR) {
648 DEBUG ((EFI_D_ERROR, "UhciExecuteTransfer: execution failed with result %x\n", QhResult->Result));
649 UhciDumpQh (Qh);
650 UhciDumpTds (Td);
651
652 Status = EFI_DEVICE_ERROR;
653 }
654
655 return Status;
656 }
657
658
659 /**
660 Update Async Request, QH and TDs.
661
662 @param Uhc The UHCI device.
663 @param AsyncReq The UHCI asynchronous transfer to update.
664 @param Result Transfer reslut.
665 @param NextToggle The toggle of next data.
666
667 **/
668 VOID
669 UhciUpdateAsyncReq (
670 IN USB_HC_DEV *Uhc,
671 IN UHCI_ASYNC_REQUEST *AsyncReq,
672 IN UINT32 Result,
673 IN UINT32 NextToggle
674 )
675 {
676 UHCI_QH_SW *Qh;
677 UHCI_TD_SW *FirstTd;
678 UHCI_TD_SW *Td;
679
680 Qh = AsyncReq->QhSw;
681 FirstTd = AsyncReq->FirstTd;
682
683 if (Result == EFI_USB_NOERROR) {
684 //
685 // The last transfer succeeds. Then we need to update
686 // the Qh and Td for next round of transfer.
687 // 1. Update the TD's data toggle
688 // 2. Activate all the TDs
689 // 3. Link the TD to the queue head again since during
690 // execution, queue head's TD pointer is changed by
691 // hardware.
692 //
693 for (Td = FirstTd; Td != NULL; Td = Td->NextTd) {
694 Td->TdHw.DataToggle = NextToggle;
695 NextToggle ^= 1;
696 Td->TdHw.Status |= USBTD_ACTIVE;
697 }
698
699 UhciLinkTdToQh (Uhc, Qh, FirstTd);
700 return ;
701 }
702 }
703
704
705 /**
706 Create Async Request node, and Link to List.
707
708 @param Uhc The UHCI device.
709 @param Qh The queue head of the transfer.
710 @param FirstTd First TD of the transfer.
711 @param DevAddr Device Address.
712 @param EndPoint EndPoint Address.
713 @param DataLen Data length.
714 @param Interval Polling Interval when inserted to frame list.
715 @param Mapping Mapping value.
716 @param Data Data buffer, unmapped.
717 @param Callback Callback after interrupt transfeer.
718 @param Context Callback Context passed as function parameter.
719 @param IsLow Is Low Speed.
720
721 @retval EFI_SUCCESS An asynchronous transfer is created.
722 @retval EFI_INVALID_PARAMETER Paremeter is error.
723 @retval EFI_OUT_OF_RESOURCES Failed because of resource shortage.
724
725 **/
726 EFI_STATUS
727 UhciCreateAsyncReq (
728 IN USB_HC_DEV *Uhc,
729 IN UHCI_QH_SW *Qh,
730 IN UHCI_TD_SW *FirstTd,
731 IN UINT8 DevAddr,
732 IN UINT8 EndPoint,
733 IN UINTN DataLen,
734 IN UINTN Interval,
735 IN VOID *Mapping,
736 IN UINT8 *Data,
737 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
738 IN VOID *Context,
739 IN BOOLEAN IsLow
740 )
741 {
742 UHCI_ASYNC_REQUEST *AsyncReq;
743
744 AsyncReq = AllocatePool (sizeof (UHCI_ASYNC_REQUEST));
745
746 if (AsyncReq == NULL) {
747 return EFI_OUT_OF_RESOURCES;
748 }
749
750 //
751 // Fill Request field. Data is allocated host memory, not mapped
752 //
753 AsyncReq->Signature = UHCI_ASYNC_INT_SIGNATURE;
754 AsyncReq->DevAddr = DevAddr;
755 AsyncReq->EndPoint = EndPoint;
756 AsyncReq->DataLen = DataLen;
757 AsyncReq->Interval = UhciConvertPollRate(Interval);
758 AsyncReq->Mapping = Mapping;
759 AsyncReq->Data = Data;
760 AsyncReq->Callback = Callback;
761 AsyncReq->Context = Context;
762 AsyncReq->QhSw = Qh;
763 AsyncReq->FirstTd = FirstTd;
764 AsyncReq->IsLow = IsLow;
765
766 //
767 // Insert the new interrupt transfer to the head of the list.
768 // The interrupt transfer's monitor function scans the whole
769 // list from head to tail. The new interrupt transfer MUST be
770 // added to the head of the list.
771 //
772 InsertHeadList (&(Uhc->AsyncIntList), &(AsyncReq->Link));
773
774 return EFI_SUCCESS;
775 }
776
777
778 /**
779 Free an asynchronous request's resource such as memory.
780
781 @param Uhc The UHCI device.
782 @param AsyncReq The asynchronous request to free.
783
784 **/
785 VOID
786 UhciFreeAsyncReq (
787 IN USB_HC_DEV *Uhc,
788 IN UHCI_ASYNC_REQUEST *AsyncReq
789 )
790 {
791 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
792
793 UhciDestoryTds (Uhc, AsyncReq->FirstTd);
794 UsbHcFreeMem (Uhc->MemPool, AsyncReq->QhSw, sizeof (UHCI_QH_SW));
795
796 if (AsyncReq->Mapping != NULL) {
797 Uhc->PciIo->Unmap (Uhc->PciIo, AsyncReq->Mapping);
798 }
799
800 if (AsyncReq->Data != NULL) {
801 gBS->FreePool (AsyncReq->Data);
802 }
803
804 gBS->FreePool (AsyncReq);
805 }
806
807
808 /**
809 Unlink an asynchronous request's from UHC's asynchronus list.
810 also remove the queue head from the frame list. If FreeNow,
811 release its resource also. Otherwise, add the request to the
812 UHC's recycle list to wait for a while before release the memory.
813 Until then, hardware won't hold point to the request.
814
815 @param Uhc The UHCI device.
816 @param AsyncReq The asynchronous request to free.
817 @param FreeNow If TRUE, free the resource immediately, otherwise
818 add the request to recycle wait list.
819
820 **/
821 VOID
822 UhciUnlinkAsyncReq (
823 IN USB_HC_DEV *Uhc,
824 IN UHCI_ASYNC_REQUEST *AsyncReq,
825 IN BOOLEAN FreeNow
826 )
827 {
828 ASSERT ((Uhc != NULL) && (AsyncReq != NULL));
829
830 RemoveEntryList (&(AsyncReq->Link));
831 UhciUnlinkQhFromFrameList (Uhc, AsyncReq->QhSw);
832
833 if (FreeNow) {
834 UhciFreeAsyncReq (Uhc, AsyncReq);
835 } else {
836 //
837 // To sychronize with hardware, mark the queue head as inactive
838 // then add AsyncReq to UHC's recycle list
839 //
840 AsyncReq->QhSw->QhHw.VerticalLink = QH_VLINK (NULL, TRUE);
841 AsyncReq->Recycle = Uhc->RecycleWait;
842 Uhc->RecycleWait = AsyncReq;
843 }
844 }
845
846
847 /**
848 Delete Async Interrupt QH and TDs.
849
850 @param Uhc The UHCI device.
851 @param DevAddr Device Address.
852 @param EndPoint EndPoint Address.
853 @param Toggle The next data toggle to use.
854
855 @retval EFI_SUCCESS The request is deleted.
856 @retval EFI_INVALID_PARAMETER Paremeter is error.
857 @retval EFI_NOT_FOUND The asynchronous isn't found.
858
859 **/
860 EFI_STATUS
861 UhciRemoveAsyncReq (
862 IN USB_HC_DEV *Uhc,
863 IN UINT8 DevAddr,
864 IN UINT8 EndPoint,
865 OUT UINT8 *Toggle
866 )
867 {
868 EFI_STATUS Status;
869 UHCI_ASYNC_REQUEST *AsyncReq;
870 UHCI_QH_RESULT QhResult;
871 LIST_ENTRY *Link;
872 BOOLEAN Found;
873
874 Status = EFI_SUCCESS;
875
876 //
877 // If no asynchronous interrupt transaction exists
878 //
879 if (IsListEmpty (&(Uhc->AsyncIntList))) {
880 return EFI_SUCCESS;
881 }
882
883 //
884 // Find the asynchronous transfer to this device/endpoint pair
885 //
886 Found = FALSE;
887 Link = Uhc->AsyncIntList.ForwardLink;
888
889 do {
890 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
891 Link = Link->ForwardLink;
892
893 if ((AsyncReq->DevAddr == DevAddr) && (AsyncReq->EndPoint == EndPoint)) {
894 Found = TRUE;
895 break;
896 }
897
898 } while (Link != &(Uhc->AsyncIntList));
899
900 if (!Found) {
901 return EFI_NOT_FOUND;
902 }
903
904 //
905 // Check the result of the async transfer then update it
906 // to get the next data toggle to use.
907 //
908 UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
909 *Toggle = QhResult.NextToggle;
910
911 //
912 // Don't release the request now, keep it to synchronize with hardware.
913 //
914 UhciUnlinkAsyncReq (Uhc, AsyncReq, FALSE);
915 return Status;
916 }
917
918
919 /**
920 Recycle the asynchronouse request. When a queue head
921 is unlinked from frame list, host controller hardware
922 may still hold a cached pointer to it. To synchronize
923 with hardware, the request is released in two steps:
924 first it is linked to the UHC's RecycleWait list. At
925 the next time UhciMonitorAsyncReqList is fired, it is
926 moved to UHC's Recylelist. Then, at another timer
927 activation, all the requests on Recycle list is freed.
928 This guarrantes that each unlink queue head keeps
929 existing for at least 50ms, far enough for the hardware
930 to clear its cache.
931
932 @param Uhc The UHCI device.
933
934 **/
935 VOID
936 UhciRecycleAsyncReq (
937 IN USB_HC_DEV *Uhc
938 )
939 {
940 UHCI_ASYNC_REQUEST *Req;
941 UHCI_ASYNC_REQUEST *Next;
942
943 Req = Uhc->Recycle;
944
945 while (Req != NULL) {
946 Next = Req->Recycle;
947 UhciFreeAsyncReq (Uhc, Req);
948 Req = Next;
949 }
950
951 Uhc->Recycle = Uhc->RecycleWait;
952 Uhc->RecycleWait = NULL;
953 }
954
955
956
957 /**
958 Release all the asynchronous transfers on the lsit.
959
960 @param Uhc The UHCI device.
961
962 **/
963 VOID
964 UhciFreeAllAsyncReq (
965 IN USB_HC_DEV *Uhc
966 )
967 {
968 LIST_ENTRY *Head;
969 UHCI_ASYNC_REQUEST *AsyncReq;
970
971 //
972 // Call UhciRecycleAsyncReq twice. The requests on Recycle
973 // will be released at the first call; The requests on
974 // RecycleWait will be released at the second call.
975 //
976 UhciRecycleAsyncReq (Uhc);
977 UhciRecycleAsyncReq (Uhc);
978
979 Head = &(Uhc->AsyncIntList);
980
981 if (IsListEmpty (Head)) {
982 return;
983 }
984
985 while (!IsListEmpty (Head)) {
986 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Head->ForwardLink);
987 UhciUnlinkAsyncReq (Uhc, AsyncReq, TRUE);
988 }
989 }
990
991
992 /**
993 Interrupt transfer periodic check handler.
994
995 @param Event The event of the time.
996 @param Context Context of the event, pointer to USB_HC_DEV.
997
998 **/
999 VOID
1000 EFIAPI
1001 UhciMonitorAsyncReqList (
1002 IN EFI_EVENT Event,
1003 IN VOID *Context
1004 )
1005 {
1006 UHCI_ASYNC_REQUEST *AsyncReq;
1007 LIST_ENTRY *Link;
1008 USB_HC_DEV *Uhc;
1009 VOID *Data;
1010 BOOLEAN Finished;
1011 UHCI_QH_RESULT QhResult;
1012
1013 Uhc = (USB_HC_DEV *) Context;
1014
1015 //
1016 // Recycle the asynchronous requests expired, and promote
1017 // requests waiting to be recycled the next time when this
1018 // timer expires
1019 //
1020 UhciRecycleAsyncReq (Uhc);
1021
1022 if (IsListEmpty (&(Uhc->AsyncIntList))) {
1023 return ;
1024 }
1025
1026 //
1027 // This loop must be delete safe
1028 //
1029 Link = Uhc->AsyncIntList.ForwardLink;
1030
1031 do {
1032 AsyncReq = UHCI_ASYNC_INT_FROM_LINK (Link);
1033 Link = Link->ForwardLink;
1034
1035 Finished = UhciCheckTdStatus (Uhc, AsyncReq->FirstTd, AsyncReq->IsLow, &QhResult);
1036
1037 if (!Finished) {
1038 continue;
1039 }
1040
1041 //
1042 // Copy the data to temporary buffer if there are some
1043 // data transferred. We may have zero-length packet
1044 //
1045 Data = NULL;
1046
1047 if (QhResult.Complete != 0) {
1048 Data = AllocatePool (QhResult.Complete);
1049
1050 if (Data == NULL) {
1051 return ;
1052 }
1053
1054 CopyMem (Data, AsyncReq->FirstTd->Data, QhResult.Complete);
1055 }
1056
1057 UhciUpdateAsyncReq (Uhc, AsyncReq, QhResult.Result, QhResult.NextToggle);
1058
1059 //
1060 // Now, either transfer is SUCCESS or met errors since
1061 // we have skipped to next transfer earlier if current
1062 // transfer is still active.
1063 //
1064 if (QhResult.Result == EFI_USB_NOERROR) {
1065 AsyncReq->Callback (Data, QhResult.Complete, AsyncReq->Context, QhResult.Result);
1066 } else {
1067 //
1068 // Leave error recovery to its related device driver.
1069 // A common case of the error recovery is to re-submit
1070 // the interrupt transfer. When an interrupt transfer
1071 // is re-submitted, its position in the linked list is
1072 // changed. It is inserted to the head of the linked
1073 // list, while this function scans the whole list from
1074 // head to tail. Thus, the re-submitted interrupt transfer's
1075 // callback function will not be called again in this round.
1076 //
1077 AsyncReq->Callback (NULL, 0, AsyncReq->Context, QhResult.Result);
1078 }
1079
1080 if (Data != NULL) {
1081 gBS->FreePool (Data);
1082 }
1083 } while (Link != &(Uhc->AsyncIntList));
1084 }