]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - 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
1/** @file\r
2\r
3 EHCI transfer scheduling routines.\r
4\r
5Copyright (c) 2007 - 2010, 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
14**/\r
15\r
16#include "Ehci.h"\r
17\r
18\r
19/**\r
20 Create helper QTD/QH for the EHCI device.\r
21\r
22 @param Ehc The EHCI device.\r
23\r
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
26\r
27**/\r
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 EFI_PHYSICAL_ADDRESS PciAddr;\r
38\r
39 //\r
40 // Create an inactive Qtd to terminate the short packet read.\r
41 //\r
42 Qtd = EhcCreateQtd (Ehc, NULL, NULL, 0, QTD_PID_INPUT, 0, 64);\r
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
72 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
73 QhHw = &Qh->QhHw;\r
74 QhHw->HorizonLink = QH_LINK (PciAddr + OFFSET_OF(EHC_QH, QhHw), EHC_TYPE_QH, FALSE);\r
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
98/**\r
99 Initialize the schedule data structure such as frame list.\r
100\r
101 @param Ehc The EHCI device to init schedule data.\r
102\r
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
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
119 EFI_STATUS Status;\r
120 EFI_PHYSICAL_ADDRESS PciAddr;\r
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
161 Ehc->PeriodFrame = Buf;\r
162 Ehc->PeriodFrameMap = Map;\r
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
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
181 EHC_HIGH_32BIT (PhyAddr)\r
182 );\r
183\r
184 if (Ehc->MemPool == NULL) {\r
185 goto ErrorExit;\r
186 }\r
187\r
188 Status = EhcCreateHelpQ (Ehc);\r
189\r
190 if (EFI_ERROR (Status)) {\r
191 goto ErrorExit;\r
192 }\r
193\r
194 //\r
195 // Initialize the frame list entries then set the registers\r
196 //\r
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
204\r
205 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
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
214 }\r
215\r
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
221 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (EHC_QH));\r
222 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (PciAddr));\r
223 return EFI_SUCCESS;\r
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
244}\r
245\r
246\r
247/**\r
248 Free the schedule data. It may be partially initialized.\r
249\r
250 @param Ehc The EHCI device.\r
251\r
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
283 if (Ehc->PeriodFrame != NULL) {\r
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
292 Ehc->PeriodFrame\r
293 );\r
294\r
295 Ehc->PeriodFrame = NULL;\r
296 }\r
297\r
298 if (Ehc->PeriodFrameHost != NULL) {\r
299 FreePool (Ehc->PeriodFrameHost);\r
300 Ehc->PeriodFrameHost = NULL;\r
301 }\r
302}\r
303\r
304\r
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
312 @param Ehc The EHCI device.\r
313 @param Qh The queue head to link.\r
314\r
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
323 EFI_PHYSICAL_ADDRESS PciAddr;\r
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
335 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head, sizeof (EHC_QH));\r
336 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
337 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
338 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
339}\r
340\r
341\r
342/**\r
343 Unlink a queue head from the asynchronous schedule list.\r
344 Need to synchronize with hardware.\r
345\r
346 @param Ehc The EHCI device.\r
347 @param Qh The queue head to unlink.\r
348\r
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
358 EFI_PHYSICAL_ADDRESS PciAddr;\r
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
372 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Head, sizeof (EHC_QH));\r
373 Head->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
374\r
375 //\r
376 // Set and wait the door bell to synchronize with the hardware\r
377 //\r
378 Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
379\r
380 if (EFI_ERROR (Status)) {\r
381 DEBUG ((EFI_D_ERROR, "EhcUnlinkQhFromAsync: Failed to synchronize with doorbell\n"));\r
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
391 @param Ehc The EHCI device.\r
392 @param Qh The queue head to link.\r
393\r
394**/\r
395VOID\r
396EhcLinkQhToPeriod (\r
397 IN USB2_HC_DEV *Ehc,\r
398 IN EHC_QH *Qh\r
399 )\r
400{\r
401 UINTN Index;\r
402 EHC_QH *Prev;\r
403 EHC_QH *Next;\r
404 EFI_PHYSICAL_ADDRESS PciAddr;\r
405\r
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
411 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
412 Next = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
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
460 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
461 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
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
472 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Next, sizeof (EHC_QH));\r
473 Qh->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
474 }\r
475\r
476 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Qh, sizeof (EHC_QH));\r
477\r
478 if (Prev == NULL) {\r
479 ((UINT32*)Ehc->PeriodFrame)[Index] = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
480 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh;\r
481 } else {\r
482 Prev->NextQh = Qh;\r
483 Prev->QhHw.HorizonLink = QH_LINK (PciAddr, EHC_TYPE_QH, FALSE);\r
484 }\r
485 }\r
486}\r
487\r
488\r
489/**\r
490 Unlink an interrupt queue head from the periodic\r
491 schedule frame list.\r
492\r
493 @param Ehc The EHCI device.\r
494 @param Qh The queue head to unlink.\r
495\r
496**/\r
497VOID\r
498EhcUnlinkQhFromPeriod (\r
499 IN USB2_HC_DEV *Ehc,\r
500 IN EHC_QH *Qh\r
501 )\r
502{\r
503 UINTN Index;\r
504 EHC_QH *Prev;\r
505 EHC_QH *This;\r
506\r
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
512 ASSERT (!EHC_LINK_TERMINATED (((UINT32*)Ehc->PeriodFrame)[Index]));\r
513 This = (EHC_QH*)((UINTN*)Ehc->PeriodFrameHost)[Index];\r
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
537 ((UINT32*)Ehc->PeriodFrame)[Index] = Qh->QhHw.HorizonLink;\r
538 ((UINTN*)Ehc->PeriodFrameHost)[Index] = (UINTN)Qh->NextQh;\r
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
547/**\r
548 Check the URB's execution result and update the URB's\r
549 result accordingly.\r
550\r
551 @param Ehc The EHCI device.\r
552 @param Urb The URB to check result.\r
553\r
554 @return Whether the result of URB transfer is finialized.\r
555\r
556**/\r
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
568 EFI_PHYSICAL_ADDRESS PciAddr;\r
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
630 EhcDumpQh (Urb->Qh, "Short packet read", FALSE);\r
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
638 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (EHC_QTD));\r
639 if (QtdHw->AltNext == QTD_LINK (PciAddr, FALSE)) {\r
640 DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, break\n"));\r
641\r
642 Finished = TRUE;\r
643 goto ON_EXIT;\r
644 }\r
645\r
646 DEBUG ((EFI_D_INFO, "EhcCheckUrbResult: Short packet read, continue\n"));\r
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
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
673\r
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
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
692 Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;\r
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
702 gBS->Stall (EHC_SYNC_POLL_INTERVAL);\r
703 }\r
704\r
705 if (!Finished) {\r
706 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer not finished in %dms\n", (UINT32)TimeOut));\r
707 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
708\r
709 Status = EFI_TIMEOUT;\r
710\r
711 } else if (Urb->Result != EFI_USB_NOERROR) {\r
712 DEBUG ((EFI_D_ERROR, "EhcExecTransfer: transfer failed with %x\n", Urb->Result));\r
713 EhcDumpQh (Urb->Qh, NULL, FALSE);\r
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
724 the device and endpoint.\r
725\r
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
730\r
731 @retval EFI_SUCCESS An asynchronous transfer is removed.\r
732 @retval EFI_NOT_FOUND No transfer for the device is found.\r
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
748 Direction = (((EpNum & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
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
777 Remove all the asynchronous interrutp transfers.\r
778\r
779 @param Ehc The EHCI device.\r
780\r
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
802\r
803/**\r
804 Flush data from PCI controller specific address to mapped system\r
805 memory address.\r
806\r
807 @param Ehc The EHCI device.\r
808 @param Urb The URB to unmap.\r
809\r
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
812\r
813**/\r
814EFI_STATUS\r
815EhcFlushAsyncIntMap (\r
816 IN USB2_HC_DEV *Ehc,\r
817 IN URB *Urb\r
818 )\r
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
835\r
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
856\r
857/**\r
858 Update the queue head for next round of asynchronous transfer.\r
859\r
860 @param Ehc The EHCI device.\r
861 @param Urb The URB to update.\r
862\r
863**/\r
864VOID\r
865EhcUpdateAsyncRequest (\r
866 IN USB2_HC_DEV *Ehc,\r
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
876 EFI_PHYSICAL_ADDRESS PciAddr;\r
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
920 QhHw->Ioc = 0;\r
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
928 PciAddr = UsbHcGetPciAddressForHostMem (Ehc->MemPool, FirstQtd, sizeof (EHC_QTD));\r
929 QhHw->NextQtd = QTD_LINK (PciAddr, FALSE);\r
930 }\r
931\r
932 return ;\r
933}\r
934\r
935\r
936/**\r
937 Interrupt transfer periodic check handler.\r
938\r
939 @param Event Interrupt event.\r
940 @param Context Pointer to USB2_HC_DEV.\r
941\r
942**/\r
943VOID\r
944EFIAPI\r
945EhcMonitorAsyncRequests (\r
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
957 EFI_STATUS Status;\r
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
975 //\r
976 // Flush any PCI posted write transactions from a PCI host\r
977 // bridge to system memory.\r
978 //\r
979 Status = EhcFlushAsyncIntMap (Ehc, Urb);\r
980 if (EFI_ERROR (Status)) {\r
981 DEBUG ((EFI_D_ERROR, "EhcMonitorAsyncRequests: Fail to Flush AsyncInt Mapped Memeory\n"));\r
982 }\r
983\r
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
997 EhcUpdateAsyncRequest (Ehc, Urb);\r
998 continue;\r
999 }\r
1000\r
1001 CopyMem (ProcBuf, Urb->Data, Urb->Completed);\r
1002 }\r
1003\r
1004 EhcUpdateAsyncRequest (Ehc, Urb);\r
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
1027 FreePool (ProcBuf);\r
1028 }\r
1029 }\r
1030\r
1031 gBS->RestoreTPL (OldTpl);\r
1032}\r