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