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