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