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