这段时间都在忙着port custom features from LK to edk2,搭了个框后主要耗时在custom fastboot download。

一般我们在fastboot flash xxx_part xxx_part.img时,会先触发一个download cmd完成后再触发一个flash cmd。我们先看下QCOM原生CmdDownload()

/* Handle Download Command */
STATIC VOID
CmdDownload (IN CONST CHAR8 *arg, IN VOID *data, IN UINT32 sz)
{
CHAR8 Response[13] = "DATA";
UINT32 InitStrLen = AsciiStrLen ("DATA");
...
gBS->CopyMem (GetFastbootDeviceData ()->gTxBuffer, Response,
sizeof (Response));
mState = ExpectDataState;
mBytesReceivedSoFar = 0;
GetFastbootDeviceData ()->UsbDeviceProtocol->Send (
ENDPOINT_OUT, sizeof (Response), GetFastbootDeviceData ()->gTxBuffer);
DEBUG ((EFI_D_VERBOSE, "CmdDownload: Send 12 %a\n",
GetFastbootDeviceData ()->gTxBuffer));
}

再看下LK的:

static void cmd_download(const char *arg, void *data, unsigned sz)
{
STACKBUF_DMA_ALIGN(response, MAX_RSP_SIZE);
unsigned len = hex2unsigned(arg);
int r;

download_size = 0;
if (len > download_max) {
fastboot_fail("data too large");
return;
}

snprintf((char *)response, MAX_RSP_SIZE, "DATA%08x", len);
if (usb_if.usb_write(response, strlen((const char *)response)) < 0)
return;
/*
* Discard the cache contents before starting the download
*/
arch_invalidate_cache_range((addr_t) download_base, ROUNDUP(len, CACHE_LINE));

r = usb_if.usb_read(download_base, len);
if ((r < 0) || ((unsigned) r != len)) {
fastboot_state = STATE_ERROR;
return;
}
download_size = len;
fastboot_okay("");
}

ok, LK是先usb_write了一个response,然后usb_read数据到download_base。而EDK2只有一个USB Tx操作(就是发送了一个response到host),另一个跑哪了?

我们在AcceptData能看到这个usb_read:

STATIC VOID
AcceptData (IN UINT64 Size, IN VOID *Data)
{
...
/* Postpone Fastboot Okay until flash completed */
FastbootOkayDelay ();
mState = ExpectCmdState;
} else {
GetFastbootDeviceData ()->UsbDeviceProtocol->Send (
ENDPOINT_IN, GetXfrSize (), (Data + mBytesReceivedSoFar));
DEBUG ((EFI_D_VERBOSE, "AcceptData: Send %d\n", GetXfrSize ()));
}
}

rt, 就是这个Send(ENDPOINT_IN ...)。那谁trigger了这个Accept? 往前看:

VOID
DataReady (IN UINT64 Size, IN VOID *Data)
{
DEBUG ((EFI_D_VERBOSE, "DataReady %d\n", Size));
if (mState == ExpectCmdState)
AcceptCmd (Size, (CHAR8 *)Data);
else if (mState == ExpectDataState)
AcceptData (Size, Data);
else {
DEBUG ((EFI_D_ERROR, "DataReady Unknown status received\r\n"));
return;
}
}
/* Process bulk transfer out come for Rx */
STATIC EFI_STATUS
ProcessBulkXfrCompleteRx (IN USB_DEVICE_TRANSFER_OUTCOME *Uto)
{
EFI_STATUS Status = EFI_SUCCESS;

// switch on the transfer status
switch (Uto->Status) {
case UsbDeviceTransferStatusCompleteOK:
if (FastbootCurrentState () == ExpectDataState)
DataReady (Uto->BytesCompleted, FastbootDloadBuffer ());
else
DataReady (Uto->BytesCompleted, Fbd.gRxBuffer);
break;

注意这里有个usb_read。为什么会放这里? Think about the last USB operation.

EFI_STATUS HandleUsbEvents (VOID)
{
...
if (USB_ENDPOINT_DIRECTION_OUT ==
USB_INDEX_TO_EPDIR (Payload.TransferOutcome.EndpointIndex)) {

Status = ProcessBulkXfrCompleteRx (&Payload.TransferOutcome);
if (EFI_ERROR (Status)) {
/* Should not happen, even if it happens we keep waiting for USB to be
* connected */
DEBUG ((EFI_D_ERROR,
"Error, should not happen! Check your USB connection"));
}
} else {
/* Else the direction is from device to host, process TX */
Status = ProcessBulkXfrCompleteTx (&Payload.TransferOutcome);

HandleUsbEvents()会处理Rx/Tx完成event,记住是完成时才handle。

/* Wait for USB events in tight loop */
while (1) {
Status = HandleUsbEvents ();
if (EFI_ERROR (Status) && (Status != EFI_ABORTED)) {
DEBUG ((EFI_D_ERROR, "Error, failed to handle USB event\n"));
break;
}

if (FastbootFatal ()) {
DEBUG ((EFI_D_ERROR, "Continue detected, Exiting App...\n"));
break;
}
}

ok, just loop USB events。可见USB rx不是一次性读完,而是分块逐步完成。

这个read完成后host会发个flash cmd,进入CmdFlash()

CmdFlash()区分sparse格式和non-sparse,Android系统镜像过大就引入了sparse格式:

DEBUG ((EFI_D_VERBOSE, "=== Sparse Image Header ===\n"));
DEBUG ((EFI_D_VERBOSE, "magic: 0x%x\n", sparse_header->magic));
DEBUG (
(EFI_D_VERBOSE, "major_version: 0x%x\n", sparse_header->major_version));
DEBUG (
(EFI_D_VERBOSE, "minor_version: 0x%x\n", sparse_header->minor_version));
DEBUG ((EFI_D_VERBOSE, "file_hdr_sz: %d\n", sparse_header->file_hdr_sz));
DEBUG ((EFI_D_VERBOSE, "chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz));
DEBUG ((EFI_D_VERBOSE, "blk_sz: %d\n", sparse_header->blk_sz));
DEBUG ((EFI_D_VERBOSE, "total_blks: %d\n", sparse_header->total_blks));
DEBUG ((EFI_D_VERBOSE, "total_chunks: %d\n", sparse_header->total_chunks));

上面就是它的header,基本就是按chunk来处理:

/* Start processing the chunks */
for (SparseImgData.Chunk = 0;
SparseImgData.Chunk < sparse_header->total_chunks;
SparseImgData.Chunk++) {

提下CmdFlash()开头的几个buff:

/* Handle Flash Command */
STATIC VOID
CmdFlash (IN CONST CHAR8 *arg, IN VOID *data, IN UINT32 sz)
{
...
ExchangeFlashAndUsbDataBuf ();
...
}

STATIC VOID ExchangeFlashAndUsbDataBuf (VOID)
{
VOID *mTmpbuff;

mTmpbuff = mUsbDataBuffer;
mUsbDataBuffer = mFlashDataBuffer;
mFlashDataBuffer = mTmpbuff;
mFlashNumDataBytes = mNumDataBytes;
}

mUsbDataBuffer是host传过来的data by usb intf,RAM地址是这个。mNumDataBytes就是这个data size。

btw: edk2 fastboot支持并行下载了for sparse img:] 遥想在S corp.时,还搞过这个feature by multi-core…