]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/EhciDxe/EhciSched.c
Removing "Ex" in PI PCD_PPI/PROTOCOL's interface according to PI 1.2 latest update.
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciDxe / EhciSched.c
CommitLineData
913cb9dc 1/** @file\r
2\r
78c2ffb5 3 EHCI transfer scheduling routines.\r
4\r
1ccdbf2a 5Copyright (c) 2007 - 2010, 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 "Ehci.h"\r
17\r
18\r
19/**\r
78c2ffb5 20 Create helper QTD/QH for the EHCI device.\r
913cb9dc 21\r
78c2ffb5 22 @param Ehc The EHCI device.\r
913cb9dc 23\r
78c2ffb5 24 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.\r
25 @retval EFI_SUCCESS Helper QH/QTD are created.\r
913cb9dc 26\r
27**/\r
913cb9dc 28EFI_STATUS\r
29EhcCreateHelpQ (\r
30 IN USB2_HC_DEV *Ehc\r
31 )\r
32{\r
33 USB_ENDPOINT Ep;\r
34 EHC_QH *Qh;\r
35 QH_HW *QhHw;\r
36 EHC_QTD *Qtd;\r
739802e4 37 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 38\r
39 //\r
40 // Create an inactive Qtd to terminate the short packet read.\r
41 //\r
739802e4 42 Qtd = EhcCreateQtd (Ehc, NULL, NULL, 0, QTD_PID_INPUT, 0, 64);\r
913cb9dc 43\r
44 if (Qtd == NULL) {\r
45 return EFI_OUT_OF_RESOURCES;\r
46 }\r
47\r
48 Qtd->QtdHw.Status = QTD_STAT_HALTED;\r
49 Ehc->ShortReadStop = Qtd;\r
50\r
51 //\r
52 // Create a QH to act as the EHC reclamation header.\r
53 // Set the header to loopback to itself.\r
54 //\r
55 Ep.DevAddr = 0;\r
56 Ep.EpAddr = 1;\r
57 Ep.Direction = EfiUsbDataIn;\r
58 Ep.DevSpeed = EFI_USB_SPEED_HIGH;\r
59 Ep.MaxPacket = 64;\r
60 Ep.HubAddr = 0;\r
61 Ep.HubPort = 0;\r
62 Ep.Toggle = 0;\r
63 Ep.Type = EHC_BULK_TRANSFER;\r
64 Ep.PollRate = 1;\r
65\r
66 Qh = EhcCreateQh (Ehc, &Ep);\r
67\r
68 if (Qh == NULL) {\r
69 return EFI_OUT_OF_RESOURCES;\r
70 }\r
71\r
739802e4 72 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
913cb9dc 73 QhHw = &Qh->QhHw;\r
739802e4 74 QhHw->HorizonLink = QH_LINK (PciAddr + OFFSET_OF(EHC_QH, QhHw), EHC_TYPE_QH, FALSE);\r
913cb9dc 75 QhHw->Status = QTD_STAT_HALTED;\r
76 QhHw->ReclaimHead = 1;\r
77 Ehc->ReclaimHead = Qh;\r
78\r
79 //\r
80 // Create a dummy QH to act as the terminator for periodical schedule\r
81 //\r
82 Ep.EpAddr = 2;\r
83 Ep.Type = EHC_INT_TRANSFER_SYNC;\r
84\r
85 Qh = EhcCreateQh (Ehc, &Ep);\r
86\r
87 if (Qh == NULL) {\r
88 return EFI_OUT_OF_RESOURCES;\r
89 }\r
90\r
91 Qh->QhHw.Status = QTD_STAT_HALTED;\r
92 Ehc->PeriodOne = Qh;\r
93\r
94 return EFI_SUCCESS;\r
95}\r
96\r
97\r
913cb9dc 98/**\r
78c2ffb5 99 Initialize the schedule data structure such as frame list.\r
913cb9dc 100\r
78c2ffb5 101 @param Ehc The EHCI device to init schedule data.\r
913cb9dc 102\r
78c2ffb5 103 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.\r
104 @retval EFI_SUCCESS The schedule data is initialized.\r
913cb9dc 105\r
106**/\r
107EFI_STATUS\r
108EhcInitSched (\r
109 IN USB2_HC_DEV *Ehc\r
110 )\r
111{\r
112 EFI_PCI_IO_PROTOCOL *PciIo;\r
113 VOID *Buf;\r
114 EFI_PHYSICAL_ADDRESS PhyAddr;\r
115 VOID *Map;\r
116 UINTN Pages;\r
117 UINTN Bytes;\r
118 UINTN Index;\r
913cb9dc 119 EFI_STATUS Status;\r
739802e4 120 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 121\r
122 //\r
123 // First initialize the periodical schedule data:\r
124 // 1. Allocate and map the memory for the frame list\r
125 // 2. Create the help QTD/QH\r
126 // 3. Initialize the frame entries\r
127 // 4. Set the frame list register\r
128 //\r
129 PciIo = Ehc->PciIo;\r
130\r
131 Bytes = 4096;\r
132 Pages = EFI_SIZE_TO_PAGES (Bytes);\r
133\r
134 Status = PciIo->AllocateBuffer (\r
135 PciIo,\r
136 AllocateAnyPages,\r
137 EfiBootServicesData,\r
138 Pages,\r
139 &Buf,\r
140 0\r
141 );\r
142\r
143 if (EFI_ERROR (Status)) {\r
144 return EFI_OUT_OF_RESOURCES;\r
145 }\r
146\r
147 Status = PciIo->Map (\r
148 PciIo,\r
149 EfiPciIoOperationBusMasterCommonBuffer,\r
150 Buf,\r
151 &Bytes,\r
152 &PhyAddr,\r
153 &Map\r
154 );\r
155\r
156 if (EFI_ERROR (Status) || (Bytes != 4096)) {\r
157 PciIo->FreeBuffer (PciIo, Pages, Buf);\r
158 return EFI_OUT_OF_RESOURCES;\r
159 }\r
160\r
592b87a4 161 Ehc->PeriodFrame = Buf;\r
913cb9dc 162 Ehc->PeriodFrameMap = Map;\r
592b87a4 163\r
164 //\r
165 // Program the FRAMELISTBASE register with the low 32 bit addr\r
166 //\r
167 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (PhyAddr));\r
168 //\r
169 // Program the CTRLDSSEGMENT register with the high 32 bit addr\r
170 //\r
171 EhcWriteOpReg (Ehc, EHC_CTRLDSSEG_OFFSET, EHC_HIGH_32BIT (PhyAddr));\r
913cb9dc 172\r
173 //\r
174 // Init memory pool management then create the helper\r
175 // QTD/QH. If failed, previously allocated resources\r
176 // will be freed by EhcFreeSched\r
177 //\r
178 Ehc->MemPool = UsbHcInitMemPool (\r
179 PciIo,\r
180 EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),\r
592b87a4 181 EHC_HIGH_32BIT (PhyAddr)\r
913cb9dc 182 );\r
183\r
184 if (Ehc->MemPool == NULL) {\r
592b87a4 185 goto ErrorExit;\r
913cb9dc 186 }\r
187\r
188 Status = EhcCreateHelpQ (Ehc);\r
189\r
190 if (EFI_ERROR (Status)) {\r
592b87a4 191 goto ErrorExit;\r
913cb9dc 192 }\r
193\r
194 //\r
195 // Initialize the frame list entries then set the registers\r
196 //\r
592b87a4 197 Ehc->PeriodFrameHost = AllocateZeroPool (EHC_FRAME_LEN * sizeof (UINTN));\r
198 if (Ehc->PeriodFrameHost == NULL) {\r
199 Status = EFI_OUT_OF_RESOURCES;\r
200 goto ErrorExit;\r
201 }\r
202\r
203 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
913cb9dc 204\r
205 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
592b87a4 206 //\r
207 // Store the pci bus address of the QH in period frame list which will be accessed by pci bus master.\r
208 //\r
209 ((UINT32 *)(Ehc->PeriodFrame))[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
210 //\r
211 // Store the host address of the QH in period frame list which will be accessed by host.\r
212 //\r
213 ((UINTN *)(Ehc->PeriodFrameHost))[Index] = (UINTN)Ehc->PeriodOne;\r
913cb9dc 214 }\r
215\r
913cb9dc 216 //\r
217 // Second initialize the asynchronous schedule:\r
218 // Only need to set the AsynListAddr register to\r
219 // the reclamation header\r
220 //\r
739802e4 221 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
222 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));\r
913cb9dc 223 return EFI_SUCCESS;\r
592b87a4 224\r
225ErrorExit:\r
226 PciIo->FreeBuffer (PciIo, Pages, Buf);\r
227 PciIo->Unmap (PciIo, Map);\r
228\r
229 if (Ehc->PeriodOne != NULL) {\r
230 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
231 Ehc->PeriodOne = NULL;\r
232 }\r
233\r
234 if (Ehc->ReclaimHead != NULL) {\r
235 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
236 Ehc->ReclaimHead = NULL;\r
237 }\r
238\r
239 if (Ehc->ShortReadStop != NULL) {\r
240 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
241 Ehc->ShortReadStop = NULL;\r
242 }\r
243 return Status;\r
913cb9dc 244}\r
245\r
246\r
913cb9dc 247/**\r
248 Free the schedule data. It may be partially initialized.\r
249\r
78c2ffb5 250 @param Ehc The EHCI device.\r
913cb9dc 251\r
913cb9dc 252**/\r
253VOID\r
254EhcFreeSched (\r
255 IN USB2_HC_DEV *Ehc\r
256 )\r
257{\r
258 EFI_PCI_IO_PROTOCOL *PciIo;\r
259\r
260 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);\r
261 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);\r
262\r
263 if (Ehc->PeriodOne != NULL) {\r
264 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (EHC_QH));\r
265 Ehc->PeriodOne = NULL;\r
266 }\r
267\r
268 if (Ehc->ReclaimHead != NULL) {\r
269 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
270 Ehc->ReclaimHead = NULL;\r
271 }\r
272\r
273 if (Ehc->ShortReadStop != NULL) {\r
274 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
275 Ehc->ShortReadStop = NULL;\r
276 }\r
277\r
278 if (Ehc->MemPool != NULL) {\r
279 UsbHcFreeMemPool (Ehc->MemPool);\r
280 Ehc->MemPool = NULL;\r
281 }\r
282\r
592b87a4 283 if (Ehc->PeriodFrame != NULL) {\r
913cb9dc 284 PciIo = Ehc->PciIo;\r
285 ASSERT (PciIo != NULL);\r
286\r
287 PciIo->Unmap (PciIo, Ehc->PeriodFrameMap);\r
288\r
289 PciIo->FreeBuffer (\r
290 PciIo,\r
291 EFI_SIZE_TO_PAGES (EFI_PAGE_SIZE),\r
592b87a4 292 Ehc->PeriodFrame\r
913cb9dc 293 );\r
294\r
592b87a4 295 Ehc->PeriodFrame = NULL;\r
296 }\r
297\r
298 if (Ehc->PeriodFrameHost != NULL) {\r
299 FreePool (Ehc->PeriodFrameHost);\r
739802e4 300 Ehc->PeriodFrameHost = NULL;\r
913cb9dc 301 }\r
302}\r
303\r
304\r
913cb9dc 305/**\r
306 Link the queue head to the asynchronous schedule list.\r
307 UEFI only supports one CTRL/BULK transfer at a time\r
308 due to its interfaces. This simplifies the AsynList\r
309 management: A reclamation header is always linked to\r
310 the AsyncListAddr, the only active QH is appended to it.\r
311\r
78c2ffb5 312 @param Ehc The EHCI device.\r
313 @param Qh The queue head to link.\r
913cb9dc 314\r
913cb9dc 315**/\r
316VOID\r
317EhcLinkQhToAsync (\r
318 IN USB2_HC_DEV *Ehc,\r
319 IN EHC_QH *Qh\r
320 )\r
321{\r
322 EHC_QH *Head;\r
739802e4 323 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 324\r
325 //\r
326 // Append the queue head after the reclaim header, then\r
327 // fix the hardware visiable parts (EHCI R1.0 page 72).\r
328 // ReclaimHead is always linked to the EHCI's AsynListAddr.\r
329 //\r
330 Head = Ehc->ReclaimHead;\r
331\r
332 Qh->NextQh = Head->NextQh;\r
333 Head->NextQh = Qh;\r
334\r
739802e4 335 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head, sizeof (EHC_QH));\r
592b87a4 336 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
739802e4 337 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
338 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 339}\r
340\r
341\r
342/**\r
343 Unlink a queue head from the asynchronous schedule list.\r
78c2ffb5 344 Need to synchronize with hardware.\r
913cb9dc 345\r
78c2ffb5 346 @param Ehc The EHCI device.\r
347 @param Qh The queue head to unlink.\r
913cb9dc 348\r
913cb9dc 349**/\r
350VOID\r
351EhcUnlinkQhFromAsync (\r
352 IN USB2_HC_DEV *Ehc,\r
353 IN EHC_QH *Qh\r
354 )\r
355{\r
356 EHC_QH *Head;\r
357 EFI_STATUS Status;\r
739802e4 358 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 359\r
360 ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
361\r
362 //\r
363 // Remove the QH from reclamation head, then update the hardware\r
364 // visiable part: Only need to loopback the ReclaimHead. The Qh\r
365 // is pointing to ReclaimHead (which is staill in the list).\r
366 //\r
367 Head = Ehc->ReclaimHead;\r
368\r
369 Head->NextQh = Qh->NextQh;\r
370 Qh->NextQh = NULL;\r
371\r
739802e4 372 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head, sizeof (EHC_QH));\r
373 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 374\r
375 //\r
376 // Set and wait the door bell to synchronize with the hardware\r
377 //\r
41e8ff27 378 Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
913cb9dc 379\r
380 if (EFI_ERROR (Status)) {\r
1c619535 381 DEBUG ((EFI_D_ERROR, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));\r
913cb9dc 382 }\r
383}\r
384\r
385\r
386/**\r
387 Link a queue head for interrupt transfer to the periodic\r
388 schedule frame list. This code is very much the same as\r
389 that in UHCI.\r
390\r
78c2ffb5 391 @param Ehc The EHCI device.\r
392 @param Qh The queue head to link.\r
913cb9dc 393\r
913cb9dc 394**/\r
395VOID\r
396EhcLinkQhToPeriod (\r
397 IN USB2_HC_DEV *Ehc,\r
398 IN EHC_QH *Qh\r
399 )\r
400{\r
913cb9dc 401 UINTN Index;\r
402 EHC_QH *Prev;\r
403 EHC_QH *Next;\r
739802e4 404 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 405\r
913cb9dc 406 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
407 //\r
408 // First QH can't be NULL because we always keep PeriodOne\r
409 // heads on the frame list\r
410 //\r
592b87a4 411 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
412 Next = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
913cb9dc 413 Prev = NULL;\r
414\r
415 //\r
416 // Now, insert the queue head (Qh) into this frame:\r
417 // 1. Find a queue head with the same poll interval, just insert\r
418 // Qh after this queue head, then we are done.\r
419 //\r
420 // 2. Find the position to insert the queue head into:\r
421 // Previous head's interval is bigger than Qh's\r
422 // Next head's interval is less than Qh's\r
423 // Then, insert the Qh between then\r
424 //\r
425 while (Next->Interval > Qh->Interval) {\r
426 Prev = Next;\r
427 Next = Next->NextQh;\r
428 }\r
429\r
430 ASSERT (Next != NULL);\r
431\r
432 //\r
433 // The entry may have been linked into the frame by early insertation.\r
434 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh\r
435 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.\r
436 // It isn't necessary to compare all the QH with the same interval to\r
437 // Qh. This is because if there is other QH with the same interval, Qh\r
438 // should has been inserted after that at Frames[0] and at Frames[0] it is\r
439 // impossible for (Next == Qh)\r
440 //\r
441 if (Next == Qh) {\r
442 continue;\r
443 }\r
444\r
445 if (Next->Interval == Qh->Interval) {\r
446 //\r
447 // If there is a QH with the same interval, it locates at\r
448 // Frames[0], and we can simply insert it after this QH. We\r
449 // are all done.\r
450 //\r
451 ASSERT ((Index == 0) && (Qh->NextQh == NULL));\r
452\r
453 Prev = Next;\r
454 Next = Next->NextQh;\r
455\r
456 Qh->NextQh = Next;\r
457 Prev->NextQh = Qh;\r
458\r
459 Qh->QhHw.HorizonLink = Prev->QhHw.HorizonLink;\r
739802e4 460 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
461 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 462 break;\r
463 }\r
464\r
465 //\r
466 // OK, find the right position, insert it in. If Qh's next\r
467 // link has already been set, it is in position. This is\r
468 // guarranted by 2^n polling interval.\r
469 //\r
470 if (Qh->NextQh == NULL) {\r
471 Qh->NextQh = Next;\r
739802e4 472 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Next, sizeof (EHC_QH));\r
473 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 474 }\r
475\r
739802e4 476 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
477\r
913cb9dc 478 if (Prev == NULL) {\r
592b87a4 479 ((UINT32*)Ehc->PeriodFrame)[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
480 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh;\r
913cb9dc 481 } else {\r
482 Prev->NextQh = Qh;\r
739802e4 483 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
913cb9dc 484 }\r
485 }\r
486}\r
487\r
488\r
489/**\r
490 Unlink an interrupt queue head from the periodic\r
78c2ffb5 491 schedule frame list.\r
913cb9dc 492\r
78c2ffb5 493 @param Ehc The EHCI device.\r
494 @param Qh The queue head to unlink.\r
913cb9dc 495\r
913cb9dc 496**/\r
497VOID\r
498EhcUnlinkQhFromPeriod (\r
499 IN USB2_HC_DEV *Ehc,\r
500 IN EHC_QH *Qh\r
501 )\r
502{\r
913cb9dc 503 UINTN Index;\r
504 EHC_QH *Prev;\r
505 EHC_QH *This;\r
506\r
913cb9dc 507 for (Index = 0; Index < EHC_FRAME_LEN; Index += Qh->Interval) {\r
508 //\r
509 // Frame link can't be NULL because we always keep PeroidOne\r
510 // on the frame list\r
511 //\r
592b87a4 512 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
513 This = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
913cb9dc 514 Prev = NULL;\r
515\r
516 //\r
517 // Walk through the frame's QH list to find the\r
518 // queue head to remove\r
519 //\r
520 while ((This != NULL) && (This != Qh)) {\r
521 Prev = This;\r
522 This = This->NextQh;\r
523 }\r
524\r
525 //\r
526 // Qh may have already been unlinked from this frame\r
527 // by early action. See the comments in EhcLinkQhToPeriod.\r
528 //\r
529 if (This == NULL) {\r
530 continue;\r
531 }\r
532\r
533 if (Prev == NULL) {\r
534 //\r
535 // Qh is the first entry in the frame\r
536 //\r
592b87a4 537 ((UINT32*)Ehc->PeriodFrame)[Index] = Qh->QhHw.HorizonLink;\r
538 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh->NextQh;\r
913cb9dc 539 } else {\r
540 Prev->NextQh = Qh->NextQh;\r
541 Prev->QhHw.HorizonLink = Qh->QhHw.HorizonLink;\r
542 }\r
543 }\r
544}\r
545\r
546\r
913cb9dc 547/**\r
548 Check the URB's execution result and update the URB's\r
549 result accordingly.\r
550\r
78c2ffb5 551 @param Ehc The EHCI device.\r
552 @param Urb The URB to check result.\r
913cb9dc 553\r
554 @return Whether the result of URB transfer is finialized.\r
555\r
556**/\r
913cb9dc 557BOOLEAN\r
558EhcCheckUrbResult (\r
559 IN USB2_HC_DEV *Ehc,\r
560 IN URB *Urb\r
561 )\r
562{\r
563 LIST_ENTRY *Entry;\r
564 EHC_QTD *Qtd;\r
565 QTD_HW *QtdHw;\r
566 UINT8 State;\r
567 BOOLEAN Finished;\r
739802e4 568 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 569\r
570 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));\r
571\r
572 Finished = TRUE;\r
573 Urb->Completed = 0;\r
574\r
575 Urb->Result = EFI_USB_NOERROR;\r
576\r
577 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {\r
578 Urb->Result |= EFI_USB_ERR_SYSTEM;\r
579 goto ON_EXIT;\r
580 }\r
581\r
582 EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
583 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);\r
584 QtdHw = &Qtd->QtdHw;\r
585 State = (UINT8) QtdHw->Status;\r
586\r
587 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {\r
588 //\r
589 // EHCI will halt the queue head when met some error.\r
590 // If it is halted, the result of URB is finialized.\r
591 //\r
592 if ((State & QTD_STAT_ERR_MASK) == 0) {\r
593 Urb->Result |= EFI_USB_ERR_STALL;\r
594 }\r
595\r
596 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {\r
597 Urb->Result |= EFI_USB_ERR_BABBLE;\r
598 }\r
599\r
600 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {\r
601 Urb->Result |= EFI_USB_ERR_BUFFER;\r
602 }\r
603\r
604 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {\r
605 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
606 }\r
607\r
608 Finished = TRUE;\r
609 goto ON_EXIT;\r
610\r
611 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {\r
612 //\r
613 // The QTD is still active, no need to check furthur.\r
614 //\r
615 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;\r
616\r
617 Finished = FALSE;\r
618 goto ON_EXIT;\r
619\r
620 } else {\r
621 //\r
622 // This QTD is finished OK or met short packet read. Update the\r
623 // transfer length if it isn't a setup.\r
624 //\r
625 if (QtdHw->Pid != QTD_PID_SETUP) {\r
626 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;\r
627 }\r
628\r
629 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {\r
1c619535 630 EhcDumpQh (Urb->Qh, "Short packet read", FALSE);\r
913cb9dc 631\r
632 //\r
633 // Short packet read condition. If it isn't a setup transfer,\r
634 // no need to check furthur: the queue head will halt at the\r
635 // ShortReadStop. If it is a setup transfer, need to check the\r
636 // Status Stage of the setup transfer to get the finial result\r
637 //\r
739802e4 638 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
639 if (QtdHw->AltNext == QTD_LINK (PciAddr, FALSE)) {\r
1c619535 640 DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, break\n"));\r
913cb9dc 641\r
642 Finished = TRUE;\r
643 goto ON_EXIT;\r
644 }\r
645\r
1c619535 646 DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, continue\n"));\r
913cb9dc 647 }\r
648 }\r
649 }\r
650\r
651ON_EXIT:\r
652 //\r
653 // Return the data toggle set by EHCI hardware, bulk and interrupt\r
654 // transfer will use this to initialize the next transaction. For\r
655 // Control transfer, it always start a new data toggle sequence for\r
656 // new transfer.\r
657 //\r
658 // NOTICE: don't move DT update before the loop, otherwise there is\r
659 // a race condition that DT is wrong.\r
660 //\r
661 Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;\r
662\r
663 return Finished;\r
664}\r
665\r
666\r
667/**\r
668 Execute the transfer by polling the URB. This is a synchronous operation.\r
669\r
78c2ffb5 670 @param Ehc The EHCI device.\r
671 @param Urb The URB to execute.\r
672 @param TimeOut The time to wait before abort, in millisecond.\r
913cb9dc 673\r
78c2ffb5 674 @return EFI_DEVICE_ERROR The transfer failed due to transfer error.\r
675 @return EFI_TIMEOUT The transfer failed due to time out.\r
676 @return EFI_SUCCESS The transfer finished OK.\r
913cb9dc 677\r
678**/\r
679EFI_STATUS\r
680EhcExecTransfer (\r
681 IN USB2_HC_DEV *Ehc,\r
682 IN URB *Urb,\r
683 IN UINTN TimeOut\r
684 )\r
685{\r
686 EFI_STATUS Status;\r
687 UINTN Index;\r
688 UINTN Loop;\r
689 BOOLEAN Finished;\r
690\r
691 Status = EFI_SUCCESS;\r
41e8ff27 692 Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;\r
913cb9dc 693 Finished = FALSE;\r
694\r
695 for (Index = 0; Index < Loop; Index++) {\r
696 Finished = EhcCheckUrbResult (Ehc, Urb);\r
697\r
698 if (Finished) {\r
699 break;\r
700 }\r
701\r
41e8ff27 702 gBS->Stall (EHC_SYNC_POLL_INTERVAL);\r
913cb9dc 703 }\r
704\r
705 if (!Finished) {\r
7df7393f 706 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32)TimeOut));\r
1c619535 707 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
913cb9dc 708\r
709 Status = EFI_TIMEOUT;\r
710\r
711 } else if (Urb->Result != EFI_USB_NOERROR) {\r
1c619535 712 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer failed with %x\n", Urb->Result));\r
713 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
913cb9dc 714\r
715 Status = EFI_DEVICE_ERROR;\r
716 }\r
717\r
718 return Status;\r
719}\r
720\r
721\r
722/**\r
723 Delete a single asynchronous interrupt transfer for\r
78c2ffb5 724 the device and endpoint.\r
913cb9dc 725\r
78c2ffb5 726 @param Ehc The EHCI device.\r
727 @param DevAddr The address of the target device.\r
728 @param EpNum The endpoint of the target.\r
729 @param DataToggle Return the next data toggle to use.\r
913cb9dc 730\r
78c2ffb5 731 @retval EFI_SUCCESS An asynchronous transfer is removed.\r
732 @retval EFI_NOT_FOUND No transfer for the device is found.\r
913cb9dc 733\r
734**/\r
735EFI_STATUS\r
736EhciDelAsyncIntTransfer (\r
737 IN USB2_HC_DEV *Ehc,\r
738 IN UINT8 DevAddr,\r
739 IN UINT8 EpNum,\r
740 OUT UINT8 *DataToggle\r
741 )\r
742{\r
743 LIST_ENTRY *Entry;\r
744 LIST_ENTRY *Next;\r
745 URB *Urb;\r
746 EFI_USB_DATA_DIRECTION Direction;\r
747\r
78c2ffb5 748 Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
913cb9dc 749 EpNum &= 0x0F;\r
750\r
751 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
752 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
753\r
754 if ((Urb->Ep.DevAddr == DevAddr) && (Urb->Ep.EpAddr == EpNum) &&\r
755 (Urb->Ep.Direction == Direction)) {\r
756 //\r
757 // Check the URB status to retrieve the next data toggle\r
758 // from the associated queue head.\r
759 //\r
760 EhcCheckUrbResult (Ehc, Urb);\r
761 *DataToggle = Urb->DataToggle;\r
762\r
763 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
764 RemoveEntryList (&Urb->UrbList);\r
765\r
766 gBS->FreePool (Urb->Data);\r
767 EhcFreeUrb (Ehc, Urb);\r
768 return EFI_SUCCESS;\r
769 }\r
770 }\r
771\r
772 return EFI_NOT_FOUND;\r
773}\r
774\r
775\r
776/**\r
78c2ffb5 777 Remove all the asynchronous interrutp transfers.\r
913cb9dc 778\r
78c2ffb5 779 @param Ehc The EHCI device.\r
913cb9dc 780\r
913cb9dc 781**/\r
782VOID\r
783EhciDelAllAsyncIntTransfers (\r
784 IN USB2_HC_DEV *Ehc\r
785 )\r
786{\r
787 LIST_ENTRY *Entry;\r
788 LIST_ENTRY *Next;\r
789 URB *Urb;\r
790\r
791 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
792 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
793\r
794 EhcUnlinkQhFromPeriod (Ehc, Urb->Qh);\r
795 RemoveEntryList (&Urb->UrbList);\r
796\r
797 gBS->FreePool (Urb->Data);\r
798 EhcFreeUrb (Ehc, Urb);\r
799 }\r
800}\r
801\r
50fa1b3a 802\r
78c2ffb5 803/**\r
1c619535 804 Flush data from PCI controller specific address to mapped system\r
50fa1b3a 805 memory address.\r
806\r
78c2ffb5 807 @param Ehc The EHCI device.\r
808 @param Urb The URB to unmap.\r
50fa1b3a 809\r
78c2ffb5 810 @retval EFI_SUCCESS Success to flush data to mapped system memory.\r
811 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.\r
1c619535 812\r
78c2ffb5 813**/\r
814EFI_STATUS\r
815EhcFlushAsyncIntMap (\r
816 IN USB2_HC_DEV *Ehc,\r
817 IN URB *Urb\r
818 )\r
50fa1b3a 819{\r
820 EFI_STATUS Status;\r
821 EFI_PHYSICAL_ADDRESS PhyAddr;\r
822 EFI_PCI_IO_PROTOCOL_OPERATION MapOp;\r
823 EFI_PCI_IO_PROTOCOL *PciIo;\r
824 UINTN Len;\r
825 VOID *Map;\r
826\r
827 PciIo = Ehc->PciIo;\r
828 Len = Urb->DataLen;\r
829\r
830 if (Urb->Ep.Direction == EfiUsbDataIn) {\r
831 MapOp = EfiPciIoOperationBusMasterWrite;\r
832 } else {\r
833 MapOp = EfiPciIoOperationBusMasterRead;\r
834 }\r
1c619535 835\r
50fa1b3a 836 Status = PciIo->Unmap (PciIo, Urb->DataMap);\r
837 if (EFI_ERROR (Status)) {\r
838 goto ON_ERROR;\r
839 }\r
840\r
841 Urb->DataMap = NULL;\r
842\r
843 Status = PciIo->Map (PciIo, MapOp, Urb->Data, &Len, &PhyAddr, &Map);\r
844 if (EFI_ERROR (Status) || (Len != Urb->DataLen)) {\r
845 goto ON_ERROR;\r
846 }\r
847\r
848 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);\r
849 Urb->DataMap = Map;\r
850 return EFI_SUCCESS;\r
851\r
852ON_ERROR:\r
853 return EFI_DEVICE_ERROR;\r
854}\r
855\r
913cb9dc 856\r
913cb9dc 857/**\r
78c2ffb5 858 Update the queue head for next round of asynchronous transfer.\r
913cb9dc 859\r
739802e4 860 @param Ehc The EHCI device.\r
78c2ffb5 861 @param Urb The URB to update.\r
913cb9dc 862\r
913cb9dc 863**/\r
913cb9dc 864VOID\r
865EhcUpdateAsyncRequest (\r
739802e4 866 IN USB2_HC_DEV *Ehc,\r
913cb9dc 867 IN URB *Urb\r
868 )\r
869{\r
870 LIST_ENTRY *Entry;\r
871 EHC_QTD *FirstQtd;\r
872 QH_HW *QhHw;\r
873 EHC_QTD *Qtd;\r
874 QTD_HW *QtdHw;\r
875 UINTN Index;\r
739802e4 876 EFI_PHYSICAL_ADDRESS PciAddr;\r
913cb9dc 877\r
878 Qtd = NULL;\r
879\r
880 if (Urb->Result == EFI_USB_NOERROR) {\r
881 FirstQtd = NULL;\r
882\r
883 EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
884 Qtd = EFI_LIST_CONTAINER (Entry, EHC_QTD, QtdList);\r
885\r
886 if (FirstQtd == NULL) {\r
887 FirstQtd = Qtd;\r
888 }\r
889\r
890 //\r
891 // Update the QTD for next round of transfer. Host control\r
892 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/\r
893 // Current Offset. These fields need to be updated. DT isn't\r
894 // used by interrupt transfer. It uses DT in queue head.\r
895 // Current Offset is in Page[0], only need to reset Page[0]\r
896 // to initial data buffer.\r
897 //\r
898 QtdHw = &Qtd->QtdHw;\r
899 QtdHw->Status = QTD_STAT_ACTIVE;\r
900 QtdHw->ErrCnt = QTD_MAX_ERR;\r
901 QtdHw->CurPage = 0;\r
902 QtdHw->TotalBytes = (UINT32) Qtd->DataLen;\r
903 QtdHw->Page[0] = EHC_LOW_32BIT (Qtd->Data);\r
904 }\r
905\r
906 //\r
907 // Update QH for next round of transfer. Host control only\r
908 // touch the fields in transfer overlay area. Only need to\r
909 // zero out the overlay area and set NextQtd to the first\r
910 // QTD. DateToggle bit is left untouched.\r
911 //\r
912 QhHw = &Urb->Qh->QhHw;\r
913 QhHw->CurQtd = QTD_LINK (0, TRUE);\r
914 QhHw->AltQtd = 0;\r
915\r
916 QhHw->Status = 0;\r
917 QhHw->Pid = 0;\r
918 QhHw->ErrCnt = 0;\r
919 QhHw->CurPage = 0;\r
1ccdbf2a 920 QhHw->Ioc = 0;\r
913cb9dc 921 QhHw->TotalBytes = 0;\r
922\r
923 for (Index = 0; Index < 5; Index++) {\r
924 QhHw->Page[Index] = 0;\r
925 QhHw->PageHigh[Index] = 0;\r
926 }\r
927\r
739802e4 928 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, FirstQtd, sizeof (EHC_QTD));\r
929 QhHw->NextQtd = QTD_LINK (PciAddr, FALSE);\r
913cb9dc 930 }\r
931\r
932 return ;\r
933}\r
934\r
935\r
936/**\r
78c2ffb5 937 Interrupt transfer periodic check handler.\r
913cb9dc 938\r
78c2ffb5 939 @param Event Interrupt event.\r
940 @param Context Pointer to USB2_HC_DEV.\r
913cb9dc 941\r
913cb9dc 942**/\r
943VOID\r
6d3ea23f 944EFIAPI\r
597f4ee2 945EhcMonitorAsyncRequests (\r
913cb9dc 946 IN EFI_EVENT Event,\r
947 IN VOID *Context\r
948 )\r
949{\r
950 USB2_HC_DEV *Ehc;\r
951 EFI_TPL OldTpl;\r
952 LIST_ENTRY *Entry;\r
953 LIST_ENTRY *Next;\r
954 BOOLEAN Finished;\r
955 UINT8 *ProcBuf;\r
956 URB *Urb;\r
50fa1b3a 957 EFI_STATUS Status;\r
913cb9dc 958\r
959 OldTpl = gBS->RaiseTPL (EHC_TPL);\r
960 Ehc = (USB2_HC_DEV *) Context;\r
961\r
962 EFI_LIST_FOR_EACH_SAFE (Entry, Next, &Ehc->AsyncIntTransfers) {\r
963 Urb = EFI_LIST_CONTAINER (Entry, URB, UrbList);\r
964\r
965 //\r
966 // Check the result of URB execution. If it is still\r
967 // active, check the next one.\r
968 //\r
969 Finished = EhcCheckUrbResult (Ehc, Urb);\r
970\r
971 if (!Finished) {\r
972 continue;\r
973 }\r
974\r
50fa1b3a 975 //\r
1c619535 976 // Flush any PCI posted write transactions from a PCI host\r
50fa1b3a 977 // bridge to system memory.\r
978 //\r
979 Status = EhcFlushAsyncIntMap (Ehc, Urb);\r
980 if (EFI_ERROR (Status)) {\r
597f4ee2 981 DEBUG ((EFI_D_ERROR, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));\r
50fa1b3a 982 }\r
1c619535 983\r
913cb9dc 984 //\r
985 // Allocate a buffer then copy the transferred data for user.\r
986 // If failed to allocate the buffer, update the URB for next\r
987 // round of transfer. Ignore the data of this round.\r
988 //\r
989 ProcBuf = NULL;\r
990\r
991 if (Urb->Result == EFI_USB_NOERROR) {\r
992 ASSERT (Urb->Completed <= Urb->DataLen);\r
993\r
994 ProcBuf = AllocatePool (Urb->Completed);\r
995\r
996 if (ProcBuf == NULL) {\r
739802e4 997 EhcUpdateAsyncRequest (Ehc, Urb);\r
913cb9dc 998 continue;\r
999 }\r
1000\r
1001 CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
1002 }\r
1003\r
739802e4 1004 EhcUpdateAsyncRequest (Ehc, Urb);\r
913cb9dc 1005\r
1006 //\r
1007 // Leave error recovery to its related device driver. A\r
1008 // common case of the error recovery is to re-submit the\r
1009 // interrupt transfer which is linked to the head of the\r
1010 // list. This function scans from head to tail. So the\r
1011 // re-submitted interrupt transfer's callback function\r
1012 // will not be called again in this round. Don't touch this\r
1013 // URB after the callback, it may have been removed by the\r
1014 // callback.\r
1015 //\r
1016 if (Urb->Callback != NULL) {\r
1017 //\r
1018 // Restore the old TPL, USB bus maybe connect device in\r
1019 // his callback. Some drivers may has a lower TPL restriction.\r
1020 //\r
1021 gBS->RestoreTPL (OldTpl);\r
1022 (Urb->Callback) (ProcBuf, Urb->Completed, Urb->Context, Urb->Result);\r
1023 OldTpl = gBS->RaiseTPL (EHC_TPL);\r
1024 }\r
1025\r
1026 if (ProcBuf != NULL) {\r
7351070b 1027 FreePool (ProcBuf);\r
913cb9dc 1028 }\r
1029 }\r
1030\r
1031 gBS->RestoreTPL (OldTpl);\r
1032}\r