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