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