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