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