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