913cb9dc |
1 | /** @file\r |
2 | \r |
3 | Copyright (c) 2007, Intel Corporation\r |
4 | All rights reserved. This program and the accompanying materials\r |
5 | are licensed and made available under the terms and conditions of the BSD License\r |
6 | which accompanies this distribution. The full text of the license may be found at\r |
7 | http://opensource.org/licenses/bsd-license.php\r |
8 | \r |
9 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r |
10 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r |
11 | \r |
12 | Module Name:\r |
13 | \r |
14 | EhciSched.c\r |
15 | \r |
16 | Abstract:\r |
17 | \r |
18 | EHCI transfer scheduling routines\r |
19 | \r |
20 | Revision 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 |
36 | STATIC\r |
37 | EFI_STATUS\r |
38 | EhcCreateHelpQ (\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 |
115 | EFI_STATUS\r |
116 | EhcInitSched (\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 |
225 | VOID\r |
226 | EhcFreeSched (\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 |
286 | VOID\r |
287 | EhcLinkQhToAsync (\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 |
319 | VOID\r |
320 | EhcUnlinkQhFromAsync (\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 |
41e8ff27 |
345 | Status = EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r |
913cb9dc |
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 |
364 | VOID\r |
365 | EhcLinkQhToPeriod (\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 |
465 | VOID\r |
466 | EhcUnlinkQhFromPeriod (\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 |
528 | STATIC\r |
529 | BOOLEAN\r |
530 | EhcCheckUrbResult (\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 |
621 | ON_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 |
649 | EFI_STATUS\r |
650 | EhcExecTransfer (\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 |
41e8ff27 |
662 | Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;\r |
913cb9dc |
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 |
41e8ff27 |
672 | gBS->Stall (EHC_SYNC_POLL_INTERVAL);\r |
913cb9dc |
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 |
705 | EFI_STATUS\r |
706 | EhciDelAsyncIntTransfer (\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 |
754 | VOID\r |
755 | EhciDelAllAsyncIntTransfers (\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 |
774 | STATIC\r |
775 | EFI_STATUS\r |
776 | EhcFlushAsyncIntMap (\r |
777 | IN USB2_HC_DEV *Ehc,\r |
778 | IN URB *Urb\r |
779 | )\r |
780 | /*++\r |
781 | \r |
782 | Routine Description:\r |
783 | \r |
784 | Flush data from PCI controller specific address to mapped system \r |
785 | memory address.\r |
786 | \r |
787 | Arguments:\r |
788 | \r |
789 | Ehc - The EHCI device\r |
790 | Urb - The URB to unmap\r |
791 | \r |
792 | Returns:\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 |
831 | ON_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 |
845 | STATIC\r |
846 | VOID\r |
847 | EhcUpdateAsyncRequest (\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 |
924 | VOID\r |
925 | EhcMoniteAsyncRequests (\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 |