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