/** @file The helper functions for BlockIo and BlockIo2 protocol. Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "SdDxe.h" /** Nonblocking I/O callback funtion 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; } EFI_STATUS SdStopTrans ( IN SD_DEVICE *Device ) { 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_STOP_TRANSMISSION; SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; SdMmcCmdBlk.CommandArgument = 0; Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); 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); } } if (EFI_ERROR (Status)) { return Status; } Status = SdStopTrans (Device); 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_CHNAGED The MediaId does not matched 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 ((EFI_D_INFO, "Sd%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", IsRead ? "Read" : "Write", Lba, BlockNum, Token->Event, 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 matched 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_CHNAGED The MediaId does not matched 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_CHNAGED The MediaId does not matched 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 ((EFI_D_ERROR, "SdEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", Lba, BlockNum, Token->Event, Status)); return Status; }