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