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