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