• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
EhcCreateQtd(IN PEI_USB2_HC_DEV * Ehc,IN UINT8 * Data,IN UINTN DataLen,IN UINT8 PktId,IN UINT8 Toggle,IN UINTN MaxPacket)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
EhcInitIntQh(IN USB_ENDPOINT * Ep,IN QH_HW * QhHw)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 *
EhcCreateQh(IN PEI_USB2_HC_DEV * Ehci,IN USB_ENDPOINT * Ep)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
EhcConvertPollRate(IN UINTN Interval)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
EhcFreeQtds(IN PEI_USB2_HC_DEV * Ehc,IN EFI_LIST_ENTRY * Qtds)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
EhcFreeUrb(IN PEI_USB2_HC_DEV * Ehc,IN PEI_URB * Urb)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
EhcCreateQtds(IN PEI_USB2_HC_DEV * Ehc,IN PEI_URB * Urb)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 *
EhcCreateUrb(IN PEI_USB2_HC_DEV * Ehc,IN UINT8 DevAddr,IN UINT8 EpAddr,IN UINT8 DevSpeed,IN UINT8 Toggle,IN UINTN MaxPacket,IN EFI_USB2_HC_TRANSACTION_TRANSLATOR * Hub,IN UINTN Type,IN EFI_USB_DEVICE_REQUEST * Request,IN VOID * Data,IN UINTN DataLen,IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,IN VOID * Context,IN UINTN Interval)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 }
611