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