]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciPei / EhciUrb.c
... / ...
CommitLineData
1/** @file\r
2PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
3which is used to enable recovery function from USB Drivers.\r
4\r
5Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
6\r
7SPDX-License-Identifier: BSD-2-Clause-Patent\r
8\r
9**/\r
10\r
11#include "EhcPeim.h"\r
12\r
13/**\r
14 Delete a single asynchronous interrupt transfer for\r
15 the device and endpoint.\r
16\r
17 @param Ehc The EHCI device.\r
18 @param Data Current data not associated with a QTD.\r
19 @param DataLen The length of the data.\r
20 @param PktId Packet ID to use in the QTD.\r
21 @param Toggle Data toggle to use in the QTD.\r
22 @param MaxPacket Maximu packet length of the endpoint.\r
23\r
24 @retval the pointer to the created QTD or NULL if failed to create one.\r
25\r
26**/\r
27PEI_EHC_QTD *\r
28EhcCreateQtd (\r
29 IN PEI_USB2_HC_DEV *Ehc,\r
30 IN UINT8 *Data,\r
31 IN UINTN DataLen,\r
32 IN UINT8 PktId,\r
33 IN UINT8 Toggle,\r
34 IN UINTN MaxPacket\r
35 )\r
36{\r
37 PEI_EHC_QTD *Qtd;\r
38 QTD_HW *QtdHw;\r
39 UINTN Index;\r
40 UINTN Len;\r
41 UINTN ThisBufLen;\r
42\r
43 ASSERT (Ehc != NULL);\r
44\r
45 Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_EHC_QTD));\r
46\r
47 if (Qtd == NULL) {\r
48 return NULL;\r
49 }\r
50\r
51 Qtd->Signature = EHC_QTD_SIG;\r
52 Qtd->Data = Data;\r
53 Qtd->DataLen = 0;\r
54\r
55 InitializeListHead (&Qtd->QtdList);\r
56\r
57 QtdHw = &Qtd->QtdHw;\r
58 QtdHw->NextQtd = QTD_LINK (NULL, TRUE);\r
59 QtdHw->AltNext = QTD_LINK (NULL, TRUE);\r
60 QtdHw->Status = QTD_STAT_ACTIVE;\r
61 QtdHw->Pid = PktId;\r
62 QtdHw->ErrCnt = QTD_MAX_ERR;\r
63 QtdHw->Ioc = 0;\r
64 QtdHw->TotalBytes = 0;\r
65 QtdHw->DataToggle = Toggle;\r
66\r
67 //\r
68 // Fill in the buffer points\r
69 //\r
70 if (Data != NULL) {\r
71 Len = 0;\r
72\r
73 for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) {\r
74 //\r
75 // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to\r
76 // compute the offset and clear Reserved fields. This is already\r
77 // done in the data point.\r
78 //\r
79 QtdHw->Page[Index] = EHC_LOW_32BIT (Data);\r
80 QtdHw->PageHigh[Index] = EHC_HIGH_32BIT (Data);\r
81\r
82 ThisBufLen = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK);\r
83\r
84 if (Len + ThisBufLen >= DataLen) {\r
85 Len = DataLen;\r
86 break;\r
87 }\r
88\r
89 Len += ThisBufLen;\r
90 Data += ThisBufLen;\r
91 }\r
92\r
93 //\r
94 // Need to fix the last pointer if the Qtd can't hold all the\r
95 // user's data to make sure that the length is in the unit of\r
96 // max packets. If it can hold all the data, there is no such\r
97 // need.\r
98 //\r
99 if (Len < DataLen) {\r
100 Len = Len - Len % MaxPacket;\r
101 }\r
102\r
103 QtdHw->TotalBytes = (UINT32) Len;\r
104 Qtd->DataLen = Len;\r
105 }\r
106\r
107 return Qtd;\r
108}\r
109\r
110/**\r
111 Initialize the queue head for interrupt transfer,\r
112 that is, initialize the following three fields:\r
113 1. SplitXState in the Status field.\r
114 2. Microframe S-mask.\r
115 3. Microframe C-mask.\r
116\r
117 @param Ep The queue head's related endpoint.\r
118 @param QhHw The queue head to initialize.\r
119\r
120**/\r
121VOID\r
122EhcInitIntQh (\r
123 IN USB_ENDPOINT *Ep,\r
124 IN QH_HW *QhHw\r
125 )\r
126{\r
127 //\r
128 // Because UEFI interface can't utilitize an endpoint with\r
129 // poll rate faster than 1ms, only need to set one bit in\r
130 // the queue head. simple. But it may be changed later. If\r
131 // sub-1ms interrupt is supported, need to update the S-Mask\r
132 // here\r
133 //\r
134 if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) {\r
135 QhHw->SMask = QH_MICROFRAME_0;\r
136 return ;\r
137 }\r
138\r
139 //\r
140 // For low/full speed device, the transfer must go through\r
141 // the split transaction. Need to update three fields\r
142 // 1. SplitXState in the status\r
143 // 2. Microframe S-Mask\r
144 // 3. Microframe C-Mask\r
145 // UEFI USB doesn't exercise admission control. It simplely\r
146 // schedule the high speed transactions in microframe 0, and\r
147 // full/low speed transactions at microframe 1. This also\r
148 // avoid the use of FSTN.\r
149 //\r
150 QhHw->SMask = QH_MICROFRAME_1;\r
151 QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5;\r
152}\r
153\r
154/**\r
155 Allocate and initialize a EHCI queue head.\r
156\r
157 @param Ehci The EHCI device.\r
158 @param Ep The endpoint to create queue head for.\r
159\r
160 @retval the pointer to the created queue head or NULL if failed to create one.\r
161\r
162**/\r
163PEI_EHC_QH *\r
164EhcCreateQh (\r
165 IN PEI_USB2_HC_DEV *Ehci,\r
166 IN USB_ENDPOINT *Ep\r
167 )\r
168{\r
169 PEI_EHC_QH *Qh;\r
170 QH_HW *QhHw;\r
171\r
172 Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_EHC_QH));\r
173\r
174 if (Qh == NULL) {\r
175 return NULL;\r
176 }\r
177\r
178 Qh->Signature = EHC_QH_SIG;\r
179 Qh->NextQh = NULL;\r
180 Qh->Interval = Ep->PollRate;\r
181\r
182 InitializeListHead (&Qh->Qtds);\r
183\r
184 QhHw = &Qh->QhHw;\r
185 QhHw->HorizonLink = QH_LINK (NULL, 0, TRUE);\r
186 QhHw->DeviceAddr = Ep->DevAddr;\r
187 QhHw->Inactive = 0;\r
188 QhHw->EpNum = Ep->EpAddr;\r
189 QhHw->EpSpeed = Ep->DevSpeed;\r
190 QhHw->DtCtrl = 0;\r
191 QhHw->ReclaimHead = 0;\r
192 QhHw->MaxPacketLen = (UINT32) Ep->MaxPacket;\r
193 QhHw->CtrlEp = 0;\r
194 QhHw->NakReload = QH_NAK_RELOAD;\r
195 QhHw->HubAddr = Ep->HubAddr;\r
196 QhHw->PortNum = Ep->HubPort;\r
197 QhHw->Multiplier = 1;\r
198 QhHw->DataToggle = Ep->Toggle;\r
199\r
200 if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {\r
201 QhHw->Status |= QTD_STAT_DO_SS;\r
202 }\r
203\r
204 switch (Ep->Type) {\r
205 case EHC_CTRL_TRANSFER:\r
206 //\r
207 // Special initialization for the control transfer:\r
208 // 1. Control transfer initialize data toggle from each QTD\r
209 // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint.\r
210 //\r
211 QhHw->DtCtrl = 1;\r
212\r
213 if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {\r
214 QhHw->CtrlEp = 1;\r
215 }\r
216 break;\r
217\r
218 case EHC_INT_TRANSFER_ASYNC:\r
219 case EHC_INT_TRANSFER_SYNC:\r
220 //\r
221 // Special initialization for the interrupt transfer\r
222 // to set the S-Mask and C-Mask\r
223 //\r
224 QhHw->NakReload = 0;\r
225 EhcInitIntQh (Ep, QhHw);\r
226 break;\r
227\r
228 case EHC_BULK_TRANSFER:\r
229 if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) {\r
230 QhHw->Status |= QTD_STAT_DO_PING;\r
231 }\r
232\r
233 break;\r
234 }\r
235\r
236 return Qh;\r
237}\r
238\r
239/**\r
240 Convert the poll interval from application to that\r
241 be used by EHCI interface data structure. Only need\r
242 to get the max 2^n that is less than interval. UEFI\r
243 can't support high speed endpoint with a interval less\r
244 than 8 microframe because interval is specified in\r
245 the unit of ms (millisecond).\r
246\r
247 @param Interval The interval to convert.\r
248\r
249 @retval The converted interval.\r
250\r
251**/\r
252UINTN\r
253EhcConvertPollRate (\r
254 IN UINTN Interval\r
255 )\r
256{\r
257 UINTN BitCount;\r
258\r
259 if (Interval == 0) {\r
260 return 1;\r
261 }\r
262\r
263 //\r
264 // Find the index (1 based) of the highest non-zero bit\r
265 //\r
266 BitCount = 0;\r
267\r
268 while (Interval != 0) {\r
269 Interval >>= 1;\r
270 BitCount++;\r
271 }\r
272\r
273 return (UINTN)1 << (BitCount - 1);\r
274}\r
275\r
276/**\r
277 Free a list of QTDs.\r
278\r
279 @param Ehc The EHCI device.\r
280 @param Qtds The list head of the QTD.\r
281\r
282**/\r
283VOID\r
284EhcFreeQtds (\r
285 IN PEI_USB2_HC_DEV *Ehc,\r
286 IN EFI_LIST_ENTRY *Qtds\r
287 )\r
288{\r
289 EFI_LIST_ENTRY *Entry;\r
290 EFI_LIST_ENTRY *Next;\r
291 PEI_EHC_QTD *Qtd;\r
292\r
293 EFI_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {\r
294 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
295\r
296 RemoveEntryList (&Qtd->QtdList);\r
297 UsbHcFreeMem (Ehc, Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD));\r
298 }\r
299}\r
300\r
301/**\r
302 Free an allocated URB. It is possible for it to be partially inited.\r
303\r
304 @param Ehc The EHCI device.\r
305 @param Urb The URB to free.\r
306\r
307**/\r
308VOID\r
309EhcFreeUrb (\r
310 IN PEI_USB2_HC_DEV *Ehc,\r
311 IN PEI_URB *Urb\r
312 )\r
313{\r
314 if (Urb->RequestPhy != NULL) {\r
315 IoMmuUnmap (Ehc->IoMmu, Urb->RequestMap);\r
316 }\r
317\r
318 if (Urb->DataMap != NULL) {\r
319 IoMmuUnmap (Ehc->IoMmu, Urb->DataMap);\r
320 }\r
321\r
322 if (Urb->Qh != NULL) {\r
323 //\r
324 // Ensure that this queue head has been unlinked from the\r
325 // schedule data structures. Free all the associated QTDs\r
326 //\r
327 EhcFreeQtds (Ehc, &Urb->Qh->Qtds);\r
328 UsbHcFreeMem (Ehc, Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH));\r
329 }\r
330}\r
331\r
332/**\r
333 Create a list of QTDs for the URB.\r
334\r
335 @param Ehc The EHCI device.\r
336 @param Urb The URB to create QTDs for.\r
337\r
338 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD.\r
339 @retval EFI_SUCCESS The QTDs are allocated for the URB.\r
340\r
341**/\r
342EFI_STATUS\r
343EhcCreateQtds (\r
344 IN PEI_USB2_HC_DEV *Ehc,\r
345 IN PEI_URB *Urb\r
346 )\r
347{\r
348 USB_ENDPOINT *Ep;\r
349 PEI_EHC_QH *Qh;\r
350 PEI_EHC_QTD *Qtd;\r
351 PEI_EHC_QTD *StatusQtd;\r
352 PEI_EHC_QTD *NextQtd;\r
353 EFI_LIST_ENTRY *Entry;\r
354 UINT32 AlterNext;\r
355 UINT8 Toggle;\r
356 UINTN Len;\r
357 UINT8 Pid;\r
358\r
359 ASSERT ((Urb != NULL) && (Urb->Qh != NULL));\r
360\r
361 //\r
362 // EHCI follows the alternet next QTD pointer if it meets\r
363 // a short read and the AlterNext pointer is valid. UEFI\r
364 // EHCI driver should terminate the transfer except the\r
365 // control transfer.\r
366 //\r
367 Toggle = 0;\r
368 Qh = Urb->Qh;\r
369 Ep = &Urb->Ep;\r
370 StatusQtd = NULL;\r
371 AlterNext = QTD_LINK (NULL, TRUE);\r
372\r
373 if (Ep->Direction == EfiUsbDataIn) {\r
374 AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);\r
375 }\r
376\r
377 //\r
378 // Build the Setup and status packets for control transfer\r
379 //\r
380 if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {\r
381 Len = sizeof (EFI_USB_DEVICE_REQUEST);\r
382 Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);\r
383\r
384 if (Qtd == NULL) {\r
385 return EFI_OUT_OF_RESOURCES;\r
386 }\r
387\r
388 InsertTailList (&Qh->Qtds, &Qtd->QtdList);\r
389\r
390 //\r
391 // Create the status packet now. Set the AlterNext to it. So, when\r
392 // EHCI meets a short control read, it can resume at the status stage.\r
393 // Use the opposite direction of the data stage, or IN if there is\r
394 // no data stage.\r
395 //\r
396 if (Ep->Direction == EfiUsbDataIn) {\r
397 Pid = QTD_PID_OUTPUT;\r
398 } else {\r
399 Pid = QTD_PID_INPUT;\r
400 }\r
401\r
402 StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);\r
403\r
404 if (StatusQtd == NULL) {\r
405 goto ON_ERROR;\r
406 }\r
407\r
408 if (Ep->Direction == EfiUsbDataIn) {\r
409 AlterNext = QTD_LINK (StatusQtd, FALSE);\r
410 }\r
411\r
412 Toggle = 1;\r
413 }\r
414\r
415 //\r
416 // Build the data packets for all the transfers\r
417 //\r
418 if (Ep->Direction == EfiUsbDataIn) {\r
419 Pid = QTD_PID_INPUT;\r
420 } else {\r
421 Pid = QTD_PID_OUTPUT;\r
422 }\r
423\r
424 Qtd = NULL;\r
425 Len = 0;\r
426\r
427 while (Len < Urb->DataLen) {\r
428 Qtd = EhcCreateQtd (\r
429 Ehc,\r
430 (UINT8 *) Urb->DataPhy + Len,\r
431 Urb->DataLen - Len,\r
432 Pid,\r
433 Toggle,\r
434 Ep->MaxPacket\r
435 );\r
436\r
437 if (Qtd == NULL) {\r
438 goto ON_ERROR;\r
439 }\r
440\r
441 Qtd->QtdHw.AltNext = AlterNext;\r
442 InsertTailList (&Qh->Qtds, &Qtd->QtdList);\r
443\r
444 //\r
445 // Switch the Toggle bit if odd number of packets are included in the QTD.\r
446 //\r
447 if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {\r
448 Toggle = (UINT8) (1 - Toggle);\r
449 }\r
450\r
451 Len += Qtd->DataLen;\r
452 }\r
453\r
454 //\r
455 // Insert the status packet for control transfer\r
456 //\r
457 if (Ep->Type == EHC_CTRL_TRANSFER) {\r
458 InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);\r
459 }\r
460\r
461 //\r
462 // OK, all the QTDs needed are created. Now, fix the NextQtd point\r
463 //\r
464 EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) {\r
465 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
466\r
467 //\r
468 // break if it is the last entry on the list\r
469 //\r
470 if (Entry->ForwardLink == &Qh->Qtds) {\r
471 break;\r
472 }\r
473\r
474 NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList);\r
475 Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);\r
476 }\r
477\r
478 //\r
479 // Link the QTDs to the queue head\r
480 //\r
481 NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList);\r
482 Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE);\r
483 return EFI_SUCCESS;\r
484\r
485ON_ERROR:\r
486 EhcFreeQtds (Ehc, &Qh->Qtds);\r
487 return EFI_OUT_OF_RESOURCES;\r
488}\r
489\r
490/**\r
491 Create a new URB and its associated QTD.\r
492\r
493 @param Ehc The EHCI device.\r
494 @param DevAddr The device address.\r
495 @param EpAddr Endpoint addrress & its direction.\r
496 @param DevSpeed The device speed.\r
497 @param Toggle Initial data toggle to use.\r
498 @param MaxPacket The max packet length of the endpoint.\r
499 @param Hub The transaction translator to use.\r
500 @param Type The transaction type.\r
501 @param Request The standard USB request for control transfer.\r
502 @param Data The user data to transfer.\r
503 @param DataLen The length of data buffer.\r
504 @param Callback The function to call when data is transferred.\r
505 @param Context The context to the callback.\r
506 @param Interval The interval for interrupt transfer.\r
507\r
508 @retval the pointer to the created URB or NULL.\r
509\r
510**/\r
511PEI_URB *\r
512EhcCreateUrb (\r
513 IN PEI_USB2_HC_DEV *Ehc,\r
514 IN UINT8 DevAddr,\r
515 IN UINT8 EpAddr,\r
516 IN UINT8 DevSpeed,\r
517 IN UINT8 Toggle,\r
518 IN UINTN MaxPacket,\r
519 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,\r
520 IN UINTN Type,\r
521 IN EFI_USB_DEVICE_REQUEST *Request,\r
522 IN VOID *Data,\r
523 IN UINTN DataLen,\r
524 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,\r
525 IN VOID *Context,\r
526 IN UINTN Interval\r
527 )\r
528{\r
529 USB_ENDPOINT *Ep;\r
530 EFI_PHYSICAL_ADDRESS PhyAddr;\r
531 EDKII_IOMMU_OPERATION MapOp;\r
532 EFI_STATUS Status;\r
533 UINTN Len;\r
534 PEI_URB *Urb;\r
535 VOID *Map;\r
536\r
537 Urb = Ehc->Urb;\r
538 Urb->Signature = EHC_URB_SIG;\r
539 InitializeListHead (&Urb->UrbList);\r
540\r
541 Ep = &Urb->Ep;\r
542 Ep->DevAddr = DevAddr;\r
543 Ep->EpAddr = (UINT8) (EpAddr & 0x0F);\r
544 Ep->Direction = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);\r
545 Ep->DevSpeed = DevSpeed;\r
546 Ep->MaxPacket = MaxPacket;\r
547\r
548 Ep->HubAddr = 0;\r
549 Ep->HubPort = 0;\r
550\r
551 if (DevSpeed != EFI_USB_SPEED_HIGH) {\r
552 ASSERT (Hub != NULL);\r
553\r
554 Ep->HubAddr = Hub->TranslatorHubAddress;\r
555 Ep->HubPort = Hub->TranslatorPortNumber;\r
556 }\r
557\r
558 Ep->Toggle = Toggle;\r
559 Ep->Type = Type;\r
560 Ep->PollRate = EhcConvertPollRate (Interval);\r
561\r
562 Urb->Request = Request;\r
563 Urb->Data = Data;\r
564 Urb->DataLen = DataLen;\r
565 Urb->Callback = Callback;\r
566 Urb->Context = Context;\r
567 Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep);\r
568\r
569 if (Urb->Qh == NULL) {\r
570 goto ON_ERROR;\r
571 }\r
572\r
573 Urb->RequestPhy = NULL;\r
574 Urb->RequestMap = NULL;\r
575 Urb->DataPhy = NULL;\r
576 Urb->DataMap = NULL;\r
577\r
578 //\r
579 // Map the request and user data\r
580 //\r
581 if (Request != NULL) {\r
582 Len = sizeof (EFI_USB_DEVICE_REQUEST);\r
583 MapOp = EdkiiIoMmuOperationBusMasterRead;\r
584 Status = IoMmuMap (Ehc->IoMmu, MapOp, Request, &Len, &PhyAddr, &Map);\r
585\r
586 if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {\r
587 goto ON_ERROR;\r
588 }\r
589\r
590 Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);\r
591 Urb->RequestMap = Map;\r
592 }\r
593\r
594 if (Data != NULL) {\r
595 Len = DataLen;\r
596\r
597 if (Ep->Direction == EfiUsbDataIn) {\r
598 MapOp = EdkiiIoMmuOperationBusMasterWrite;\r
599 } else {\r
600 MapOp = EdkiiIoMmuOperationBusMasterRead;\r
601 }\r
602\r
603 Status = IoMmuMap (Ehc->IoMmu, MapOp, Data, &Len, &PhyAddr, &Map);\r
604\r
605 if (EFI_ERROR (Status) || (Len != DataLen)) {\r
606 goto ON_ERROR;\r
607 }\r
608\r
609 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);\r
610 Urb->DataMap = Map;\r
611 }\r
612\r
613 Status = EhcCreateQtds (Ehc, Urb);\r
614\r
615 if (EFI_ERROR (Status)) {\r
616 goto ON_ERROR;\r
617 }\r
618\r
619 return Urb;\r
620\r
621ON_ERROR:\r
622 EhcFreeUrb (Ehc, Urb);\r
623 return NULL;\r
624}\r