| /** @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 (( | |
| DEBUG_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 ((DEBUG_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; | |
| } |