summaryrefslogtreecommitdiffstats
path: root/EmbeddedPkg/Drivers/AndroidFastbootTransportUsbDxe/FastbootTransportUsb.c
blob: fe934cce590c19797502361124db8728ac3ce383 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/** @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;
}