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