]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
MdeModulePkg/SdMmc: Add EDKII SD/MMC stack
[mirror_edk2.git] / MdeModulePkg / Bus / Sd / SdDxe / SdBlockIo.c
diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c b/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
new file mode 100644 (file)
index 0000000..341d788
--- /dev/null
@@ -0,0 +1,971 @@
+/** @file\r
+  The helper functions for BlockIo and BlockIo2 protocol.\r
+\r
+  Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "SdDxe.h"\r
+\r
+/**\r
+  Nonblocking I/O callback funtion when the event is signaled.\r
+\r
+  @param[in]  Event     The Event this notify function registered to.\r
+  @param[in]  Context   Pointer to the context data registered to the\r
+                        Event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+AsyncIoCallback (\r
+  IN EFI_EVENT                Event,\r
+  IN VOID                     *Context\r
+  )\r
+{\r
+  SD_REQUEST                  *Request;\r
+\r
+  gBS->CloseEvent (Event);\r
+\r
+  Request = (SD_REQUEST *) Context;\r
+\r
+  DEBUG_CODE_BEGIN ();\r
+    DEBUG ((EFI_D_INFO, "Sd Async Request: CmdIndex[%d] Arg[%08x] %r\n",\r
+            Request->SdMmcCmdBlk.CommandIndex, Request->SdMmcCmdBlk.CommandArgument,\r
+            Request->Packet.TransactionStatus));\r
+  DEBUG_CODE_END ();\r
+\r
+  if (EFI_ERROR (Request->Packet.TransactionStatus)) {\r
+    Request->Token->TransactionStatus = Request->Packet.TransactionStatus;\r
+  }\r
+\r
+  RemoveEntryList (&Request->Link);\r
+\r
+  if (Request->IsEnd) {\r
+    gBS->SignalEvent (Request->Token->Event);\r
+  }\r
+\r
+  FreePool (Request);\r
+}\r
+\r
+/**\r
+  Send command SET_RELATIVE_ADDRESS to the device to set the device address.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[out] Rca               The relative device address to assign.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdSetRca (\r
+  IN     SD_DEVICE              *Device,\r
+     OUT UINT16                 *Rca\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EFI_SD_MMC_COMMAND_BLOCK             SdMmcCmdBlk;\r
+  EFI_SD_MMC_STATUS_BLOCK              SdMmcStatusBlk;\r
+  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET  Packet;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));\r
+  ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));\r
+  ZeroMem (&Packet, sizeof (Packet));\r
+  Packet.SdMmcCmdBlk    = &SdMmcCmdBlk;\r
+  Packet.SdMmcStatusBlk = &SdMmcStatusBlk;\r
+  Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR;\r
+  SdMmcCmdBlk.CommandType  = SdMmcCommandTypeBcr;\r
+  SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6;\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);\r
+  if (!EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_INFO, "Set RCA succeeds with Resp0 = 0x%x\n", SdMmcStatusBlk.Resp0));\r
+    *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send command SELECT to the device to select/deselect the device.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Rca               The relative device address to use.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdSelect (\r
+  IN     SD_DEVICE              *Device,\r
+  IN     UINT16                 Rca\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EFI_SD_MMC_COMMAND_BLOCK             SdMmcCmdBlk;\r
+  EFI_SD_MMC_STATUS_BLOCK              SdMmcStatusBlk;\r
+  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET  Packet;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));\r
+  ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));\r
+  ZeroMem (&Packet, sizeof (Packet));\r
+  Packet.SdMmcCmdBlk    = &SdMmcCmdBlk;\r
+  Packet.SdMmcStatusBlk = &SdMmcStatusBlk;\r
+  Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD;\r
+  SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;\r
+  SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send command SEND_STATUS to the device to get device status.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Rca               The relative device address to use.\r
+  @param[out] DevStatus         The buffer to store the device status.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdSendStatus (\r
+  IN     SD_DEVICE              *Device,\r
+  IN     UINT16                 Rca,\r
+     OUT UINT32                 *DevStatus\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EFI_SD_MMC_COMMAND_BLOCK             SdMmcCmdBlk;\r
+  EFI_SD_MMC_STATUS_BLOCK              SdMmcStatusBlk;\r
+  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET  Packet;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));\r
+  ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));\r
+  ZeroMem (&Packet, sizeof (Packet));\r
+  Packet.SdMmcCmdBlk    = &SdMmcCmdBlk;\r
+  Packet.SdMmcStatusBlk = &SdMmcStatusBlk;\r
+  Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS;\r
+  SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+  SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);\r
+  if (!EFI_ERROR (Status)) {\r
+    CopyMem (DevStatus, &SdMmcStatusBlk.Resp0, sizeof (UINT32));\r
+  }\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send command SEND_CSD to the device to get the CSD register data.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Rca               The relative device address to use.\r
+  @param[out] Csd               The buffer to store the SD_CSD register data.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdGetCsd (\r
+  IN     SD_DEVICE              *Device,\r
+  IN     UINT16                 Rca,\r
+     OUT SD_CSD                 *Csd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EFI_SD_MMC_COMMAND_BLOCK             SdMmcCmdBlk;\r
+  EFI_SD_MMC_STATUS_BLOCK              SdMmcStatusBlk;\r
+  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET  Packet;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));\r
+  ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));\r
+  ZeroMem (&Packet, sizeof (Packet));\r
+  ZeroMem (Csd, sizeof (SD_CSD));\r
+\r
+  Packet.SdMmcCmdBlk    = &SdMmcCmdBlk;\r
+  Packet.SdMmcStatusBlk = &SdMmcStatusBlk;\r
+  Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  SdMmcCmdBlk.CommandIndex = SD_SEND_CSD;\r
+  SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;\r
+  SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.\r
+    //\r
+    CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Send command SEND_CID to the device to get the CID register data.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Rca               The relative device address to use.\r
+  @param[out] Cid               The buffer to store the SD_CID register data.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdGetCid (\r
+  IN     SD_DEVICE              *Device,\r
+  IN     UINT16                 Rca,\r
+     OUT SD_CID                 *Cid\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EFI_SD_MMC_COMMAND_BLOCK             SdMmcCmdBlk;\r
+  EFI_SD_MMC_STATUS_BLOCK              SdMmcStatusBlk;\r
+  EFI_SD_MMC_PASS_THRU_COMMAND_PACKET  Packet;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));\r
+  ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));\r
+  ZeroMem (&Packet, sizeof (Packet));\r
+  ZeroMem (Cid, sizeof (SD_CID));\r
+\r
+  Packet.SdMmcCmdBlk    = &SdMmcCmdBlk;\r
+  Packet.SdMmcStatusBlk = &SdMmcStatusBlk;\r
+  Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  SdMmcCmdBlk.CommandIndex = SD_SEND_CID;\r
+  SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;\r
+  SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.\r
+    //\r
+    CopyMem (((UINT8*)Cid) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CID) - 1);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read/write single block through sync or async I/O request.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Lba               The starting logical block address to be read/written.\r
+                                The caller is responsible for reading/writing to only\r
+                                legitimate locations.\r
+  @param[in]  Buffer            A pointer to the destination/source buffer for the data.\r
+  @param[in]  BufferSize        Size of Buffer, must be a multiple of device block size.\r
+  @param[in]  IsRead            Indicates it is a read or write operation.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdRwSingleBlock (\r
+  IN  SD_DEVICE                 *Device,\r
+  IN  EFI_LBA                   Lba,\r
+  IN  VOID                      *Buffer,\r
+  IN  UINTN                     BufferSize,\r
+  IN  BOOLEAN                   IsRead,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  SD_REQUEST                           *RwSingleBlkReq;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  RwSingleBlkReq = NULL;\r
+  PassThru       = Device->Private->PassThru;\r
+\r
+  RwSingleBlkReq = AllocateZeroPool (sizeof (SD_REQUEST));\r
+  if (RwSingleBlkReq == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  RwSingleBlkReq->Signature = SD_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Device->Queue, &RwSingleBlkReq->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  RwSingleBlkReq->Packet.SdMmcCmdBlk    = &RwSingleBlkReq->SdMmcCmdBlk;\r
+  RwSingleBlkReq->Packet.SdMmcStatusBlk = &RwSingleBlkReq->SdMmcStatusBlk;\r
+  //\r
+  // Calculate timeout value through the below formula.\r
+  // Timeout = (transfer size) / (2MB/s).\r
+  // Taking 2MB/s as divisor as it's the lowest transfer speed\r
+  // above class 2.\r
+  // Refer to SD Physical Layer Simplified spec section 3.4 for details.\r
+  //\r
+  RwSingleBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;\r
+\r
+  if (IsRead) {\r
+    RwSingleBlkReq->Packet.InDataBuffer     = Buffer;\r
+    RwSingleBlkReq->Packet.InTransferLength = (UINT32)BufferSize;\r
+\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_SINGLE_BLOCK;\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAdtc;\r
+    RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+  } else {\r
+    RwSingleBlkReq->Packet.OutDataBuffer     = Buffer;\r
+    RwSingleBlkReq->Packet.OutTransferLength = (UINT32)BufferSize;\r
+\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_SINGLE_BLOCK;\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAdtc;\r
+    RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+  }\r
+\r
+  if (Device->SectorAddressing) {\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba;\r
+  } else {\r
+    RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize);\r
+  }\r
+\r
+  RwSingleBlkReq->IsEnd = IsEnd;\r
+  RwSingleBlkReq->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    RwSingleBlkReq,\r
+                    &RwSingleBlkReq->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    RwSingleBlkReq->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &RwSingleBlkReq->Packet, RwSingleBlkReq->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (RwSingleBlkReq != NULL)) {\r
+      RemoveEntryList (&RwSingleBlkReq->Link);\r
+      if (RwSingleBlkReq->Event != NULL) {\r
+        gBS->CloseEvent (RwSingleBlkReq->Event);\r
+      }\r
+      FreePool (RwSingleBlkReq);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (RwSingleBlkReq != NULL) {\r
+      RemoveEntryList (&RwSingleBlkReq->Link);\r
+      FreePool (RwSingleBlkReq);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read/write multiple blocks through sync or async I/O request.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Lba               The starting logical block address to be read/written.\r
+                                The caller is responsible for reading/writing to only\r
+                                legitimate locations.\r
+  @param[in]  Buffer            A pointer to the destination/source buffer for the data.\r
+  @param[in]  BufferSize        Size of Buffer, must be a multiple of device block size.\r
+  @param[in]  IsRead            Indicates it is a read or write operation.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdRwMultiBlocks (\r
+  IN  SD_DEVICE                 *Device,\r
+  IN  EFI_LBA                   Lba,\r
+  IN  VOID                      *Buffer,\r
+  IN  UINTN                     BufferSize,\r
+  IN  BOOLEAN                   IsRead,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  SD_REQUEST                    *RwMultiBlkReq;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  RwMultiBlkReq = NULL;\r
+\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  RwMultiBlkReq = AllocateZeroPool (sizeof (SD_REQUEST));\r
+  if (RwMultiBlkReq == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  RwMultiBlkReq->Signature = SD_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Device->Queue, &RwMultiBlkReq->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  RwMultiBlkReq->Packet.SdMmcCmdBlk    = &RwMultiBlkReq->SdMmcCmdBlk;\r
+  RwMultiBlkReq->Packet.SdMmcStatusBlk = &RwMultiBlkReq->SdMmcStatusBlk;\r
+  //\r
+  // Calculate timeout value through the below formula.\r
+  // Timeout = (transfer size) / (2MB/s).\r
+  // Taking 2MB/s as divisor as it's the lowest transfer speed\r
+  // above class 2.\r
+  // Refer to SD Physical Layer Simplified spec section 3.4 for details.\r
+  //\r
+  RwMultiBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;\r
+\r
+  if (IsRead) {\r
+    RwMultiBlkReq->Packet.InDataBuffer     = Buffer;\r
+    RwMultiBlkReq->Packet.InTransferLength = (UINT32)BufferSize;\r
+\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_MULTIPLE_BLOCK;\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAdtc;\r
+    RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+  } else {\r
+    RwMultiBlkReq->Packet.OutDataBuffer     = Buffer;\r
+    RwMultiBlkReq->Packet.OutTransferLength = (UINT32)BufferSize;\r
+\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_MULTIPLE_BLOCK;\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAdtc;\r
+    RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+  }\r
+\r
+  if (Device->SectorAddressing) {\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba;\r
+  } else {\r
+    RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize);\r
+  }\r
+\r
+  RwMultiBlkReq->IsEnd = IsEnd;\r
+  RwMultiBlkReq->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    RwMultiBlkReq,\r
+                    &RwMultiBlkReq->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    RwMultiBlkReq->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &RwMultiBlkReq->Packet, RwMultiBlkReq->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (RwMultiBlkReq != NULL)) {\r
+      RemoveEntryList (&RwMultiBlkReq->Link);\r
+      if (RwMultiBlkReq->Event != NULL) {\r
+        gBS->CloseEvent (RwMultiBlkReq->Event);\r
+      }\r
+      FreePool (RwMultiBlkReq);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (RwMultiBlkReq != NULL) {\r
+      RemoveEntryList (&RwMultiBlkReq->Link);\r
+      FreePool (RwMultiBlkReq);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  This function transfers data from/to the sd memory card device.\r
+\r
+  @param[in]       Device       A pointer to the SD_DEVICE instance.\r
+  @param[in]       MediaId      The media ID that the read/write request is for.\r
+  @param[in]       Lba          The starting logical block address to be read/written.\r
+                                The caller is responsible for reading/writing to only\r
+                                legitimate locations.\r
+  @param[in, out]  Buffer       A pointer to the destination/source buffer for the data.\r
+  @param[in]       BufferSize   Size of Buffer, must be a multiple of device block size.\r
+  @param[in]       IsRead       Indicates it is a read or write operation.\r
+  @param[in, out]  Token        A pointer to the token associated with the transaction.\r
+\r
+  @retval EFI_SUCCESS           The data was read/written correctly to the device.\r
+  @retval EFI_WRITE_PROTECTED   The device can not be read/written to.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the read/write.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.\r
+  @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The read/write request contains LBAs that are not valid,\r
+                                or the buffer is not on proper alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+SdReadWrite (\r
+  IN     SD_DEVICE                      *Device,\r
+  IN     UINT32                         MediaId,\r
+  IN     EFI_LBA                        Lba,\r
+  IN OUT VOID                           *Buffer,\r
+  IN     UINTN                          BufferSize,\r
+  IN     BOOLEAN                        IsRead,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN            *Token\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_BLOCK_IO_MEDIA                    *Media;\r
+  UINTN                                 BlockSize;\r
+  UINTN                                 BlockNum;\r
+  UINTN                                 IoAlign;\r
+  UINTN                                 Remaining;\r
+  UINT32                                MaxBlock;\r
+  BOOLEAN                               LastRw;\r
+\r
+  Status = EFI_SUCCESS;\r
+  Media  = &Device->BlockMedia;\r
+  LastRw = FALSE;\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  if (!IsRead && Media->ReadOnly) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BufferSize == 0) {\r
+    if ((Token != NULL) && (Token->Event != NULL)) {\r
+      Token->TransactionStatus = EFI_SUCCESS;\r
+      gBS->SignalEvent (Token->Event);\r
+    }\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  BlockSize = Media->BlockSize;\r
+  if ((BufferSize % BlockSize) != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  BlockNum  = BufferSize / BlockSize;\r
+  if ((Lba + BlockNum - 1) > Media->LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IoAlign = Media->IoAlign;\r
+  if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Start to execute data transfer. The max block number in single cmd is 65535 blocks.\r
+  //\r
+  Remaining = BlockNum;\r
+  MaxBlock  = 0xFFFF;\r
+\r
+  while (Remaining > 0) {\r
+    if (Remaining <= MaxBlock) {\r
+      BlockNum = Remaining;\r
+      LastRw   = TRUE;\r
+    } else {\r
+      BlockNum = MaxBlock;\r
+    }\r
+\r
+    BufferSize = BlockNum * BlockSize;\r
+    if (BlockNum == 1) {\r
+      Status = SdRwSingleBlock (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw);\r
+    } else {\r
+      Status = SdRwMultiBlocks (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    DEBUG ((EFI_D_INFO, "Sd%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", IsRead ? "Read" : "Write", Lba, BlockNum, Token->Event, Status));\r
+\r
+    Lba   += BlockNum;\r
+    Buffer = (UINT8*)Buffer + BufferSize;\r
+    Remaining -= BlockNum;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Reset the Block Device.\r
+\r
+  @param  This                 Indicates a pointer to the calling context.\r
+  @param  ExtendedVerification Driver may perform diagnostics on reset.\r
+\r
+  @retval EFI_SUCCESS          The device was reset.\r
+  @retval EFI_DEVICE_ERROR     The device is not functioning properly and could\r
+                               not be reset.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdReset (\r
+  IN  EFI_BLOCK_IO_PROTOCOL     *This,\r
+  IN  BOOLEAN                   ExtendedVerification\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  SD_DEVICE                     *Device;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO (This);\r
+\r
+  PassThru = Device->Private->PassThru;\r
+  Status   = PassThru->ResetDevice (PassThru, Device->Slot);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Read BufferSize bytes from Lba into Buffer.\r
+\r
+  @param  This       Indicates a pointer to the calling context.\r
+  @param  MediaId    Id of the media, changes every time the media is replaced.\r
+  @param  Lba        The starting Logical Block Address to read from\r
+  @param  BufferSize Size of Buffer, must be a multiple of device block size.\r
+  @param  Buffer     A pointer to the destination buffer for the data. The caller is\r
+                     responsible for either having implicit or explicit ownership of the buffer.\r
+\r
+  @retval EFI_SUCCESS           The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.\r
+  @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,\r
+                                or the buffer is not on proper alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdReadBlocks (\r
+  IN     EFI_BLOCK_IO_PROTOCOL  *This,\r
+  IN     UINT32                 MediaId,\r
+  IN     EFI_LBA                Lba,\r
+  IN     UINTN                  BufferSize,\r
+     OUT VOID                   *Buffer\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SD_DEVICE              *Device;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO (This);\r
+\r
+  Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, NULL);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write BufferSize bytes from Lba into Buffer.\r
+\r
+  @param  This       Indicates a pointer to the calling context.\r
+  @param  MediaId    The media ID that the write request is for.\r
+  @param  Lba        The starting logical block address to be written. The caller is\r
+                     responsible for writing to only legitimate locations.\r
+  @param  BufferSize Size of Buffer, must be a multiple of device block size.\r
+  @param  Buffer     A pointer to the source buffer for the data.\r
+\r
+  @retval EFI_SUCCESS           The data was written correctly to the device.\r
+  @retval EFI_WRITE_PROTECTED   The device can not be written to.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.\r
+  @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,\r
+                                or the buffer is not on proper alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdWriteBlocks (\r
+  IN  EFI_BLOCK_IO_PROTOCOL   *This,\r
+  IN  UINT32                  MediaId,\r
+  IN  EFI_LBA                 Lba,\r
+  IN  UINTN                   BufferSize,\r
+  IN  VOID                    *Buffer\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SD_DEVICE              *Device;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO (This);\r
+\r
+  Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, NULL);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Flush the Block Device.\r
+\r
+  @param  This              Indicates a pointer to the calling context.\r
+\r
+  @retval EFI_SUCCESS       All outstanding data was written to the device\r
+  @retval EFI_DEVICE_ERROR  The device reported an error while writing back the data\r
+  @retval EFI_NO_MEDIA      There is no media in the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdFlushBlocks (\r
+  IN  EFI_BLOCK_IO_PROTOCOL   *This\r
+  )\r
+{\r
+  //\r
+  // return directly\r
+  //\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reset the Block Device.\r
+\r
+  @param[in]  This                 Indicates a pointer to the calling context.\r
+  @param[in]  ExtendedVerification Driver may perform diagnostics on reset.\r
+\r
+  @retval EFI_SUCCESS              The device was reset.\r
+  @retval EFI_DEVICE_ERROR         The device is not functioning properly and could\r
+                                   not be reset.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdResetEx (\r
+  IN  EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN  BOOLEAN                 ExtendedVerification\r
+  )\r
+{\r
+  SD_DEVICE                   *Device;\r
+  LIST_ENTRY                  *Link;\r
+  LIST_ENTRY                  *NextLink;\r
+  SD_REQUEST                  *Request;\r
+  EFI_TPL                     OldTpl;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);\r
+\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  for (Link = GetFirstNode (&Device->Queue);\r
+       !IsNull (&Device->Queue, Link);\r
+       Link = NextLink) {\r
+    NextLink = GetNextNode (&Device->Queue, Link);\r
+    RemoveEntryList (Link);\r
+\r
+    Request = SD_REQUEST_FROM_LINK (Link);\r
+\r
+    gBS->CloseEvent (Request->Event);\r
+    Request->Token->TransactionStatus = EFI_ABORTED;\r
+\r
+    if (Request->IsEnd) {\r
+      gBS->SignalEvent (Request->Token->Event);\r
+    }\r
+\r
+    FreePool (Request);\r
+  }\r
+  gBS->RestoreTPL (OldTpl);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Read BufferSize bytes from Lba into Buffer.\r
+\r
+  @param[in]       This         Indicates a pointer to the calling context.\r
+  @param[in]       MediaId      Id of the media, changes every time the media is replaced.\r
+  @param[in]       Lba          The starting Logical Block Address to read from.\r
+  @param[in, out]  Token        A pointer to the token associated with the transaction.\r
+  @param[in]       BufferSize   Size of Buffer, must be a multiple of device block size.\r
+  @param[out]      Buffer       A pointer to the destination buffer for the data. The caller is\r
+                                responsible for either having implicit or explicit ownership of the buffer.\r
+\r
+  @retval EFI_SUCCESS           The read request was queued if Event is not NULL.\r
+                                The data was read correctly from the device if\r
+                                the Event is NULL.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing\r
+                                the read.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.\r
+  @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of the\r
+                                intrinsic block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,\r
+                                or the buffer is not on proper alignment.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack\r
+                                of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdReadBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL *This,\r
+  IN     UINT32                 MediaId,\r
+  IN     EFI_LBA                Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN    *Token,\r
+  IN     UINTN                  BufferSize,\r
+     OUT VOID                   *Buffer\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SD_DEVICE              *Device;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);\r
+\r
+  Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, Token);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Write BufferSize bytes from Lba into Buffer.\r
+\r
+  @param[in]       This         Indicates a pointer to the calling context.\r
+  @param[in]       MediaId      The media ID that the write request is for.\r
+  @param[in]       Lba          The starting logical block address to be written. The\r
+                                caller is responsible for writing to only legitimate\r
+                                locations.\r
+  @param[in, out]  Token        A pointer to the token associated with the transaction.\r
+  @param[in]       BufferSize   Size of Buffer, must be a multiple of device block size.\r
+  @param[in]       Buffer       A pointer to the source buffer for the data.\r
+\r
+  @retval EFI_SUCCESS           The data was written correctly to the device.\r
+  @retval EFI_WRITE_PROTECTED   The device can not be written to.\r
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.\r
+  @retval EFI_NO_MEDIA          There is no media in the device.\r
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.\r
+  @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.\r
+  @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,\r
+                                or the buffer is not on proper alignment.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdWriteBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL *This,\r
+  IN     UINT32                 MediaId,\r
+  IN     EFI_LBA                Lba,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN    *Token,\r
+  IN     UINTN                  BufferSize,\r
+  IN     VOID                   *Buffer\r
+  )\r
+{\r
+  EFI_STATUS             Status;\r
+  SD_DEVICE              *Device;\r
+\r
+  Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);\r
+\r
+  Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, Token);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Flush the Block Device.\r
+\r
+  @param[in]       This     Indicates a pointer to the calling context.\r
+  @param[in, out]  Token    A pointer to the token associated with the transaction.\r
+\r
+  @retval EFI_SUCCESS       All outstanding data was written to the device\r
+  @retval EFI_DEVICE_ERROR  The device reported an error while writing back the data\r
+  @retval EFI_NO_MEDIA      There is no media in the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdFlushBlocksEx (\r
+  IN     EFI_BLOCK_IO2_PROTOCOL  *This,\r
+  IN OUT EFI_BLOCK_IO2_TOKEN     *Token\r
+  )\r
+{\r
+  //\r
+  // Signal event and return directly.\r
+  //\r
+  if (Token != NULL && Token->Event != NULL) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+    gBS->SignalEvent (Token->Event);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r