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