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