/** @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; } EFI_STATUS FastbootTransportUsbRequestReceive ( IN UINTN BufferSize ) { DEBUG ((DEBUG_ERROR, "#%a, %d, BufferSize:%d\n", __func__, __LINE__, BufferSize)); if (mUsbDevice->Request) { return mUsbDevice->Request (BufferSize); } return EFI_SUCCESS; } STATIC FASTBOOT_TRANSPORT_PROTOCOL mTransportProtocol = { FastbootTransportUsbStart, FastbootTransportUsbStop, FastbootTransportUsbSend, FastbootTransportUsbReceive, FastbootTransportUsbRequestReceive }; 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; }