这段时间都在忙着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...