1 /** @file 2 3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR> 4 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include <Protocol/AndroidFastbootTransport.h> 16 #include <Protocol/AndroidFastbootPlatform.h> 17 #include <Protocol/SimpleTextOut.h> 18 #include <Protocol/SimpleTextIn.h> 19 20 #include <Library/AbootimgLib.h> 21 #include <Library/BaseMemoryLib.h> 22 #include <Library/PcdLib.h> 23 #include <Library/PrintLib.h> 24 #include <Library/UefiApplicationEntryPoint.h> 25 #include <Library/UefiBootServicesTableLib.h> 26 #include <Library/UefiRuntimeServicesTableLib.h> 27 28 #define ANDROID_FASTBOOT_VERSION "0.7" 29 30 #define SPARSE_HEADER_MAGIC 0xED26FF3A 31 #define CHUNK_TYPE_RAW 0xCAC1 32 #define CHUNK_TYPE_FILL 0xCAC2 33 #define CHUNK_TYPE_DONT_CARE 0xCAC3 34 #define CHUNK_TYPE_CRC32 0xCAC4 35 36 #define FILL_BUF_SIZE 1024 37 38 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype))) 39 40 #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 41 42 typedef struct _SPARSE_HEADER { 43 UINT32 Magic; 44 UINT16 MajorVersion; 45 UINT16 MinorVersion; 46 UINT16 FileHeaderSize; 47 UINT16 ChunkHeaderSize; 48 UINT32 BlockSize; 49 UINT32 TotalBlocks; 50 UINT32 TotalChunks; 51 UINT32 ImageChecksum; 52 } SPARSE_HEADER; 53 54 typedef struct _CHUNK_HEADER { 55 UINT16 ChunkType; 56 UINT16 Reserved1; 57 UINT32 ChunkSize; 58 UINT32 TotalSize; 59 } CHUNK_HEADER; 60 61 /* 62 * UEFI Application using the FASTBOOT_TRANSPORT_PROTOCOL and 63 * FASTBOOT_PLATFORM_PROTOCOL to implement the Android Fastboot protocol. 64 */ 65 66 STATIC FASTBOOT_TRANSPORT_PROTOCOL *mTransport; 67 STATIC FASTBOOT_PLATFORM_PROTOCOL *mPlatform; 68 69 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut; 70 71 typedef enum { 72 ExpectCmdState, 73 ExpectDataState, 74 FastbootStateMax 75 } ANDROID_FASTBOOT_STATE; 76 77 STATIC ANDROID_FASTBOOT_STATE mState = ExpectCmdState; 78 79 // When in ExpectDataState, the number of bytes of data to expect: 80 STATIC UINT64 mNumDataBytes; 81 // .. and the number of bytes so far received this data phase 82 STATIC UINT64 mBytesReceivedSoFar; 83 // .. and the buffer to save data into 84 STATIC UINT8 *mDataBuffer = NULL; 85 86 // Event notify functions, from which gBS->Exit shouldn't be called, can signal 87 // this event when the application should exit 88 STATIC EFI_EVENT mFinishedEvent; 89 90 STATIC EFI_EVENT mFatalSendErrorEvent; 91 92 // This macro uses sizeof - only use it on arrays (i.e. string literals) 93 #define SEND_LITERAL(Str) mTransport->Send ( \ 94 sizeof (Str) - 1, \ 95 Str, \ 96 &mFatalSendErrorEvent \ 97 ) 98 #define MATCH_CMD_LITERAL(Cmd, Buf) !AsciiStrnCmp (Cmd, Buf, sizeof (Cmd) - 1) 99 100 #define IS_LOWERCASE_ASCII(Char) (Char >= 'a' && Char <= 'z') 101 102 #define FASTBOOT_STRING_MAX_LENGTH 256 103 #define FASTBOOT_COMMAND_MAX_LENGTH 64 104 105 STATIC 106 VOID 107 HandleGetVar ( 108 IN CHAR8 *CmdArg 109 ) 110 { 111 CHAR8 Response[FASTBOOT_COMMAND_MAX_LENGTH + 1] = "OKAY"; 112 EFI_STATUS Status; 113 114 // Respond to getvar:version with 0.4 (version of Fastboot protocol) 115 if (!AsciiStrnCmp ("version", CmdArg, sizeof ("version") - 1 )) { 116 SEND_LITERAL ("OKAY" ANDROID_FASTBOOT_VERSION); 117 } else { 118 // All other variables are assumed to be platform specific 119 Status = mPlatform->GetVar (CmdArg, Response + 4); 120 if (EFI_ERROR (Status)) { 121 SEND_LITERAL ("FAILSomething went wrong when looking up the variable"); 122 } else { 123 mTransport->Send (AsciiStrLen (Response), Response, &mFatalSendErrorEvent); 124 } 125 } 126 } 127 128 STATIC 129 VOID 130 HandleDownload ( 131 IN CHAR8 *NumBytesString 132 ) 133 { 134 CHAR8 Response[13]; 135 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 136 137 // Argument is 8-character ASCII string hex representation of number of bytes 138 // that will be sent in the data phase. 139 // Response is "DATA" + that same 8-character string. 140 141 // Replace any previously downloaded data 142 if (mDataBuffer != NULL) { 143 FreePool (mDataBuffer); 144 mDataBuffer = NULL; 145 } 146 147 // Parse out number of data bytes to expect 148 mNumDataBytes = AsciiStrHexToUint64 (NumBytesString); 149 if (mNumDataBytes == 0) { 150 mTextOut->OutputString (mTextOut, L"ERROR: Fail to get the number of bytes to download.\r\n"); 151 SEND_LITERAL ("FAILFailed to get the number of bytes to download"); 152 return; 153 } 154 155 UnicodeSPrint (OutputString, sizeof (OutputString), L"Downloading %d bytes\r\n", mNumDataBytes); 156 mTextOut->OutputString (mTextOut, OutputString); 157 158 mDataBuffer = AllocatePool (mNumDataBytes); 159 if (mDataBuffer == NULL) { 160 SEND_LITERAL ("FAILNot enough memory"); 161 } else { 162 ZeroMem (Response, sizeof Response); 163 if (mTransport->RequestReceive) { 164 mTransport->RequestReceive (mNumDataBytes); 165 } 166 AsciiSPrint (Response, sizeof Response, "DATA%x", 167 (UINT32)mNumDataBytes); 168 mTransport->Send (sizeof Response - 1, Response, &mFatalSendErrorEvent); 169 170 mState = ExpectDataState; 171 mBytesReceivedSoFar = 0; 172 } 173 } 174 175 STATIC 176 EFI_STATUS 177 FlashSparseImage ( 178 IN CHAR8 *PartitionName, 179 IN SPARSE_HEADER *SparseHeader 180 ) 181 { 182 EFI_STATUS Status = EFI_SUCCESS; 183 UINTN Chunk, Offset = 0, Index; 184 VOID *Image; 185 CHUNK_HEADER *ChunkHeader; 186 UINT32 FillBuf[FILL_BUF_SIZE]; 187 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 188 189 Image = (VOID *)SparseHeader; 190 Image += SparseHeader->FileHeaderSize; 191 for (Chunk = 0; Chunk < SparseHeader->TotalChunks; Chunk++) { 192 ChunkHeader = (CHUNK_HEADER *)Image; 193 DEBUG ((DEBUG_INFO, "Chunk #%d - Type: 0x%x Size: %d TotalSize: %d Offset %d\n", 194 (Chunk+1), ChunkHeader->ChunkType, ChunkHeader->ChunkSize, 195 ChunkHeader->TotalSize, Offset)); 196 Image += sizeof (CHUNK_HEADER); 197 switch (ChunkHeader->ChunkType) { 198 case CHUNK_TYPE_RAW: 199 Status = mPlatform->FlashPartitionEx ( 200 PartitionName, 201 Offset, 202 ChunkHeader->ChunkSize * SparseHeader->BlockSize, 203 Image 204 ); 205 if (EFI_ERROR (Status)) { 206 return Status; 207 } 208 Image += ChunkHeader->ChunkSize * SparseHeader->BlockSize; 209 Offset += ChunkHeader->ChunkSize * SparseHeader->BlockSize; 210 break; 211 case CHUNK_TYPE_FILL: 212 SetMem32 (FillBuf, FILL_BUF_SIZE * sizeof (UINT32), *(UINT32 *)Image); 213 Image += sizeof (UINT32); 214 for (Index = 0; Index < ChunkHeader->ChunkSize; Index++) { 215 Status = mPlatform->FlashPartitionEx ( 216 PartitionName, 217 Offset, 218 SparseHeader->BlockSize, 219 FillBuf 220 ); 221 if (EFI_ERROR (Status)) { 222 return Status; 223 } 224 Offset += SparseHeader->BlockSize; 225 } 226 break; 227 case CHUNK_TYPE_DONT_CARE: 228 Offset += ChunkHeader->ChunkSize * SparseHeader->BlockSize; 229 break; 230 default: 231 UnicodeSPrint ( 232 OutputString, 233 sizeof (OutputString), 234 L"Unsupported Chunk Type:0x%x\n", 235 ChunkHeader->ChunkType 236 ); 237 mTextOut->OutputString (mTextOut, OutputString); 238 break; 239 } 240 } 241 return Status; 242 } 243 244 STATIC 245 VOID 246 HandleFlash ( 247 IN CHAR8 *PartitionName 248 ) 249 { 250 EFI_STATUS Status; 251 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 252 SPARSE_HEADER *SparseHeader; 253 254 // Build output string 255 UnicodeSPrint (OutputString, sizeof (OutputString), L"Flashing partition %a\r\n", PartitionName); 256 mTextOut->OutputString (mTextOut, OutputString); 257 258 if (mDataBuffer == NULL) { 259 // Doesn't look like we were sent any data 260 SEND_LITERAL ("FAILNo data to flash"); 261 return; 262 } 263 264 SparseHeader = (SPARSE_HEADER *)mDataBuffer; 265 if (SparseHeader->Magic == SPARSE_HEADER_MAGIC) { 266 DEBUG ((DEBUG_INFO, "Sparse Magic: 0x%x Major: %d Minor: %d fhs: %d chs: %d bs: %d tbs: %d tcs: %d checksum: %d \n", 267 SparseHeader->Magic, SparseHeader->MajorVersion, SparseHeader->MinorVersion, SparseHeader->FileHeaderSize, 268 SparseHeader->ChunkHeaderSize, SparseHeader->BlockSize, SparseHeader->TotalBlocks, 269 SparseHeader->TotalChunks, SparseHeader->ImageChecksum)); 270 if (SparseHeader->MajorVersion != 1) { 271 DEBUG ((DEBUG_ERROR, "Sparse image version %d.%d not supported.\n", 272 SparseHeader->MajorVersion, SparseHeader->MinorVersion)); 273 return; 274 } 275 Status = FlashSparseImage (PartitionName, SparseHeader); 276 } else { 277 Status = mPlatform->FlashPartition ( 278 PartitionName, 279 mNumDataBytes, 280 mDataBuffer 281 ); 282 } 283 switch (Status) { 284 case EFI_SUCCESS: 285 mTextOut->OutputString (mTextOut, L"Done.\r\n"); 286 SEND_LITERAL ("OKAY"); 287 break; 288 case EFI_NOT_FOUND: 289 SEND_LITERAL ("FAILNo such partition."); 290 mTextOut->OutputString (mTextOut, L"No such partition.\r\n"); 291 break; 292 default: 293 SEND_LITERAL ("FAILError flashing partition."); 294 mTextOut->OutputString (mTextOut, L"Error flashing partition.\r\n"); 295 DEBUG ((EFI_D_ERROR, "Couldn't flash image:\n")); 296 break; 297 } 298 } 299 300 STATIC 301 VOID 302 HandleErase ( 303 IN CHAR8 *PartitionName 304 ) 305 { 306 EFI_STATUS Status; 307 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 308 309 // Build output string 310 UnicodeSPrint (OutputString, sizeof (OutputString), L"Erasing partition %a\r\n", PartitionName); 311 mTextOut->OutputString (mTextOut, OutputString); 312 313 Status = mPlatform->ErasePartition (PartitionName); 314 if (EFI_ERROR (Status)) { 315 SEND_LITERAL ("FAILCheck device console."); 316 DEBUG ((EFI_D_ERROR, "Couldn't erase image: %r\n", Status)); 317 } else { 318 SEND_LITERAL ("OKAY"); 319 } 320 } 321 322 STATIC 323 VOID 324 HandleBoot ( 325 VOID 326 ) 327 { 328 CHAR16 *BootPathStr; 329 330 mTextOut->OutputString (mTextOut, L"Booting downloaded image\r\n"); 331 332 if (mDataBuffer == NULL) { 333 // Doesn't look like we were sent any data 334 SEND_LITERAL ("FAILNo image in memory"); 335 return; 336 } 337 338 // We don't really have any choice but to report success, because once we 339 // boot we lose control of the system. 340 SEND_LITERAL ("OKAY"); 341 342 BootPathStr = (CHAR16 *)PcdGetPtr (PcdAndroidBootDevicePath); 343 AbootimgBootRam (mDataBuffer, mNumDataBytes, BootPathStr, NULL); 344 // We shouldn't get here 345 } 346 347 STATIC 348 VOID 349 HandleOemCommand ( 350 IN CHAR8 *Command 351 ) 352 { 353 EFI_STATUS Status; 354 355 Status = mPlatform->DoOemCommand (Command); 356 if (Status == EFI_NOT_FOUND) { 357 SEND_LITERAL ("FAILOEM Command not recognised."); 358 } else if (Status == EFI_DEVICE_ERROR) { 359 SEND_LITERAL ("FAILError while executing command"); 360 } else if (EFI_ERROR (Status)) { 361 SEND_LITERAL ("FAIL"); 362 } else { 363 SEND_LITERAL ("OKAY"); 364 } 365 } 366 367 STATIC 368 VOID 369 AcceptCmd ( 370 IN UINTN Size, 371 IN CONST CHAR8 *Data 372 ) 373 { 374 EFI_STATUS Status; 375 CHAR8 Command[FASTBOOT_COMMAND_MAX_LENGTH + 1]; 376 377 // Max command size is 64 bytes 378 if (Size > FASTBOOT_COMMAND_MAX_LENGTH) { 379 SEND_LITERAL ("FAILCommand too large"); 380 return; 381 } 382 383 // Commands aren't null-terminated. Let's get a null-terminated version. 384 AsciiStrnCpyS (Command, sizeof Command, Data, Size); 385 386 // Parse command 387 if (MATCH_CMD_LITERAL ("getvar", Command)) { 388 HandleGetVar (Command + sizeof ("getvar")); 389 } else if (MATCH_CMD_LITERAL ("download", Command)) { 390 HandleDownload (Command + sizeof ("download")); 391 } else if (MATCH_CMD_LITERAL ("verify", Command)) { 392 SEND_LITERAL ("FAILNot supported"); 393 } else if (MATCH_CMD_LITERAL ("flash", Command)) { 394 HandleFlash (Command + sizeof ("flash")); 395 } else if (MATCH_CMD_LITERAL ("erase", Command)) { 396 HandleErase (Command + sizeof ("erase")); 397 } else if (MATCH_CMD_LITERAL ("boot", Command)) { 398 HandleBoot (); 399 } else if (MATCH_CMD_LITERAL ("continue", Command)) { 400 SEND_LITERAL ("OKAY"); 401 mTextOut->OutputString (mTextOut, L"Received 'continue' command. Exiting Fastboot mode\r\n"); 402 403 gBS->SignalEvent (mFinishedEvent); 404 } else if (MATCH_CMD_LITERAL ("reboot", Command)) { 405 if (MATCH_CMD_LITERAL ("reboot-bootloader", Command)) { 406 Status = mPlatform->DoOemCommand ("reboot-bootloader"); 407 if (EFI_ERROR (Status)) { 408 SEND_LITERAL ("INFOreboot-bootloader not supported, rebooting normally."); 409 } 410 } 411 SEND_LITERAL ("OKAY"); 412 gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); 413 414 // Shouldn't get here 415 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n")); 416 } else if (MATCH_CMD_LITERAL ("powerdown", Command)) { 417 SEND_LITERAL ("OKAY"); 418 gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); 419 420 // Shouldn't get here 421 DEBUG ((EFI_D_ERROR, "Fastboot: gRT->ResetSystem didn't work\n")); 422 } else if (MATCH_CMD_LITERAL ("oem", Command)) { 423 // The "oem" command isn't in the specification, but it was observed in the 424 // wild, followed by a space, followed by the actual command. 425 HandleOemCommand (Command + sizeof ("oem")); 426 } else if (IS_LOWERCASE_ASCII (Command[0])) { 427 // Commands starting with lowercase ASCII characters are reserved for the 428 // Fastboot protocol. If we don't recognise it, it's probably the future 429 // and there are new commmands in the protocol. 430 // (By the way, the "oem" command mentioned above makes this reservation 431 // redundant, but we handle it here to be spec-compliant) 432 SEND_LITERAL ("FAILCommand not recognised. Check Fastboot version."); 433 } else { 434 HandleOemCommand (Command); 435 } 436 } 437 438 STATIC 439 VOID 440 AcceptData ( 441 IN UINTN Size, 442 IN VOID *Data 443 ) 444 { 445 UINT32 RemainingBytes = mNumDataBytes - mBytesReceivedSoFar; 446 CHAR16 OutputString[FASTBOOT_STRING_MAX_LENGTH]; 447 STATIC UINTN Count = 0; 448 449 // Protocol doesn't say anything about sending extra data so just ignore it. 450 if (Size > RemainingBytes) { 451 Size = RemainingBytes; 452 } 453 454 CopyMem (&mDataBuffer[mBytesReceivedSoFar], Data, Size); 455 456 mBytesReceivedSoFar += Size; 457 458 // Show download progress. Don't do it for every packet as outputting text 459 // might be time consuming - do it on the last packet and on every 32nd packet 460 if ((Count++ % 32) == 0 || Size == RemainingBytes) { 461 // (Note no newline in format string - it will overwrite the line each time) 462 UnicodeSPrint ( 463 OutputString, 464 sizeof (OutputString), 465 L"\r%8d / %8d bytes downloaded (%d%%)", 466 mBytesReceivedSoFar, 467 mNumDataBytes, 468 (mBytesReceivedSoFar * 100) / mNumDataBytes // percentage 469 ); 470 mTextOut->OutputString (mTextOut, OutputString); 471 } 472 473 if (mBytesReceivedSoFar == mNumDataBytes) { 474 // Download finished. 475 476 mTextOut->OutputString (mTextOut, L"\r\n"); 477 SEND_LITERAL ("OKAY"); 478 mState = ExpectCmdState; 479 } 480 } 481 482 /* 483 This is the NotifyFunction passed to CreateEvent in the FastbootAppEntryPoint 484 It will be called by the UEFI event framework when the transport protocol 485 implementation signals that data has been received from the Fastboot host. 486 The parameters are ignored. 487 */ 488 STATIC 489 VOID 490 DataReady ( 491 IN EFI_EVENT Event, 492 IN VOID *Context 493 ) 494 { 495 UINTN Size; 496 VOID *Data; 497 EFI_STATUS Status; 498 499 do { 500 // Indicate lower layer driver that how much bytes are expected. 501 if (mState == ExpectDataState) { 502 Size = mNumDataBytes; 503 } else { 504 Size = 0; 505 } 506 Status = mTransport->Receive (&Size, &Data); 507 if (!EFI_ERROR (Status)) { 508 if (mState == ExpectCmdState) { 509 AcceptCmd (Size, (CHAR8 *) Data); 510 } else if (mState == ExpectDataState) { 511 AcceptData (Size, Data); 512 } else { 513 ASSERT (FALSE); 514 } 515 FreePool (Data); 516 } 517 } while (!EFI_ERROR (Status)); 518 519 // Quit if there was a fatal error 520 if (Status != EFI_NOT_READY) { 521 ASSERT (Status == EFI_DEVICE_ERROR); 522 // (Put a newline at the beginning as we are probably in the data phase, 523 // so the download progress line, with no '\n' is probably on the console) 524 mTextOut->OutputString (mTextOut, L"\r\nFatal error receiving data. Exiting.\r\n"); 525 gBS->SignalEvent (mFinishedEvent); 526 } 527 } 528 529 /* 530 Event notify for a fatal error in transmission. 531 */ 532 STATIC 533 VOID 534 FatalErrorNotify ( 535 IN EFI_EVENT Event, 536 IN VOID *Context 537 ) 538 { 539 mTextOut->OutputString (mTextOut, L"Fatal error sending command response. Exiting.\r\n"); 540 gBS->SignalEvent (mFinishedEvent); 541 } 542 543 EFI_STATUS 544 EFIAPI 545 FastbootAppEntryPoint ( 546 IN EFI_HANDLE ImageHandle, 547 IN EFI_SYSTEM_TABLE *SystemTable 548 ) 549 { 550 EFI_STATUS Status; 551 EFI_EVENT ReceiveEvent; 552 EFI_EVENT WaitEventArray[2]; 553 UINTN EventIndex; 554 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *TextIn; 555 EFI_INPUT_KEY Key; 556 557 mDataBuffer = NULL; 558 559 Status = gBS->LocateProtocol ( 560 &gAndroidFastbootTransportProtocolGuid, 561 NULL, 562 (VOID **) &mTransport 563 ); 564 if (EFI_ERROR (Status)) { 565 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Transport Protocol: %r\n", Status)); 566 return Status; 567 } 568 569 Status = gBS->LocateProtocol (&gAndroidFastbootPlatformProtocolGuid, NULL, (VOID **) &mPlatform); 570 if (EFI_ERROR (Status)) { 571 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Fastboot Platform Protocol: %r\n", Status)); 572 return Status; 573 } 574 575 Status = mPlatform->Init (); 576 if (EFI_ERROR (Status)) { 577 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't initialise Fastboot Platform Protocol: %r\n", Status)); 578 return Status; 579 } 580 581 Status = gBS->LocateProtocol (&gEfiSimpleTextOutProtocolGuid, NULL, (VOID **) &mTextOut); 582 if (EFI_ERROR (Status)) { 583 DEBUG ((EFI_D_ERROR, 584 "Fastboot: Couldn't open Text Output Protocol: %r\n", Status 585 )); 586 return Status; 587 } 588 589 Status = gBS->LocateProtocol (&gEfiSimpleTextInProtocolGuid, NULL, (VOID **) &TextIn); 590 if (EFI_ERROR (Status)) { 591 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't open Text Input Protocol: %r\n", Status)); 592 return Status; 593 } 594 595 // Disable watchdog 596 Status = gBS->SetWatchdogTimer (0, 0x10000, 0, NULL); 597 if (EFI_ERROR (Status)) { 598 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't disable watchdog timer: %r\n", Status)); 599 } 600 601 // Create event for receipt of data from the host 602 Status = gBS->CreateEvent ( 603 EVT_NOTIFY_SIGNAL, 604 TPL_CALLBACK, 605 DataReady, 606 NULL, 607 &ReceiveEvent 608 ); 609 ASSERT_EFI_ERROR (Status); 610 611 // Create event for exiting application when "continue" command is received 612 Status = gBS->CreateEvent (0, TPL_CALLBACK, NULL, NULL, &mFinishedEvent); 613 ASSERT_EFI_ERROR (Status); 614 615 // Create event to pass to FASTBOOT_TRANSPORT_PROTOCOL.Send, signalling a 616 // fatal error 617 Status = gBS->CreateEvent ( 618 EVT_NOTIFY_SIGNAL, 619 TPL_CALLBACK, 620 FatalErrorNotify, 621 NULL, 622 &mFatalSendErrorEvent 623 ); 624 ASSERT_EFI_ERROR (Status); 625 626 627 // Start listening for data 628 Status = mTransport->Start ( 629 ReceiveEvent 630 ); 631 if (EFI_ERROR (Status)) { 632 DEBUG ((EFI_D_ERROR, "Fastboot: Couldn't start transport: %r\n", Status)); 633 return Status; 634 } 635 636 // Talk to the user 637 mTextOut->OutputString (mTextOut, 638 L"Android Fastboot mode - version " ANDROID_FASTBOOT_VERSION ".\r\n"); 639 mTextOut->OutputString (mTextOut, L"Press RETURN or SPACE key to quit.\r\n"); 640 641 // Quit when the user presses any key, or mFinishedEvent is signalled 642 WaitEventArray[0] = mFinishedEvent; 643 WaitEventArray[1] = TextIn->WaitForKey; 644 while (1) { 645 gBS->WaitForEvent (2, WaitEventArray, &EventIndex); 646 if (EventIndex == 0) { 647 break; 648 } 649 Status = TextIn->ReadKeyStroke (gST->ConIn, &Key); 650 if (Key.ScanCode == SCAN_NULL) { 651 if ((Key.UnicodeChar == CHAR_CARRIAGE_RETURN) || (Key.UnicodeChar == ' ')) { 652 break; 653 } 654 } 655 } 656 657 mTransport->Stop (); 658 if (EFI_ERROR (Status)) { 659 DEBUG ((EFI_D_ERROR, "Warning: Fastboot Transport Stop: %r\n", Status)); 660 } 661 mPlatform->UnInit (); 662 663 return EFI_SUCCESS; 664 } 665