]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Drivers/AndroidFastbootTransportTcpDxe/FastbootTransportTcp.c
ARM Packages: Fixed missing braces (the warning was disabled by GCC)
[mirror_edk2.git] / EmbeddedPkg / Drivers / AndroidFastbootTransportTcpDxe / FastbootTransportTcp.c
1 /** @file
2 #
3 # Copyright (c) 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 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 #
12 #
13 #**/
14
15 #include <Protocol/AndroidFastbootTransport.h>
16 #include <Protocol/Dhcp4.h>
17 #include <Protocol/Tcp4.h>
18 #include <Protocol/ServiceBinding.h>
19 #include <Protocol/SimpleTextOut.h>
20
21 #include <Library/BaseLib.h>
22 #include <Library/BaseMemoryLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/PrintLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Library/UefiDriverEntryPoint.h>
28 #include <Library/UefiRuntimeServicesTableLib.h>
29
30 #include <Guid/Hostname.h>
31
32 #define IP4_ADDR_TO_STRING(IpAddr, IpAddrString) UnicodeSPrint ( \
33 IpAddrString, \
34 16 * 2, \
35 L"%d.%d.%d.%d", \
36 IpAddr.Addr[0], \
37 IpAddr.Addr[1], \
38 IpAddr.Addr[2], \
39 IpAddr.Addr[3] \
40 );
41
42 // Fastboot says max packet size is 512, but FASTBOOT_TRANSPORT_PROTOCOL
43 // doesn't place a limit on the size of buffers returned by Receive.
44 // (This isn't actually a packet size - it's just the size of the buffers we
45 // pass to the TCP driver to fill with received data.)
46 // We can achieve much better performance by doing this in larger chunks.
47 #define RX_FRAGMENT_SIZE 2048
48
49 STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mTextOut;
50
51 STATIC EFI_TCP4_PROTOCOL *mTcpConnection;
52 STATIC EFI_TCP4_PROTOCOL *mTcpListener;
53
54 STATIC EFI_EVENT mReceiveEvent;
55
56 STATIC EFI_SERVICE_BINDING_PROTOCOL *mTcpServiceBinding;
57 STATIC EFI_HANDLE mTcpHandle = NULL;
58
59 // We only ever use one IO token for receive and one for transmit. To save
60 // repeatedly allocating and freeing, just allocate statically and re-use.
61 #define NUM_RX_TOKENS 16
62 #define TOKEN_NEXT(Index) (((Index) + 1) % NUM_RX_TOKENS)
63
64 STATIC UINTN mNextSubmitIndex;
65 STATIC UINTN mNextReceiveIndex;
66 STATIC EFI_TCP4_IO_TOKEN mReceiveToken[NUM_RX_TOKENS];
67 STATIC EFI_TCP4_RECEIVE_DATA mRxData[NUM_RX_TOKENS];
68 STATIC EFI_TCP4_IO_TOKEN mTransmitToken;
69 STATIC EFI_TCP4_TRANSMIT_DATA mTxData;
70 // We also reuse the accept token
71 STATIC EFI_TCP4_LISTEN_TOKEN mAcceptToken;
72 // .. and the close token
73 STATIC EFI_TCP4_CLOSE_TOKEN mCloseToken;
74
75 // List type for queued received packets
76 typedef struct _FASTBOOT_TCP_PACKET_LIST {
77 LIST_ENTRY Link;
78 VOID *Buffer;
79 UINTN BufferSize;
80 } FASTBOOT_TCP_PACKET_LIST;
81
82 STATIC LIST_ENTRY mPacketListHead;
83
84 STATIC
85 VOID
86 EFIAPI
87 DataReceived (
88 IN EFI_EVENT Event,
89 IN VOID *Context
90 );
91
92 /*
93 Helper function to set up a receive IO token and call Tcp->Receive
94 */
95 STATIC
96 EFI_STATUS
97 SubmitRecieveToken (
98 VOID
99 )
100 {
101 EFI_STATUS Status;
102 VOID *FragmentBuffer;
103
104 Status = EFI_SUCCESS;
105
106 FragmentBuffer = AllocatePool (RX_FRAGMENT_SIZE);
107 ASSERT (FragmentBuffer != NULL);
108 if (FragmentBuffer == NULL) {
109 DEBUG ((EFI_D_ERROR, "TCP Fastboot out of resources"));
110 return EFI_OUT_OF_RESOURCES;
111 }
112
113 mRxData[mNextSubmitIndex].DataLength = RX_FRAGMENT_SIZE;
114 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentLength = RX_FRAGMENT_SIZE;
115 mRxData[mNextSubmitIndex].FragmentTable[0].FragmentBuffer = FragmentBuffer;
116
117 Status = mTcpConnection->Receive (mTcpConnection, &mReceiveToken[mNextSubmitIndex]);
118 if (EFI_ERROR (Status)) {
119 DEBUG ((EFI_D_ERROR, "TCP Receive: %r\n", Status));
120 FreePool (FragmentBuffer);
121 }
122
123 mNextSubmitIndex = TOKEN_NEXT (mNextSubmitIndex);
124 return Status;
125 }
126
127 /*
128 Event notify function for when we have closed our TCP connection.
129 We can now start listening for another connection.
130 */
131 STATIC
132 VOID
133 ConnectionClosed (
134 IN EFI_EVENT Event,
135 IN VOID *Context
136 )
137 {
138 EFI_STATUS Status;
139
140 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
141 // PCB from the list of live connections. Subsequent attempts to Configure()
142 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
143 // Calling Configure with NULL is a workaround for this issue.
144 Status = mTcpConnection->Configure (mTcpConnection, NULL);
145
146 mTcpConnection = NULL;
147
148 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken);
149 if (EFI_ERROR (Status)) {
150 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status));
151 }
152 }
153
154 STATIC
155 VOID
156 CloseReceiveEvents (
157 VOID
158 )
159 {
160 UINTN Index;
161
162 for (Index = 0; Index < NUM_RX_TOKENS; Index++) {
163 gBS->CloseEvent (mReceiveToken[Index].CompletionToken.Event);
164 }
165 }
166
167 /*
168 Event notify function to be called when we receive TCP data.
169 */
170 STATIC
171 VOID
172 EFIAPI
173 DataReceived (
174 IN EFI_EVENT Event,
175 IN VOID *Context
176 )
177 {
178 EFI_STATUS Status;
179 FASTBOOT_TCP_PACKET_LIST *NewEntry;
180 EFI_TCP4_IO_TOKEN *ReceiveToken;
181
182 ReceiveToken = &mReceiveToken[mNextReceiveIndex];
183
184 Status = ReceiveToken->CompletionToken.Status;
185
186 if (Status == EFI_CONNECTION_FIN) {
187 //
188 // Remote host closed connection. Close our end.
189 //
190
191 CloseReceiveEvents ();
192
193 Status = mTcpConnection->Close (mTcpConnection, &mCloseToken);
194 ASSERT_EFI_ERROR (Status);
195
196 return;
197 }
198
199 //
200 // Add an element to the receive queue
201 //
202
203 NewEntry = AllocatePool (sizeof (FASTBOOT_TCP_PACKET_LIST));
204 if (NewEntry == NULL) {
205 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Out of resources\n"));
206 return;
207 }
208
209 mNextReceiveIndex = TOKEN_NEXT (mNextReceiveIndex);
210
211 if (!EFI_ERROR (Status)) {
212 NewEntry->Buffer
213 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentBuffer;
214 NewEntry->BufferSize
215 = ReceiveToken->Packet.RxData->FragmentTable[0].FragmentLength;
216
217 // Prepare to receive more data
218 SubmitRecieveToken();
219 } else {
220 // Fatal receive error. Put an entry with NULL in the queue, signifying
221 // to return EFI_DEVICE_ERROR from TcpFastbootTransportReceive.
222 NewEntry->Buffer = NULL;
223 NewEntry->BufferSize = 0;
224
225 DEBUG ((EFI_D_ERROR, "\nTCP Fastboot Receive error: %r\n", Status));
226 }
227
228 InsertTailList (&mPacketListHead, &NewEntry->Link);
229
230 Status = gBS->SignalEvent (mReceiveEvent);
231 ASSERT_EFI_ERROR (Status);
232 }
233
234
235 /*
236 Event notify function to be called when we accept an incoming TCP connection.
237 */
238 STATIC
239 VOID
240 EFIAPI
241 ConnectionAccepted (
242 IN EFI_EVENT Event,
243 IN VOID *Context
244 )
245 {
246 EFI_TCP4_LISTEN_TOKEN *AcceptToken;
247 EFI_STATUS Status;
248 UINTN Index;
249
250 AcceptToken = (EFI_TCP4_LISTEN_TOKEN *) Context;
251 Status = AcceptToken->CompletionToken.Status;
252
253 if (EFI_ERROR (Status)) {
254 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Error: %r\n", Status));
255 return;
256 }
257 DEBUG ((EFI_D_ERROR, "TCP Fastboot: Connection Received.\n"));
258
259 //
260 // Accepting a new TCP connection creates a new instance of the TCP protocol.
261 // Open it and prepare to receive on it.
262 //
263
264 Status = gBS->OpenProtocol (
265 AcceptToken->NewChildHandle,
266 &gEfiTcp4ProtocolGuid,
267 (VOID **) &mTcpConnection,
268 gImageHandle,
269 NULL,
270 EFI_OPEN_PROTOCOL_GET_PROTOCOL
271 );
272 if (EFI_ERROR (Status)) {
273 DEBUG ((EFI_D_ERROR, "Open TCP Connection: %r\n", Status));
274 return;
275 }
276
277 mNextSubmitIndex = 0;
278 mNextReceiveIndex = 0;
279
280 for (Index = 0; Index < NUM_RX_TOKENS; Index++) {
281 Status = gBS->CreateEvent (
282 EVT_NOTIFY_SIGNAL,
283 TPL_CALLBACK,
284 DataReceived,
285 NULL,
286 &(mReceiveToken[Index].CompletionToken.Event)
287 );
288 ASSERT_EFI_ERROR (Status);
289 }
290
291 for (Index = 0; Index < NUM_RX_TOKENS; Index++) {
292 SubmitRecieveToken();
293 }
294 }
295
296 /*
297 Set up TCP Fastboot transport: Configure the network device via DHCP then
298 start waiting for a TCP connection on the Fastboot port.
299 */
300 EFI_STATUS
301 TcpFastbootTransportStart (
302 EFI_EVENT ReceiveEvent
303 )
304 {
305 EFI_STATUS Status;
306 EFI_HANDLE NetDeviceHandle;
307 EFI_HANDLE *HandleBuffer;
308 EFI_IP4_MODE_DATA Ip4ModeData;
309 UINTN NumHandles;
310 UINTN HostnameSize = 256;
311 CHAR8 Hostname[256];
312 CHAR16 HostnameUnicode[256] = L"<no hostname>";
313 CHAR16 IpAddrString[16];
314 UINTN Index;
315
316 EFI_TCP4_CONFIG_DATA TcpConfigData = {
317 0x00, // IPv4 Type of Service
318 255, // IPv4 Time to Live
319 { // AccessPoint:
320 TRUE, // Use default address
321 { {0, 0, 0, 0} }, // IP Address (ignored - use default)
322 { {0, 0, 0, 0} }, // Subnet mask (ignored - use default)
323 FixedPcdGet32 (PcdAndroidFastbootTcpPort), // Station port
324 { {0, 0, 0, 0} }, // Remote address: accept any
325 0, // Remote Port: accept any
326 FALSE // ActiveFlag: be a "server"
327 },
328 NULL // Default advanced TCP options
329 };
330
331 mReceiveEvent = ReceiveEvent;
332 InitializeListHead (&mPacketListHead);
333
334 mTextOut->OutputString (mTextOut, L"Initialising TCP Fastboot transport...\r\n");
335
336 //
337 // Open a passive TCP instance
338 //
339
340 Status = gBS->LocateHandleBuffer (
341 ByProtocol,
342 &gEfiTcp4ServiceBindingProtocolGuid,
343 NULL,
344 &NumHandles,
345 &HandleBuffer
346 );
347 if (EFI_ERROR (Status)) {
348 DEBUG ((EFI_D_ERROR, "Find TCP Service Binding: %r\n", Status));
349 return Status;
350 }
351
352 // We just use the first network device
353 NetDeviceHandle = HandleBuffer[0];
354
355 Status = gBS->OpenProtocol (
356 NetDeviceHandle,
357 &gEfiTcp4ServiceBindingProtocolGuid,
358 (VOID **) &mTcpServiceBinding,
359 gImageHandle,
360 NULL,
361 EFI_OPEN_PROTOCOL_GET_PROTOCOL
362 );
363 if (EFI_ERROR (Status)) {
364 DEBUG ((EFI_D_ERROR, "Open TCP Service Binding: %r\n", Status));
365 return Status;
366 }
367
368 Status = mTcpServiceBinding->CreateChild (mTcpServiceBinding, &mTcpHandle);
369 if (EFI_ERROR (Status)) {
370 DEBUG ((EFI_D_ERROR, "TCP ServiceBinding Create: %r\n", Status));
371 return Status;
372 }
373
374 Status = gBS->OpenProtocol (
375 mTcpHandle,
376 &gEfiTcp4ProtocolGuid,
377 (VOID **) &mTcpListener,
378 gImageHandle,
379 NULL,
380 EFI_OPEN_PROTOCOL_GET_PROTOCOL
381 );
382 if (EFI_ERROR (Status)) {
383 DEBUG ((EFI_D_ERROR, "Open TCP Protocol: %r\n", Status));
384 }
385
386 //
387 // Set up re-usable tokens
388 //
389
390 for (Index = 0; Index < NUM_RX_TOKENS; Index++) {
391 mRxData[Index].UrgentFlag = FALSE;
392 mRxData[Index].FragmentCount = 1;
393 mReceiveToken[Index].Packet.RxData = &mRxData[Index];
394 }
395
396 mTxData.Push = TRUE;
397 mTxData.Urgent = FALSE;
398 mTxData.FragmentCount = 1;
399 mTransmitToken.Packet.TxData = &mTxData;
400
401 Status = gBS->CreateEvent (
402 EVT_NOTIFY_SIGNAL,
403 TPL_CALLBACK,
404 ConnectionAccepted,
405 &mAcceptToken,
406 &mAcceptToken.CompletionToken.Event
407 );
408 ASSERT_EFI_ERROR (Status);
409
410 Status = gBS->CreateEvent (
411 EVT_NOTIFY_SIGNAL,
412 TPL_CALLBACK,
413 ConnectionClosed,
414 &mCloseToken,
415 &mCloseToken.CompletionToken.Event
416 );
417 ASSERT_EFI_ERROR (Status);
418
419 //
420 // Configure the TCP instance
421 //
422
423 Status = mTcpListener->Configure (mTcpListener, &TcpConfigData);
424 if (Status == EFI_NO_MAPPING) {
425 // Wait until the IP configuration process (probably DHCP) has finished
426 do {
427 Status = mTcpListener->GetModeData (mTcpListener,
428 NULL, NULL,
429 &Ip4ModeData,
430 NULL, NULL
431 );
432 ASSERT_EFI_ERROR (Status);
433 } while (!Ip4ModeData.IsConfigured);
434 Status = mTcpListener->Configure (mTcpListener, &TcpConfigData);
435 } else if (EFI_ERROR (Status)) {
436 DEBUG ((EFI_D_ERROR, "TCP Configure: %r\n", Status));
437 return Status;
438 }
439
440 //
441 // Tell the user our address and hostname
442 //
443 IP4_ADDR_TO_STRING (Ip4ModeData.ConfigData.StationAddress, IpAddrString);
444
445 // Look up hostname
446 Status = gRT->GetVariable (
447 L"Hostname",
448 &gEfiHostnameVariableGuid,
449 NULL,
450 &HostnameSize,
451 &Hostname
452 );
453 if (!EFI_ERROR (Status) && HostnameSize != 0) {
454 AsciiStrToUnicodeStr (Hostname, HostnameUnicode);
455 }
456
457 // Hostname variable is not null-terminated.
458 Hostname[HostnameSize] = L'\0';
459
460 mTextOut->OutputString (mTextOut, L"TCP Fastboot transport configured.");
461 mTextOut->OutputString (mTextOut, L"\r\nIP address: ");
462 mTextOut->OutputString (mTextOut ,IpAddrString);
463 mTextOut->OutputString (mTextOut, L"\r\n");
464 mTextOut->OutputString (mTextOut, L"\r\nhostname: ");
465 mTextOut->OutputString (mTextOut, HostnameUnicode);
466 mTextOut->OutputString (mTextOut, L"\r\n");
467
468 //
469 // Start listening for a connection
470 //
471
472 Status = mTcpListener->Accept (mTcpListener, &mAcceptToken);
473 if (EFI_ERROR (Status)) {
474 DEBUG ((EFI_D_ERROR, "TCP Accept: %r\n", Status));
475 return Status;
476 }
477
478 mTextOut->OutputString (mTextOut, L"TCP Fastboot transport initialised.\r\n");
479
480 FreePool (HandleBuffer);
481
482 return EFI_SUCCESS;
483 }
484
485 EFI_STATUS
486 TcpFastbootTransportStop (
487 VOID
488 )
489 {
490 EFI_TCP4_CLOSE_TOKEN CloseToken;
491 EFI_STATUS Status;
492 UINTN EventIndex;
493 FASTBOOT_TCP_PACKET_LIST *Entry;
494 FASTBOOT_TCP_PACKET_LIST *NextEntry;
495
496 // Close any existing TCP connection, blocking until it's done.
497 if (mTcpConnection != NULL) {
498 CloseReceiveEvents ();
499
500 CloseToken.AbortOnClose = FALSE;
501
502 Status = gBS->CreateEvent (0, 0, NULL, NULL, &CloseToken.CompletionToken.Event);
503 ASSERT_EFI_ERROR (Status);
504
505 Status = mTcpConnection->Close (mTcpConnection, &CloseToken);
506 ASSERT_EFI_ERROR (Status);
507
508 Status = gBS->WaitForEvent (
509 1,
510 &CloseToken.CompletionToken.Event,
511 &EventIndex
512 );
513 ASSERT_EFI_ERROR (Status);
514
515 ASSERT_EFI_ERROR (CloseToken.CompletionToken.Status);
516
517 // Possible bug in EDK2 TCP4 driver: closing a connection doesn't remove its
518 // PCB from the list of live connections. Subsequent attempts to Configure()
519 // a TCP instance with the same local port will fail with INVALID_PARAMETER.
520 // Calling Configure with NULL is a workaround for this issue.
521 Status = mTcpConnection->Configure (mTcpConnection, NULL);
522 ASSERT_EFI_ERROR (Status);
523 }
524
525
526 gBS->CloseEvent (mAcceptToken.CompletionToken.Event);
527
528 // Stop listening for connections.
529 // Ideally we would do this with Cancel, but it isn't implemented by EDK2.
530 // So we just "reset this TCPv4 instance brutally".
531 Status = mTcpListener->Configure (mTcpListener, NULL);
532 ASSERT_EFI_ERROR (Status);
533
534 Status = mTcpServiceBinding->DestroyChild (mTcpServiceBinding, &mTcpHandle);
535
536 // Free any data the user didn't pick up
537 Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead);
538 while (!IsNull (&mPacketListHead, &Entry->Link)) {
539 NextEntry = (FASTBOOT_TCP_PACKET_LIST *) GetNextNode (&mPacketListHead, &Entry->Link);
540
541 RemoveEntryList (&Entry->Link);
542 if (Entry->Buffer) {
543 FreePool (Entry->Buffer);
544 }
545 FreePool (Entry);
546
547 Entry = NextEntry;
548 }
549
550 return EFI_SUCCESS;
551 }
552
553 /*
554 Event notify function for when data has been sent. Free resources and report
555 errors.
556 Context should point to the transmit IO token passed to
557 TcpConnection->Transmit.
558 */
559 STATIC
560 VOID
561 DataSent (
562 EFI_EVENT Event,
563 VOID *Context
564 )
565 {
566 EFI_STATUS Status;
567
568 Status = mTransmitToken.CompletionToken.Status;
569 if (EFI_ERROR (Status)) {
570 DEBUG ((EFI_D_ERROR, "TCP Fastboot transmit result: %r\n", Status));
571 gBS->SignalEvent (*(EFI_EVENT *) Context);
572 }
573
574 FreePool (mTransmitToken.Packet.TxData->FragmentTable[0].FragmentBuffer);
575 }
576
577 EFI_STATUS
578 TcpFastbootTransportSend (
579 IN UINTN BufferSize,
580 IN CONST VOID *Buffer,
581 IN EFI_EVENT *FatalErrorEvent
582 )
583 {
584 EFI_STATUS Status;
585
586 if (BufferSize > 512) {
587 return EFI_INVALID_PARAMETER;
588 }
589
590 //
591 // Build transmit IO token
592 //
593
594 // Create an event so we are notified when a transmission is complete.
595 // We use this to free resources and report errors.
596 Status = gBS->CreateEvent (
597 EVT_NOTIFY_SIGNAL,
598 TPL_CALLBACK,
599 DataSent,
600 FatalErrorEvent,
601 &mTransmitToken.CompletionToken.Event
602 );
603 ASSERT_EFI_ERROR (Status);
604
605 mTxData.DataLength = BufferSize;
606
607 mTxData.FragmentTable[0].FragmentLength = BufferSize;
608 mTxData.FragmentTable[0].FragmentBuffer = AllocateCopyPool (
609 BufferSize,
610 Buffer
611 );
612
613 Status = mTcpConnection->Transmit (mTcpConnection, &mTransmitToken);
614 if (EFI_ERROR (Status)) {
615 DEBUG ((EFI_D_ERROR, "TCP Transmit: %r\n", Status));
616 return Status;
617 }
618
619 return EFI_SUCCESS;
620 }
621
622
623 EFI_STATUS
624 TcpFastbootTransportReceive (
625 OUT UINTN *BufferSize,
626 OUT VOID **Buffer
627 )
628 {
629 FASTBOOT_TCP_PACKET_LIST *Entry;
630
631 if (IsListEmpty (&mPacketListHead)) {
632 return EFI_NOT_READY;
633 }
634
635 Entry = (FASTBOOT_TCP_PACKET_LIST *) GetFirstNode (&mPacketListHead);
636
637 if (Entry->Buffer == NULL) {
638 // There was an error receiving this packet.
639 return EFI_DEVICE_ERROR;
640 }
641
642 *Buffer = Entry->Buffer;
643 *BufferSize = Entry->BufferSize;
644
645 RemoveEntryList (&Entry->Link);
646 FreePool (Entry);
647
648 return EFI_SUCCESS;
649 }
650
651 FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = {
652 TcpFastbootTransportStart,
653 TcpFastbootTransportStop,
654 TcpFastbootTransportSend,
655 TcpFastbootTransportReceive
656 };
657
658 EFI_STATUS
659 TcpFastbootTransportEntryPoint (
660 IN EFI_HANDLE ImageHandle,
661 IN EFI_SYSTEM_TABLE *SystemTable
662 )
663 {
664 EFI_STATUS Status;
665
666
667 Status = gBS->LocateProtocol(
668 &gEfiSimpleTextOutProtocolGuid,
669 NULL,
670 (VOID **) &mTextOut
671 );
672 if (EFI_ERROR (Status)) {
673 DEBUG ((EFI_D_ERROR, "Fastboot: Open Text Output Protocol: %r\n", Status));
674 return Status;
675 }
676
677 Status = gBS->InstallProtocolInterface (
678 &ImageHandle,
679 &gAndroidFastbootTransportProtocolGuid,
680 EFI_NATIVE_INTERFACE,
681 &mTransportProtocol
682 );
683 if (EFI_ERROR (Status)) {
684 DEBUG ((EFI_D_ERROR, "Fastboot: Install transport Protocol: %r\n", Status));
685 }
686
687 return Status;
688 }