]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/EhciPei/EhciUrb.c
597a4947f5bc6e7bd9fba11463125ec5c70ce7c5
[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, 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->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->Qh != NULL) {
322 //
323 // Ensure that this queue head has been unlinked from the
324 // schedule data structures. Free all the associated QTDs
325 //
326 EhcFreeQtds (Ehc, &Urb->Qh->Qtds);
327 UsbHcFreeMem (Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH));
328 }
329 }
330
331 /**
332 Create a list of QTDs for the URB.
333
334 @param Ehc The EHCI device.
335 @param Urb The URB to create QTDs for.
336
337 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for QTD.
338 @retval EFI_SUCCESS The QTDs are allocated for the URB.
339
340 **/
341 EFI_STATUS
342 EhcCreateQtds (
343 IN PEI_USB2_HC_DEV *Ehc,
344 IN PEI_URB *Urb
345 )
346 {
347 USB_ENDPOINT *Ep;
348 PEI_EHC_QH *Qh;
349 PEI_EHC_QTD *Qtd;
350 PEI_EHC_QTD *StatusQtd;
351 PEI_EHC_QTD *NextQtd;
352 EFI_LIST_ENTRY *Entry;
353 UINT32 AlterNext;
354 UINT8 Toggle;
355 UINTN Len;
356 UINT8 Pid;
357
358 ASSERT ((Urb != NULL) && (Urb->Qh != NULL));
359
360 //
361 // EHCI follows the alternet next QTD pointer if it meets
362 // a short read and the AlterNext pointer is valid. UEFI
363 // EHCI driver should terminate the transfer except the
364 // control transfer.
365 //
366 Toggle = 0;
367 Qh = Urb->Qh;
368 Ep = &Urb->Ep;
369 StatusQtd = NULL;
370 AlterNext = QTD_LINK (NULL, TRUE);
371
372 if (Ep->Direction == EfiUsbDataIn) {
373 AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);
374 }
375
376 //
377 // Build the Setup and status packets for control transfer
378 //
379 if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {
380 Len = sizeof (EFI_USB_DEVICE_REQUEST);
381 Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);
382
383 if (Qtd == NULL) {
384 return EFI_OUT_OF_RESOURCES;
385 }
386
387 InsertTailList (&Qh->Qtds, &Qtd->QtdList);
388
389 //
390 // Create the status packet now. Set the AlterNext to it. So, when
391 // EHCI meets a short control read, it can resume at the status stage.
392 // Use the opposite direction of the data stage, or IN if there is
393 // no data stage.
394 //
395 if (Ep->Direction == EfiUsbDataIn) {
396 Pid = QTD_PID_OUTPUT;
397 } else {
398 Pid = QTD_PID_INPUT;
399 }
400
401 StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);
402
403 if (StatusQtd == NULL) {
404 goto ON_ERROR;
405 }
406
407 if (Ep->Direction == EfiUsbDataIn) {
408 AlterNext = QTD_LINK (StatusQtd, FALSE);
409 }
410
411 Toggle = 1;
412 }
413
414 //
415 // Build the data packets for all the transfers
416 //
417 if (Ep->Direction == EfiUsbDataIn) {
418 Pid = QTD_PID_INPUT;
419 } else {
420 Pid = QTD_PID_OUTPUT;
421 }
422
423 Qtd = NULL;
424 Len = 0;
425
426 while (Len < Urb->DataLen) {
427 Qtd = EhcCreateQtd (
428 Ehc,
429 (UINT8 *) Urb->DataPhy + Len,
430 Urb->DataLen - Len,
431 Pid,
432 Toggle,
433 Ep->MaxPacket
434 );
435
436 if (Qtd == NULL) {
437 goto ON_ERROR;
438 }
439
440 Qtd->QtdHw.AltNext = AlterNext;
441 InsertTailList (&Qh->Qtds, &Qtd->QtdList);
442
443 //
444 // Switch the Toggle bit if odd number of packets are included in the QTD.
445 //
446 if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {
447 Toggle = (UINT8) (1 - Toggle);
448 }
449
450 Len += Qtd->DataLen;
451 }
452
453 //
454 // Insert the status packet for control transfer
455 //
456 if (Ep->Type == EHC_CTRL_TRANSFER) {
457 InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);
458 }
459
460 //
461 // OK, all the QTDs needed are created. Now, fix the NextQtd point
462 //
463 EFI_LIST_FOR_EACH (Entry, &Qh->Qtds) {
464 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
465
466 //
467 // break if it is the last entry on the list
468 //
469 if (Entry->ForwardLink == &Qh->Qtds) {
470 break;
471 }
472
473 NextQtd = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList);
474 Qtd->QtdHw.NextQtd = QTD_LINK (NextQtd, FALSE);
475 }
476
477 //
478 // Link the QTDs to the queue head
479 //
480 NextQtd = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList);
481 Qh->QhHw.NextQtd = QTD_LINK (NextQtd, FALSE);
482 return EFI_SUCCESS;
483
484 ON_ERROR:
485 EhcFreeQtds (Ehc, &Qh->Qtds);
486 return EFI_OUT_OF_RESOURCES;
487 }
488
489 /**
490 Create a new URB and its associated QTD.
491
492 @param Ehc The EHCI device.
493 @param DevAddr The device address.
494 @param EpAddr Endpoint addrress & its direction.
495 @param DevSpeed The device speed.
496 @param Toggle Initial data toggle to use.
497 @param MaxPacket The max packet length of the endpoint.
498 @param Hub The transaction translator to use.
499 @param Type The transaction type.
500 @param Request The standard USB request for control transfer.
501 @param Data The user data to transfer.
502 @param DataLen The length of data buffer.
503 @param Callback The function to call when data is transferred.
504 @param Context The context to the callback.
505 @param Interval The interval for interrupt transfer.
506
507 @retval the pointer to the created URB or NULL.
508
509 **/
510 PEI_URB *
511 EhcCreateUrb (
512 IN PEI_USB2_HC_DEV *Ehc,
513 IN UINT8 DevAddr,
514 IN UINT8 EpAddr,
515 IN UINT8 DevSpeed,
516 IN UINT8 Toggle,
517 IN UINTN MaxPacket,
518 IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
519 IN UINTN Type,
520 IN EFI_USB_DEVICE_REQUEST *Request,
521 IN VOID *Data,
522 IN UINTN DataLen,
523 IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,
524 IN VOID *Context,
525 IN UINTN Interval
526 )
527 {
528 USB_ENDPOINT *Ep;
529 EFI_PHYSICAL_ADDRESS PhyAddr;
530 EFI_STATUS Status;
531 UINTN Len;
532 PEI_URB *Urb;
533 VOID *Map;
534
535
536 Map = NULL;
537
538 Urb = Ehc->Urb;
539 Urb->Signature = EHC_URB_SIG;
540 InitializeListHead (&Urb->UrbList);
541
542 Ep = &Urb->Ep;
543 Ep->DevAddr = DevAddr;
544 Ep->EpAddr = (UINT8) (EpAddr & 0x0F);
545 Ep->Direction = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
546 Ep->DevSpeed = DevSpeed;
547 Ep->MaxPacket = MaxPacket;
548
549 Ep->HubAddr = 0;
550 Ep->HubPort = 0;
551
552 if (DevSpeed != EFI_USB_SPEED_HIGH) {
553 ASSERT (Hub != NULL);
554
555 Ep->HubAddr = Hub->TranslatorHubAddress;
556 Ep->HubPort = Hub->TranslatorPortNumber;
557 }
558
559 Ep->Toggle = Toggle;
560 Ep->Type = Type;
561 Ep->PollRate = EhcConvertPollRate (Interval);
562
563 Urb->Request = Request;
564 Urb->Data = Data;
565 Urb->DataLen = DataLen;
566 Urb->Callback = Callback;
567 Urb->Context = Context;
568 Urb->Qh = EhcCreateQh (Ehc, &Urb->Ep);
569
570 if (Urb->Qh == NULL) {
571 goto ON_ERROR;
572 }
573
574 //
575 // Map the request and user data
576 //
577 if (Request != NULL) {
578 Len = sizeof (EFI_USB_DEVICE_REQUEST);
579 PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Request ;
580 if ( (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {
581 goto ON_ERROR;
582 }
583
584 Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);
585 Urb->RequestMap = Map;
586 }
587
588 if (Data != NULL) {
589 Len = DataLen;
590 PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) Data ;
591 if ( (Len != DataLen)) {
592 goto ON_ERROR;
593 }
594
595 Urb->DataPhy = (VOID *) ((UINTN) PhyAddr);
596 Urb->DataMap = Map;
597 }
598
599 Status = EhcCreateQtds (Ehc, Urb);
600
601 if (EFI_ERROR (Status)) {
602 goto ON_ERROR;
603 }
604
605 return Urb;
606
607 ON_ERROR:
608 EhcFreeUrb (Ehc, Urb);
609 return NULL;
610 }