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