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