summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c1381
1 files changed, 1381 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
new file mode 100644
index 00000000..c78bf8b5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
@@ -0,0 +1,1381 @@
+/** @file
+ The helper functions for BlockIo and BlockIo2 protocol.
+
+ Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "SdDxe.h"
+
+/**
+ Nonblocking I/O callback function when the event is signaled.
+
+ @param[in] Event The Event this notify function registered to.
+ @param[in] Context Pointer to the context data registered to the
+ Event.
+
+**/
+VOID
+EFIAPI
+AsyncIoCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ SD_REQUEST *Request;
+
+ gBS->CloseEvent (Event);
+
+ Request = (SD_REQUEST *) Context;
+
+ DEBUG_CODE_BEGIN ();
+ DEBUG ((EFI_D_INFO, "Sd Async Request: CmdIndex[%d] Arg[%08x] %r\n",
+ Request->SdMmcCmdBlk.CommandIndex, Request->SdMmcCmdBlk.CommandArgument,
+ Request->Packet.TransactionStatus));
+ DEBUG_CODE_END ();
+
+ if (EFI_ERROR (Request->Packet.TransactionStatus)) {
+ Request->Token->TransactionStatus = Request->Packet.TransactionStatus;
+ }
+
+ RemoveEntryList (&Request->Link);
+
+ if (Request->IsEnd) {
+ gBS->SignalEvent (Request->Token->Event);
+ }
+
+ FreePool (Request);
+}
+
+/**
+ Send command SET_RELATIVE_ADDRESS to the device to set the device address.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[out] Rca The relative device address to assign.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdSetRca (
+ IN SD_DEVICE *Device,
+ OUT UINT16 *Rca
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "Set RCA succeeds with Resp0 = 0x%x\n", SdMmcStatusBlk.Resp0));
+ *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SELECT to the device to select/deselect the device.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdSelect (
+ IN SD_DEVICE *Device,
+ IN UINT16 Rca
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ if (Rca != 0) {
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
+ }
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+
+ return Status;
+}
+
+/**
+ Send command SEND_STATUS to the device to get device status.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] DevStatus The buffer to store the device status.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdSendStatus (
+ IN SD_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT UINT32 *DevStatus
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+ if (!EFI_ERROR (Status)) {
+ CopyMem (DevStatus, &SdMmcStatusBlk.Resp0, sizeof (UINT32));
+ }
+ return Status;
+}
+
+/**
+ Send command SEND_CSD to the device to get the CSD register data.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] Csd The buffer to store the SD_CSD register data.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdGetCsd (
+ IN SD_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT SD_CSD *Csd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ ZeroMem (Csd, sizeof (SD_CSD));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_CSD;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
+ //
+ CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Send command SEND_CID to the device to get the CID register data.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Rca The relative device address to use.
+ @param[out] Cid The buffer to store the SD_CID register data.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdGetCid (
+ IN SD_DEVICE *Device,
+ IN UINT16 Rca,
+ OUT SD_CID *Cid
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk;
+ EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk;
+ EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet;
+
+ PassThru = Device->Private->PassThru;
+
+ ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk));
+ ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk));
+ ZeroMem (&Packet, sizeof (Packet));
+ ZeroMem (Cid, sizeof (SD_CID));
+
+ Packet.SdMmcCmdBlk = &SdMmcCmdBlk;
+ Packet.SdMmcStatusBlk = &SdMmcStatusBlk;
+ Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ SdMmcCmdBlk.CommandIndex = SD_SEND_CID;
+ SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2;
+ SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16;
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL);
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
+ //
+ CopyMem (((UINT8*)Cid) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CID) - 1);
+ }
+
+ return Status;
+}
+
+/**
+ Read/write single block through sync or async I/O request.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Lba The starting logical block address to be read/written.
+ The caller is responsible for reading/writing to only
+ legitimate locations.
+ @param[in] Buffer A pointer to the destination/source buffer for the data.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @param[in] Token A pointer to the token associated with the transaction.
+ @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
+ This parameter is only meaningful in async I/O request.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdRwSingleBlock (
+ IN SD_DEVICE *Device,
+ IN EFI_LBA Lba,
+ IN VOID *Buffer,
+ IN UINTN BufferSize,
+ IN BOOLEAN IsRead,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ SD_REQUEST *RwSingleBlkReq;
+ EFI_TPL OldTpl;
+
+ RwSingleBlkReq = NULL;
+ PassThru = Device->Private->PassThru;
+
+ RwSingleBlkReq = AllocateZeroPool (sizeof (SD_REQUEST));
+ if (RwSingleBlkReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ RwSingleBlkReq->Signature = SD_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Device->Queue, &RwSingleBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ RwSingleBlkReq->Packet.SdMmcCmdBlk = &RwSingleBlkReq->SdMmcCmdBlk;
+ RwSingleBlkReq->Packet.SdMmcStatusBlk = &RwSingleBlkReq->SdMmcStatusBlk;
+ //
+ // Calculate timeout value through the below formula.
+ // Timeout = (transfer size) / (2MB/s).
+ // Taking 2MB/s as divisor as it's the lowest transfer speed
+ // above class 2.
+ // Refer to SD Physical Layer Simplified spec section 3.4 for details.
+ //
+ RwSingleBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;
+
+ if (IsRead) {
+ RwSingleBlkReq->Packet.InDataBuffer = Buffer;
+ RwSingleBlkReq->Packet.InTransferLength = (UINT32)BufferSize;
+
+ RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_SINGLE_BLOCK;
+ RwSingleBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ } else {
+ RwSingleBlkReq->Packet.OutDataBuffer = Buffer;
+ RwSingleBlkReq->Packet.OutTransferLength = (UINT32)BufferSize;
+
+ RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_SINGLE_BLOCK;
+ RwSingleBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ }
+
+ if (Device->SectorAddressing) {
+ RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba;
+ } else {
+ RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize);
+ }
+
+ RwSingleBlkReq->IsEnd = IsEnd;
+ RwSingleBlkReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ AsyncIoCallback,
+ RwSingleBlkReq,
+ &RwSingleBlkReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ RwSingleBlkReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &RwSingleBlkReq->Packet, RwSingleBlkReq->Event);
+
+Error:
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ //
+ // For asynchronous operation, only free request and event in error case.
+ // The request and event will be freed in asynchronous callback for success case.
+ //
+ if (EFI_ERROR (Status) && (RwSingleBlkReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&RwSingleBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (RwSingleBlkReq->Event != NULL) {
+ gBS->CloseEvent (RwSingleBlkReq->Event);
+ }
+ FreePool (RwSingleBlkReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (RwSingleBlkReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&RwSingleBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (RwSingleBlkReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read/write multiple blocks through sync or async I/O request.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Lba The starting logical block address to be read/written.
+ The caller is responsible for reading/writing to only
+ legitimate locations.
+ @param[in] Buffer A pointer to the destination/source buffer for the data.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @param[in] Token A pointer to the token associated with the transaction.
+ @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
+ This parameter is only meaningful in async I/O request.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdRwMultiBlocks (
+ IN SD_DEVICE *Device,
+ IN EFI_LBA Lba,
+ IN VOID *Buffer,
+ IN UINTN BufferSize,
+ IN BOOLEAN IsRead,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ SD_REQUEST *RwMultiBlkReq;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ EFI_TPL OldTpl;
+
+ RwMultiBlkReq = NULL;
+
+ PassThru = Device->Private->PassThru;
+
+ RwMultiBlkReq = AllocateZeroPool (sizeof (SD_REQUEST));
+ if (RwMultiBlkReq == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ RwMultiBlkReq->Signature = SD_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Device->Queue, &RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ RwMultiBlkReq->Packet.SdMmcCmdBlk = &RwMultiBlkReq->SdMmcCmdBlk;
+ RwMultiBlkReq->Packet.SdMmcStatusBlk = &RwMultiBlkReq->SdMmcStatusBlk;
+ //
+ // Calculate timeout value through the below formula.
+ // Timeout = (transfer size) / (2MB/s).
+ // Taking 2MB/s as divisor as it's the lowest transfer speed
+ // above class 2.
+ // Refer to SD Physical Layer Simplified spec section 3.4 for details.
+ //
+ RwMultiBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;
+
+ if (IsRead) {
+ RwMultiBlkReq->Packet.InDataBuffer = Buffer;
+ RwMultiBlkReq->Packet.InTransferLength = (UINT32)BufferSize;
+
+ RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_MULTIPLE_BLOCK;
+ RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ } else {
+ RwMultiBlkReq->Packet.OutDataBuffer = Buffer;
+ RwMultiBlkReq->Packet.OutTransferLength = (UINT32)BufferSize;
+
+ RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_MULTIPLE_BLOCK;
+ RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc;
+ RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+ }
+
+ if (Device->SectorAddressing) {
+ RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba;
+ } else {
+ RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize);
+ }
+
+ RwMultiBlkReq->IsEnd = IsEnd;
+ RwMultiBlkReq->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ AsyncIoCallback,
+ RwMultiBlkReq,
+ &RwMultiBlkReq->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ RwMultiBlkReq->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &RwMultiBlkReq->Packet, RwMultiBlkReq->Event);
+
+Error:
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ //
+ // For asynchronous operation, only free request and event in error case.
+ // The request and event will be freed in asynchronous callback for success case.
+ //
+ if (EFI_ERROR (Status) && (RwMultiBlkReq != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (RwMultiBlkReq->Event != NULL) {
+ gBS->CloseEvent (RwMultiBlkReq->Event);
+ }
+ FreePool (RwMultiBlkReq);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (RwMultiBlkReq != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&RwMultiBlkReq->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (RwMultiBlkReq);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ This function transfers data from/to the sd memory card device.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] MediaId The media ID that the read/write request is for.
+ @param[in] Lba The starting logical block address to be read/written.
+ The caller is responsible for reading/writing to only
+ legitimate locations.
+ @param[in, out] Buffer A pointer to the destination/source buffer for the data.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] IsRead Indicates it is a read or write operation.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+
+ @retval EFI_SUCCESS The data was read/written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be read/written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the read/write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read/write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+SdReadWrite (
+ IN SD_DEVICE *Device,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT VOID *Buffer,
+ IN UINTN BufferSize,
+ IN BOOLEAN IsRead,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token
+ )
+{
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN BlockNum;
+ UINTN IoAlign;
+ UINTN Remaining;
+ UINT32 MaxBlock;
+ BOOLEAN LastRw;
+
+ Status = EFI_SUCCESS;
+ Media = &Device->BlockMedia;
+ LastRw = FALSE;
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (!IsRead && Media->ReadOnly) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ //
+ // Check parameters.
+ //
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BufferSize == 0) {
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ gBS->SignalEvent (Token->Event);
+ }
+ return EFI_SUCCESS;
+ }
+
+ BlockSize = Media->BlockSize;
+ if ((BufferSize % BlockSize) != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ BlockNum = BufferSize / BlockSize;
+ if ((Lba + BlockNum - 1) > Media->LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IoAlign = Media->IoAlign;
+ if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ }
+
+ //
+ // Start to execute data transfer. The max block number in single cmd is 65535 blocks.
+ //
+ Remaining = BlockNum;
+ MaxBlock = 0xFFFF;
+
+ while (Remaining > 0) {
+ if (Remaining <= MaxBlock) {
+ BlockNum = Remaining;
+ LastRw = TRUE;
+ } else {
+ BlockNum = MaxBlock;
+ }
+
+ BufferSize = BlockNum * BlockSize;
+ if (BlockNum == 1) {
+ Status = SdRwSingleBlock (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw);
+ } else {
+ Status = SdRwMultiBlocks (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw);
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ DEBUG ((DEBUG_BLKIO, "Sd%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n",
+ IsRead ? "Read" : "Write", Lba, BlockNum,
+ (Token != NULL) ? Token->Event : NULL, Status));
+ Lba += BlockNum;
+ Buffer = (UINT8*)Buffer + BufferSize;
+ Remaining -= BlockNum;
+ }
+
+ return Status;
+}
+
+/**
+ Reset the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+ @param ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+SdReset (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ EFI_STATUS Status;
+ SD_DEVICE *Device;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO (This);
+
+ PassThru = Device->Private->PassThru;
+ Status = PassThru->ResetDevice (PassThru, Device->Slot);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ return Status;
+}
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId Id of the media, changes every time the media is replaced.
+ @param Lba The starting Logical Block Address to read from
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit ownership of the buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+SdReadBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SD_DEVICE *Device;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO (This);
+
+ Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, NULL);
+ return Status;
+}
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param This Indicates a pointer to the calling context.
+ @param MediaId The media ID that the write request is for.
+ @param Lba The starting logical block address to be written. The caller is
+ responsible for writing to only legitimate locations.
+ @param BufferSize Size of Buffer, must be a multiple of device block size.
+ @param Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+SdWriteBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SD_DEVICE *Device;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO (This);
+
+ Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, NULL);
+ return Status;
+}
+
+/**
+ Flush the Block Device.
+
+ @param This Indicates a pointer to the calling context.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+SdFlushBlocks (
+ IN EFI_BLOCK_IO_PROTOCOL *This
+ )
+{
+ //
+ // return directly
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset the Block Device.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] ExtendedVerification Driver may perform diagnostics on reset.
+
+ @retval EFI_SUCCESS The device was reset.
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could
+ not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+SdResetEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
+ )
+{
+ SD_DEVICE *Device;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NextLink;
+ SD_REQUEST *Request;
+ EFI_TPL OldTpl;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);
+
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ for (Link = GetFirstNode (&Device->Queue);
+ !IsNull (&Device->Queue, Link);
+ Link = NextLink) {
+ NextLink = GetNextNode (&Device->Queue, Link);
+ RemoveEntryList (Link);
+
+ Request = SD_REQUEST_FROM_LINK (Link);
+
+ gBS->CloseEvent (Request->Event);
+ Request->Token->TransactionStatus = EFI_ABORTED;
+
+ if (Request->IsEnd) {
+ gBS->SignalEvent (Request->Token->Event);
+ }
+
+ FreePool (Request);
+ }
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read BufferSize bytes from Lba into Buffer.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId Id of the media, changes every time the media is replaced.
+ @param[in] Lba The starting Logical Block Address to read from.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[out] Buffer A pointer to the destination buffer for the data. The caller is
+ responsible for either having implicit or explicit ownership of the buffer.
+
+ @retval EFI_SUCCESS The read request was queued if Event is not NULL.
+ The data was read correctly from the device if
+ the Event is NULL.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing
+ the read.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
+ intrinsic block size of the device.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
+ of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+SdReadBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SD_DEVICE *Device;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);
+
+ Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, Token);
+ return Status;
+}
+
+/**
+ Write BufferSize bytes from Lba into Buffer.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId The media ID that the write request is for.
+ @param[in] Lba The starting logical block address to be written. The
+ caller is responsible for writing to only legitimate
+ locations.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+ @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
+ @param[in] Buffer A pointer to the source buffer for the data.
+
+ @retval EFI_SUCCESS The data was written correctly to the device.
+ @retval EFI_WRITE_PROTECTED The device can not be written to.
+ @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
+ or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+SdWriteBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SD_DEVICE *Device;
+
+ Device = SD_DEVICE_DATA_FROM_BLKIO2 (This);
+
+ Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, Token);
+ return Status;
+}
+
+/**
+ Flush the Block Device.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in, out] Token A pointer to the token associated with the transaction.
+
+ @retval EFI_SUCCESS All outstanding data was written to the device
+ @retval EFI_DEVICE_ERROR The device reported an error while writing back the data
+ @retval EFI_NO_MEDIA There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+SdFlushBlocksEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token
+ )
+{
+ //
+ // Signal event and return directly.
+ //
+ if (Token != NULL && Token->Event != NULL) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ gBS->SignalEvent (Token->Event);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set the erase start address through sync or async I/O request.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] StartLba The starting logical block address to be erased.
+ @param[in] Token A pointer to the token associated with the transaction.
+ @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
+ This parameter is only meaningful in async I/O request.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdEraseBlockStart (
+ IN SD_DEVICE *Device,
+ IN EFI_LBA StartLba,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ SD_REQUEST *EraseBlockStart;
+ EFI_TPL OldTpl;
+
+ EraseBlockStart = NULL;
+ PassThru = Device->Private->PassThru;
+
+ EraseBlockStart = AllocateZeroPool (sizeof (SD_REQUEST));
+ if (EraseBlockStart == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ EraseBlockStart->Signature = SD_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Device->Queue, &EraseBlockStart->Link);
+ gBS->RestoreTPL (OldTpl);
+ EraseBlockStart->Packet.SdMmcCmdBlk = &EraseBlockStart->SdMmcCmdBlk;
+ EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk;
+ EraseBlockStart->Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ EraseBlockStart->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_START;
+ EraseBlockStart->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+
+ if (Device->SectorAddressing) {
+ EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba;
+ } else {
+ EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Device->BlockMedia.BlockSize);
+ }
+
+ EraseBlockStart->IsEnd = IsEnd;
+ EraseBlockStart->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ AsyncIoCallback,
+ EraseBlockStart,
+ &EraseBlockStart->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ EraseBlockStart->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->Event);
+
+Error:
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ //
+ // For asynchronous operation, only free request and event in error case.
+ // The request and event will be freed in asynchronous callback for success case.
+ //
+ if (EFI_ERROR (Status) && (EraseBlockStart != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlockStart->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (EraseBlockStart->Event != NULL) {
+ gBS->CloseEvent (EraseBlockStart->Event);
+ }
+ FreePool (EraseBlockStart);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (EraseBlockStart != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlockStart->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (EraseBlockStart);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Set the erase end address through sync or async I/O request.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] EndLba The ending logical block address to be erased.
+ @param[in] Token A pointer to the token associated with the transaction.
+ @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
+ This parameter is only meaningful in async I/O request.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdEraseBlockEnd (
+ IN SD_DEVICE *Device,
+ IN EFI_LBA EndLba,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ SD_REQUEST *EraseBlockEnd;
+ EFI_TPL OldTpl;
+
+ EraseBlockEnd = NULL;
+ PassThru = Device->Private->PassThru;
+
+ EraseBlockEnd = AllocateZeroPool (sizeof (SD_REQUEST));
+ if (EraseBlockEnd == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ EraseBlockEnd->Signature = SD_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Device->Queue, &EraseBlockEnd->Link);
+ gBS->RestoreTPL (OldTpl);
+ EraseBlockEnd->Packet.SdMmcCmdBlk = &EraseBlockEnd->SdMmcCmdBlk;
+ EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk;
+ EraseBlockEnd->Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ EraseBlockEnd->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_END;
+ EraseBlockEnd->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;
+
+ if (Device->SectorAddressing) {
+ EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba;
+ } else {
+ EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Device->BlockMedia.BlockSize);
+ }
+
+ EraseBlockEnd->IsEnd = IsEnd;
+ EraseBlockEnd->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ AsyncIoCallback,
+ EraseBlockEnd,
+ &EraseBlockEnd->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ EraseBlockEnd->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->Event);
+
+Error:
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ //
+ // For asynchronous operation, only free request and event in error case.
+ // The request and event will be freed in asynchronous callback for success case.
+ //
+ if (EFI_ERROR (Status) && (EraseBlockEnd != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlockEnd->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (EraseBlockEnd->Event != NULL) {
+ gBS->CloseEvent (EraseBlockEnd->Event);
+ }
+ FreePool (EraseBlockEnd);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (EraseBlockEnd != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlockEnd->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (EraseBlockEnd);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Erase specified blocks through sync or async I/O request.
+
+ @param[in] Device A pointer to the SD_DEVICE instance.
+ @param[in] Token A pointer to the token associated with the transaction.
+ @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds.
+ This parameter is only meaningful in async I/O request.
+
+ @retval EFI_SUCCESS The request is executed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources.
+ @retval Others The request could not be executed successfully.
+
+**/
+EFI_STATUS
+SdEraseBlock (
+ IN SD_DEVICE *Device,
+ IN EFI_BLOCK_IO2_TOKEN *Token,
+ IN BOOLEAN IsEnd
+ )
+{
+ EFI_STATUS Status;
+ EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru;
+ SD_REQUEST *EraseBlock;
+ EFI_TPL OldTpl;
+
+ EraseBlock = NULL;
+ PassThru = Device->Private->PassThru;
+
+ EraseBlock = AllocateZeroPool (sizeof (SD_REQUEST));
+ if (EraseBlock == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ EraseBlock->Signature = SD_REQUEST_SIGNATURE;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&Device->Queue, &EraseBlock->Link);
+ gBS->RestoreTPL (OldTpl);
+ EraseBlock->Packet.SdMmcCmdBlk = &EraseBlock->SdMmcCmdBlk;
+ EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk;
+ EraseBlock->Packet.Timeout = SD_GENERIC_TIMEOUT;
+
+ EraseBlock->SdMmcCmdBlk.CommandIndex = SD_ERASE;
+ EraseBlock->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc;
+ EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;
+
+ EraseBlock->IsEnd = IsEnd;
+ EraseBlock->Token = Token;
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ AsyncIoCallback,
+ EraseBlock,
+ &EraseBlock->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ } else {
+ EraseBlock->Event = NULL;
+ }
+
+ Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->Event);
+
+Error:
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ //
+ // For asynchronous operation, only free request and event in error case.
+ // The request and event will be freed in asynchronous callback for success case.
+ //
+ if (EFI_ERROR (Status) && (EraseBlock != NULL)) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlock->Link);
+ gBS->RestoreTPL (OldTpl);
+ if (EraseBlock->Event != NULL) {
+ gBS->CloseEvent (EraseBlock->Event);
+ }
+ FreePool (EraseBlock);
+ }
+ } else {
+ //
+ // For synchronous operation, free request whatever the execution result is.
+ //
+ if (EraseBlock != NULL) {
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&EraseBlock->Link);
+ gBS->RestoreTPL (OldTpl);
+ FreePool (EraseBlock);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Erase a specified number of device blocks.
+
+ @param[in] This Indicates a pointer to the calling context.
+ @param[in] MediaId The media ID that the erase request is for.
+ @param[in] Lba The starting logical block address to be
+ erased. The caller is responsible for erasing
+ only legitimate locations.
+ @param[in, out] Token A pointer to the token associated with the
+ transaction.
+ @param[in] Size The size in bytes to be erased. This must be
+ a multiple of the physical block size of the
+ device.
+
+ @retval EFI_SUCCESS The erase request was queued if Event is not
+ NULL. The data was erased correctly to the
+ device if the Event is NULL.to the device.
+ @retval EFI_WRITE_PROTECTED The device cannot be erased due to write
+ protection.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the erase operation.
+ @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not
+ valid.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
+
+**/
+EFI_STATUS
+EFIAPI
+SdEraseBlocks (
+ IN EFI_ERASE_BLOCK_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN OUT EFI_ERASE_BLOCK_TOKEN *Token,
+ IN UINTN Size
+ )
+{
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_MEDIA *Media;
+ UINTN BlockSize;
+ UINTN BlockNum;
+ EFI_LBA LastLba;
+ SD_DEVICE *Device;
+
+ Status = EFI_SUCCESS;
+ Device = SD_DEVICE_DATA_FROM_ERASEBLK (This);
+ Media = &Device->BlockMedia;
+
+ if (MediaId != Media->MediaId) {
+ return EFI_MEDIA_CHANGED;
+ }
+
+ if (Media->ReadOnly) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ //
+ // Check parameters.
+ //
+ BlockSize = Media->BlockSize;
+ if ((Size % BlockSize) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BlockNum = Size / BlockSize;
+ if ((Lba + BlockNum - 1) > Media->LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Token != NULL) && (Token->Event != NULL)) {
+ Token->TransactionStatus = EFI_SUCCESS;
+ }
+
+ LastLba = Lba + BlockNum - 1;
+
+ Status = SdEraseBlockStart (Device, Lba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SdEraseBlockEnd (Device, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SdEraseBlock (Device, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "SdEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n",
+ Lba,
+ BlockNum,
+ (Token != NULL) ? Token->Event : NULL,
+ Status
+ ));
+
+ return Status;
+}
+