/** @file
Copyright (c) 2014, ARM Ltd. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
/*
* Implementation of the FASTBOOT_TRANSPORT_PROTOCOL using the USB_DEVICE_PROTOCOL
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}