/** @file | |
Copyright (c) 2014, ARM Ltd. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
/* | |
* Implementation of the FASTBOOT_TRANSPORT_PROTOCOL using the USB_DEVICE_PROTOCOL | |
*/ | |
#include <Protocol/UsbDevice.h> | |
#include <Protocol/AndroidFastbootTransport.h> | |
#include <Protocol/SimpleTextOut.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiDriverEntryPoint.h> | |
STATIC USB_DEVICE_PROTOCOL *mUsbDevice; | |
// Configuration attributes: | |
// bit 7 reserved and must be 1, bit 6 means self-powered. | |
#define CONFIG_DESC_ATTRIBUTES (BIT7 | BIT6) | |
#define MAX_PACKET_SIZE_BULK 512 | |
STATIC USB_DEVICE_PROTOCOL *mUsbDevice; | |
STATIC EFI_EVENT mReceiveEvent = NULL; | |
STATIC LIST_ENTRY mPacketList; | |
// List type for queued received packets | |
typedef struct _FASTBOOT_USB_PACKET_LIST { | |
LIST_ENTRY Link; | |
VOID *Buffer; | |
UINTN BufferSize; | |
} FASTBOOT_USB_PACKET_LIST; | |
/* | |
No string descriptors - all string descriptor members are set to 0 | |
*/ | |
STATIC USB_DEVICE_DESCRIPTOR mDeviceDescriptor = { | |
sizeof (USB_DEVICE_DESCRIPTOR), // Length | |
USB_DESC_TYPE_DEVICE, // DescriptorType | |
0x0200, // BcdUSB | |
0xFF, // DeviceClass | |
0, // DeviceSubClass | |
0, // DeviceProtocol | |
64, // MaxPacketSize0 | |
FixedPcdGet32 (PcdAndroidFastbootUsbVendorId), // IdVendor | |
FixedPcdGet32 (PcdAndroidFastbootUsbProductId), // IdProduct | |
0, // BcdDevice | |
0, // StrManufacturer | |
0, // StrProduct | |
0, // StrSerialNumber | |
1 // NumConfigurations | |
}; | |
/* | |
We have one configuration, one interface, and two endpoints (one IN, one OUT) | |
*/ | |
// Lazy (compile-time) way to concatenate descriptors to pass to the USB device | |
// protocol | |
#pragma pack(1) | |
typedef struct { | |
USB_CONFIG_DESCRIPTOR ConfigDescriptor; | |
USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; | |
USB_ENDPOINT_DESCRIPTOR EndpointDescriptor1; | |
USB_ENDPOINT_DESCRIPTOR EndpointDescriptor2; | |
} GET_CONFIG_DESCRIPTOR_RESPONSE; | |
#pragma pack() | |
STATIC GET_CONFIG_DESCRIPTOR_RESPONSE mGetConfigDescriptorResponse = { | |
{ // USB_CONFIG_DESCRIPTOR | |
sizeof (USB_CONFIG_DESCRIPTOR), // Length; | |
USB_DESC_TYPE_CONFIG, // DescriptorType; | |
sizeof (GET_CONFIG_DESCRIPTOR_RESPONSE), // TotalLength; | |
1, // NumInterfaces; | |
1, // ConfigurationValue; | |
0, // Configuration; | |
CONFIG_DESC_ATTRIBUTES, // Attributes; | |
0 // MaxPower; | |
}, | |
{ // USB_INTERFACE_DESCRIPTOR | |
sizeof (USB_INTERFACE_DESCRIPTOR), // Length; | |
USB_DESC_TYPE_INTERFACE, // DescriptorType; | |
0, // InterfaceNumber; | |
0, // AlternateSetting; | |
2, // NumEndpoints; | |
0xFF, // InterfaceClass; | |
// Vendor specific interface subclass and protocol codes. | |
// I found these values in the Fastboot code | |
// (in match_fastboot_with_serial in fastboot.c). | |
0x42, // InterfaceSubClass; | |
0x03, // InterfaceProtocol; | |
0 // Interface; | |
}, | |
{ // USB_ENDPOINT_DESCRIPTOR (In Endpoint) | |
sizeof (USB_ENDPOINT_DESCRIPTOR), // Length; | |
USB_DESC_TYPE_ENDPOINT, // DescriptorType; | |
1 | BIT7, // EndpointAddress; | |
0x2, // Attributes; | |
MAX_PACKET_SIZE_BULK, // MaxPacketSize; | |
16 // Interval; | |
}, | |
{ // STATIC USB_ENDPOINT_DESCRIPTOR (Out Endpoint) | |
sizeof (USB_ENDPOINT_DESCRIPTOR), // Length; | |
USB_DESC_TYPE_ENDPOINT, // DescriptorType; | |
1, // EndpointAddress; | |
0x2, // Attributes; | |
MAX_PACKET_SIZE_BULK, // MaxPacketSize; | |
16 // Interval; | |
} | |
}; | |
STATIC | |
VOID | |
DataReceived ( | |
IN UINTN Size, | |
IN VOID *Buffer | |
) | |
{ | |
FASTBOOT_USB_PACKET_LIST *NewEntry; | |
NewEntry = AllocatePool (sizeof (*NewEntry)); | |
ASSERT (NewEntry != NULL); | |
NewEntry->Buffer = Buffer; | |
NewEntry->BufferSize = Size; | |
InsertTailList (&mPacketList, &NewEntry->Link); | |
if (mReceiveEvent) { | |
gBS->SignalEvent (mReceiveEvent); | |
} | |
} | |
STATIC | |
VOID | |
DataSent ( | |
IN UINT8 EndpointIndex | |
) | |
{ | |
// Don't care. | |
} | |
/* | |
Set up the transport system for use by Fastboot. | |
e.g. For USB this probably means making the device enumerable. | |
*/ | |
EFI_STATUS | |
FastbootTransportUsbStart ( | |
EFI_EVENT ReceiveEvent | |
) | |
{ | |
GET_CONFIG_DESCRIPTOR_RESPONSE *Responses; | |
mReceiveEvent = ReceiveEvent; | |
mGetConfigDescriptorResponse.ConfigDescriptor.TotalLength = sizeof (GET_CONFIG_DESCRIPTOR_RESPONSE); | |
Responses = &mGetConfigDescriptorResponse; | |
InitializeListHead (&mPacketList); | |
return mUsbDevice->Start (&mDeviceDescriptor, (VOID **)&Responses, DataReceived, DataSent); | |
} | |
/* | |
Function to be called when all Fastboot transactions are finished, to | |
de-initialise the transport system. | |
e.g. A USB OTG system might want to get out of peripheral mode so it can be | |
a USB host. | |
*/ | |
EFI_STATUS | |
FastbootTransportUsbStop ( | |
VOID | |
) | |
{ | |
// not yet implemented in USB | |
return EFI_SUCCESS; | |
} | |
/* | |
Send data. This function can be used both for command responses like "OKAY" | |
and for the data phase (the protocol doesn't describe any situation when the | |
latter might be necessary, but does allow it) | |
*/ | |
EFI_STATUS | |
FastbootTransportUsbSend ( | |
IN UINTN BufferSize, | |
IN CONST VOID *Buffer, | |
IN EFI_EVENT *FatalErrorEvent | |
) | |
{ | |
// Current USB protocol is blocking, so ignore FatalErrorEvent | |
return mUsbDevice->Send (1, BufferSize, Buffer); | |
} | |
/* | |
When the event has been Signalled to say data is available from the host, | |
this function is used to get data. In order to handle the case where several | |
packets are received before ReceiveEvent's notify function is called, packets | |
received are queued, and each call to this function returns the next packet in | |
the queue. It should therefore be called in a loop, the exit condition being a | |
return of EFI_NOT_READY. | |
Parameters: | |
Buffer - The buffer in which to place data | |
BufferSize - The size of Buffer in bytes | |
Return EFI_NOT_READY if there is no data available | |
*/ | |
EFI_STATUS | |
FastbootTransportUsbReceive ( | |
OUT UINTN *BufferSize, | |
OUT VOID **Buffer | |
) | |
{ | |
FASTBOOT_USB_PACKET_LIST *Entry; | |
if (IsListEmpty (&mPacketList)) { | |
return EFI_NOT_READY; | |
} | |
Entry = (FASTBOOT_USB_PACKET_LIST *)GetFirstNode (&mPacketList); | |
*BufferSize = Entry->BufferSize; | |
*Buffer = Entry->Buffer; | |
RemoveEntryList (&Entry->Link); | |
FreePool (Entry); | |
return EFI_SUCCESS; | |
} | |
STATIC FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { | |
FastbootTransportUsbStart, | |
FastbootTransportUsbStop, | |
FastbootTransportUsbSend, | |
FastbootTransportUsbReceive | |
}; | |
EFI_STATUS | |
FastbootTransportUsbEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// Assume there's only one USB peripheral controller. | |
Status = gBS->LocateProtocol (&gUsbDeviceProtocolGuid, NULL, (VOID **)&mUsbDevice); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = gBS->InstallProtocolInterface ( | |
&ImageHandle, | |
&gAndroidFastbootTransportProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mTransportProtocol | |
); | |
return Status; | |
} |