Home | History | Annotate | Download | only in EhciPei
      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 }
    611