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