Home | History | Annotate | Download | only in Dxe
      1 /** @file
      2 OHCI transfer scheduling routines.
      3 
      4 Copyright (c) 2013-2015 Intel Corporation.
      5 
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 
     17 #include "Ohci.h"
     18 
     19 /**
     20 
     21   Add an item of interrupt context
     22 
     23   @param  Ohc                   UHC private data
     24   @param  NewEntry              New entry to add
     25 
     26   @retval EFI_SUCCESS           Item successfully added
     27 
     28 **/
     29 EFI_STATUS
     30 OhciAddInterruptContextEntry (
     31   IN  USB_OHCI_HC_DEV          *Ohc,
     32   IN  INTERRUPT_CONTEXT_ENTRY  *NewEntry
     33   )
     34 {
     35   INTERRUPT_CONTEXT_ENTRY  *Entry;
     36   EFI_TPL                  OriginalTPL;
     37 
     38   OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
     39 
     40   if (Ohc->InterruptContextList == NULL) {
     41     Ohc->InterruptContextList = NewEntry;
     42   } else {
     43     Entry = Ohc->InterruptContextList;
     44     while (Entry->NextEntry != NULL) {
     45       Entry = Entry->NextEntry;
     46     }
     47     Entry->NextEntry = NewEntry;
     48   }
     49 
     50   gBS->RestoreTPL (OriginalTPL);
     51 
     52   return EFI_SUCCESS;
     53 }
     54 
     55 
     56 /**
     57 
     58   Free a interrupt context entry
     59 
     60   @param  Ohc                   UHC private data
     61   @param  Entry                 Pointer to an interrupt context entry
     62 
     63   @retval EFI_SUCCESS           Entry freed
     64   @retval EFI_INVALID_PARAMETER Entry is NULL
     65 
     66 **/
     67 EFI_STATUS
     68 OhciFreeInterruptContextEntry (
     69   IN USB_OHCI_HC_DEV          *Ohc,
     70   IN INTERRUPT_CONTEXT_ENTRY  *Entry
     71   )
     72 {
     73   TD_DESCRIPTOR           *Td;
     74   if (Entry == NULL) {
     75     return EFI_INVALID_PARAMETER;
     76   }
     77   if (Entry->UCBufferMapping != NULL) {
     78     Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping);
     79   }
     80   if (Entry->UCBuffer != NULL) {
     81     FreePool(Entry->UCBuffer);
     82   }
     83   while (Entry->DataTd) {
     84     Td = Entry->DataTd;
     85     Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer);
     86     UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR));
     87   }
     88   FreePool(Entry);
     89   return EFI_SUCCESS;
     90 }
     91 
     92 
     93 /**
     94 
     95   Free entries match the device address and endpoint address
     96 
     97   @Param  Ohc                   UHC private date
     98   @Param  DeviceAddress         Item to free must match this device address
     99   @Param  EndPointAddress       Item to free must match this end point address
    100   @Param  DataToggle            DataToggle for output
    101 
    102   @retval  EFI_SUCCESS          Items match the requirement removed
    103 
    104 **/
    105 EFI_STATUS
    106 OhciFreeInterruptContext(
    107   IN  USB_OHCI_HC_DEV     *Ohc,
    108   IN  UINT8               DeviceAddress,
    109   IN  UINT8               EndPointAddress,
    110   OUT UINT8               *DataToggle
    111   )
    112 {
    113   INTERRUPT_CONTEXT_ENTRY  *Entry;
    114   INTERRUPT_CONTEXT_ENTRY  *TempEntry;
    115   EFI_TPL                  OriginalTPL;
    116 
    117 
    118   OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
    119 
    120   while (Ohc->InterruptContextList != NULL &&
    121     Ohc->InterruptContextList->DeviceAddress == DeviceAddress &&
    122     Ohc->InterruptContextList->EndPointAddress == EndPointAddress) {
    123     TempEntry = Ohc->InterruptContextList;
    124     Ohc->InterruptContextList = Ohc->InterruptContextList->NextEntry;
    125     if (DataToggle != NULL) {
    126       *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);
    127     }
    128     OhciFreeInterruptContextEntry (Ohc, TempEntry);
    129   }
    130 
    131   Entry = Ohc->InterruptContextList;
    132   if (Entry == NULL) {
    133     gBS->RestoreTPL (OriginalTPL);
    134     return EFI_SUCCESS;
    135   }
    136   while (Entry->NextEntry != NULL) {
    137     if (Entry->NextEntry->DeviceAddress == DeviceAddress &&
    138       Entry->NextEntry->EndPointAddress == EndPointAddress) {
    139       TempEntry = Entry->NextEntry;
    140       Entry->NextEntry = Entry->NextEntry->NextEntry;
    141       if (DataToggle != NULL) {
    142         *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);
    143       }
    144       OhciFreeInterruptContextEntry (Ohc, TempEntry);
    145     } else {
    146       Entry = Entry->NextEntry;
    147     }
    148   }
    149 
    150   gBS->RestoreTPL (OriginalTPL);
    151 
    152   return EFI_SUCCESS;
    153 }
    154 
    155 
    156 /**
    157 
    158   Convert Error code from OHCI format to EFI format
    159 
    160   @Param  ErrorCode             ErrorCode in OHCI format
    161 
    162   @retval                       ErrorCode in EFI format
    163 
    164 **/
    165 UINT32
    166 ConvertErrorCode (
    167   IN  UINT32              ErrorCode
    168   )
    169 {
    170   UINT32                  TransferResult;
    171 
    172   switch (ErrorCode) {
    173     case TD_NO_ERROR:
    174       TransferResult = EFI_USB_NOERROR;
    175       break;
    176 
    177     case TD_TOBE_PROCESSED:
    178     case TD_TOBE_PROCESSED_2:
    179       TransferResult = EFI_USB_ERR_NOTEXECUTE;
    180       break;
    181 
    182     case TD_DEVICE_STALL:
    183       TransferResult = EFI_USB_ERR_STALL;
    184       break;
    185 
    186     case TD_BUFFER_OVERRUN:
    187     case TD_BUFFER_UNDERRUN:
    188       TransferResult = EFI_USB_ERR_BUFFER;
    189       break;
    190 
    191     case TD_CRC_ERROR:
    192       TransferResult = EFI_USB_ERR_CRC;
    193       break;
    194 
    195     case TD_NO_RESPONSE:
    196       TransferResult = EFI_USB_ERR_TIMEOUT;
    197       break;
    198 
    199     case TD_BITSTUFFING_ERROR:
    200       TransferResult = EFI_USB_ERR_BITSTUFF;
    201       break;
    202 
    203     default:
    204       TransferResult = EFI_USB_ERR_SYSTEM;
    205   }
    206 
    207   return TransferResult;
    208 }
    209 
    210 
    211 /**
    212 
    213   Check TDs Results
    214 
    215   @Param  Ohc                   UHC private data
    216   @Param  Td                    TD_DESCRIPTOR
    217   @Param  Result                Result to return
    218 
    219   @retval TRUE                  means OK
    220   @retval FLASE                 means Error or Short packet
    221 
    222 **/
    223 BOOLEAN
    224 OhciCheckTDsResults (
    225   IN  USB_OHCI_HC_DEV     *Ohc,
    226   IN  TD_DESCRIPTOR       *Td,
    227   OUT UINT32              *Result
    228   )
    229 {
    230   UINT32                  TdCompletionCode;
    231 
    232   *Result   = EFI_USB_NOERROR;
    233 
    234   while (Td) {
    235     TdCompletionCode = Td->Word0.ConditionCode;
    236 
    237     *Result |= ConvertErrorCode(TdCompletionCode);
    238     //
    239     // if any error encountered, stop processing the left TDs.
    240     //
    241     if (*Result) {
    242       return FALSE;
    243     }
    244 
    245     Td = (TD_DESCRIPTOR *)(UINTN)(Td->NextTDPointer);
    246   }
    247   return TRUE;
    248 
    249 }
    250 
    251 
    252 /**
    253 
    254   Check the task status on an ED
    255 
    256   @Param  Ed                    Pointer to the ED task that TD hooked on
    257   @Param  HeadTd                TD header for current transaction
    258 
    259   @retval                       Task Status Code
    260 
    261 **/
    262 
    263 UINT32
    264 CheckEDStatus (
    265   IN  ED_DESCRIPTOR       *Ed,
    266   IN  TD_DESCRIPTOR       *HeadTd,
    267   OUT OHCI_ED_RESULT      *EdResult
    268   )
    269 {
    270   while(HeadTd != NULL) {
    271     if (HeadTd->NextTDPointer == 0) {
    272       return TD_NO_ERROR;
    273     }
    274     if (HeadTd->Word0.ConditionCode != 0) {
    275       return HeadTd->Word0.ConditionCode;
    276     }
    277     EdResult->NextToggle = ((UINT8)(HeadTd->Word0.DataToggle) & BIT0) ^ BIT0;
    278     HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);
    279   }
    280   if (OhciGetEDField (Ed, ED_TDHEAD_PTR) != OhciGetEDField (Ed, ED_TDTAIL_PTR)) {
    281     return TD_TOBE_PROCESSED;
    282   }
    283   return TD_NO_ERROR;
    284 }
    285 
    286 /**
    287 
    288   Check the task status
    289 
    290   @Param  Ohc                   UHC private data
    291   @Param  ListType              Pipe type
    292   @Param  Ed                    Pointer to the ED task hooked on
    293   @Param  HeadTd                Head of TD corresponding to the task
    294   @Param  ErrorCode             return the ErrorCode
    295 
    296   @retval  EFI_SUCCESS          Task done
    297   @retval  EFI_NOT_READY        Task on processing
    298   @retval  EFI_DEVICE_ERROR     Some error occured
    299 
    300 **/
    301 EFI_STATUS
    302 CheckIfDone (
    303   IN  USB_OHCI_HC_DEV       *Ohc,
    304   IN  DESCRIPTOR_LIST_TYPE  ListType,
    305   IN  ED_DESCRIPTOR         *Ed,
    306   IN  TD_DESCRIPTOR         *HeadTd,
    307   OUT OHCI_ED_RESULT        *EdResult
    308   )
    309 {
    310   EdResult->ErrorCode = TD_TOBE_PROCESSED;
    311 
    312   switch (ListType) {
    313     case CONTROL_LIST:
    314       if (OhciGetHcCommandStatus (Ohc, CONTROL_LIST_FILLED) != 0) {
    315         return EFI_NOT_READY;
    316       }
    317       break;
    318     case BULK_LIST:
    319       if (OhciGetHcCommandStatus (Ohc, BULK_LIST_FILLED) != 0) {
    320         return EFI_NOT_READY;
    321       }
    322       break;
    323     default:
    324       break;
    325   }
    326 
    327   EdResult->ErrorCode = CheckEDStatus (Ed, HeadTd, EdResult);
    328 
    329   if (EdResult->ErrorCode == TD_NO_ERROR) {
    330     return EFI_SUCCESS;
    331   } else if (EdResult->ErrorCode == TD_TOBE_PROCESSED) {
    332     return EFI_NOT_READY;
    333   } else {
    334     return EFI_DEVICE_ERROR;
    335   }
    336 }
    337 
    338 
    339 /**
    340 
    341   Convert TD condition code to Efi Status
    342 
    343   @Param  ConditionCode         Condition code to convert
    344 
    345   @retval  EFI_SUCCESS          No error occured
    346   @retval  EFI_NOT_READY        TD still on processing
    347   @retval  EFI_DEVICE_ERROR     Error occured in processing TD
    348 
    349 **/
    350 
    351 EFI_STATUS
    352 OhciTDConditionCodeToStatus (
    353   IN  UINT32              ConditionCode
    354   )
    355 {
    356   if (ConditionCode == TD_NO_ERROR) {
    357     return EFI_SUCCESS;
    358   }
    359 
    360   if (ConditionCode == TD_TOBE_PROCESSED) {
    361     return EFI_NOT_READY;
    362   }
    363 
    364   return EFI_DEVICE_ERROR;
    365 }
    366 
    367 /**
    368 
    369   Invoke callbacks hooked on done TDs
    370 
    371   @Param  Entry                 Interrupt transfer transaction information data structure
    372   @Param  Context               Ohc private data
    373 
    374 **/
    375 
    376 VOID
    377 OhciInvokeInterruptCallBack(
    378   IN  INTERRUPT_CONTEXT_ENTRY  *Entry,
    379   IN  UINT32                   Result
    380 )
    381 {
    382   //Generally speaking, Keyboard driver should not
    383   //check the Keyboard buffer if an error happens, it will be robust
    384   //if we NULLed the buffer once error happens
    385   if (Result) {
    386     Entry->CallBackFunction (
    387              NULL,
    388              0,
    389              Entry->Context,
    390              Result
    391              );
    392   }else{
    393     Entry->CallBackFunction (
    394              (VOID *)(UINTN)(Entry->DataTd->DataBuffer),
    395              Entry->DataTd->ActualSendLength,
    396              Entry->Context,
    397              Result
    398              );
    399   }
    400 }
    401 
    402 
    403 /**
    404 
    405   Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs
    406 
    407   @param  Event                 Event handle
    408   @param  Context               Device private data
    409 
    410 **/
    411 
    412 VOID
    413 EFIAPI
    414 OhciHouseKeeper (
    415   IN  EFI_EVENT           Event,
    416   IN  VOID                *Context
    417   )
    418 {
    419 
    420   USB_OHCI_HC_DEV          *Ohc;
    421   INTERRUPT_CONTEXT_ENTRY  *Entry;
    422   INTERRUPT_CONTEXT_ENTRY  *PreEntry;
    423   ED_DESCRIPTOR            *Ed;
    424   TD_DESCRIPTOR            *DataTd;
    425   TD_DESCRIPTOR            *HeadTd;
    426 
    427   UINT8                    Toggle;
    428   EFI_TPL                  OriginalTPL;
    429   UINT32                   Result;
    430 
    431   Ohc = (USB_OHCI_HC_DEV *) Context;
    432   OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY);
    433 
    434   Entry = Ohc->InterruptContextList;
    435   PreEntry = NULL;
    436 
    437   while(Entry != NULL) {
    438 
    439     OhciCheckTDsResults(Ohc, Entry->DataTd, &Result );
    440     if (((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) ||
    441       ((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE)) {
    442       PreEntry = Entry;
    443       Entry = Entry->NextEntry;
    444       continue;
    445     }
    446 
    447     if (Entry->CallBackFunction != NULL) {
    448       OhciInvokeInterruptCallBack (Entry, Result);
    449       if (Ohc->InterruptContextList == NULL) {
    450         gBS->RestoreTPL (OriginalTPL);
    451         return;
    452       }
    453     }
    454     if (Entry->IsPeriodic) {
    455 
    456       Ed = Entry->Ed;
    457       HeadTd = Entry->DataTd;
    458       DataTd = HeadTd;
    459       Toggle = 0;
    460       if (Result == EFI_USB_NOERROR) {
    461         //
    462         // Update toggle if there is no error, and re-submit the interrupt Ed&Tds
    463         //
    464         if ((Ed != NULL) && (DataTd != NULL)) {
    465           Ed->Word0.Skip = 1;
    466         }
    467         //
    468         // From hcir1_0a.pdf 4.2.2
    469         // ToggleCarry:This bit is the data toggle carry bit,
    470         // Whenever a TD is retired, this bit is written to
    471         // contain the last data toggle value(LSb of data Toggel
    472         // file) from the retired TD.
    473         // This field is not used for Isochronous Endpoints
    474         //
    475         if (Ed == NULL) {
    476           return;
    477         }
    478         Toggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);
    479         while(DataTd != NULL) {
    480           if (DataTd->NextTDPointer == 0) {
    481             DataTd->Word0.DataToggle = 0;
    482             break;
    483           } else {
    484             OhciSetTDField (DataTd, TD_DT_TOGGLE, Toggle);
    485           }
    486           DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);
    487           Toggle ^= 1;
    488         }
    489         //
    490         // HC will only update DataToggle, ErrorCount, ConditionCode
    491         // CurrentBufferPointer & NextTD, so we only need to update
    492         // them once we want to active them again
    493         //
    494         DataTd = HeadTd;
    495         while (DataTd != NULL) {
    496           if (DataTd->NextTDPointer == 0) {
    497             OhciSetTDField (DataTd, TD_ERROR_CNT | TD_COND_CODE | TD_CURR_BUFFER_PTR | TD_NEXT_PTR, 0);
    498             break;
    499           }
    500           OhciSetTDField (DataTd, TD_ERROR_CNT, 0);
    501           OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);
    502           DataTd->NextTD = DataTd->NextTDPointer;
    503           DataTd->CurrBufferPointer = DataTd->DataBuffer;
    504           DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);
    505         }
    506         //
    507         // Active current Ed,Td
    508         //
    509         // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,
    510         // So we only need to update them once we want to active them again.
    511         //
    512         if ((Ed != NULL) && (DataTd != NULL)) {
    513           Ed->Word2.TdHeadPointer = (UINT32)((UINTN)HeadTd>>4);
    514           OhciSetEDField (Ed, ED_HALTED | ED_DTTOGGLE, 0);
    515           Ed->Word0.Skip = 0;
    516         }
    517       }
    518     } else {
    519       if (PreEntry == NULL) {
    520         Ohc->InterruptContextList = Entry->NextEntry;
    521       } else {
    522         PreEntry = Entry;
    523         PreEntry->NextEntry = Entry->NextEntry;
    524       }
    525       OhciFreeInterruptContextEntry (Ohc, PreEntry);
    526       gBS->RestoreTPL (OriginalTPL);
    527       return;
    528     }
    529     PreEntry = Entry;
    530     Entry = Entry->NextEntry;
    531   }
    532   gBS->RestoreTPL (OriginalTPL);
    533 }
    534 
    535