/** @file | |
Block I/O protocol for MMC/SD device | |
Copyright (c) 2013-2015 Intel Corporation. | |
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 "SDMediaDevice.h" | |
/** | |
Implements EFI_BLOCK_IO_PROTOCOL.Reset() function. | |
@param This The EFI_BLOCK_IO_PROTOCOL instance. | |
@param ExtendedVerification Indicates that the driver may perform a more exhaustive. | |
verification operation of the device during reset. | |
(This parameter is ingored in this driver.) | |
@retval EFI_SUCCESS Success | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MMCSDBlockReset ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
{ | |
CARD_DATA *CardData; | |
EFI_SD_HOST_IO_PROTOCOL *SDHostIo; | |
CardData = CARD_DATA_FROM_THIS(This); | |
SDHostIo = CardData->SDHostIo; | |
return SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD); | |
} | |
/** | |
Implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks() function. | |
@param This The EFI_BLOCK_IO_PROTOCOL instance. | |
@param MediaId The media id that the write request is for. | |
@param LBA The starting logical block address to read from on the device. | |
The caller is responsible for writing to only legitimate locations. | |
@param BufferSize The size of the Buffer in bytes. This must be a multiple of the | |
intrinsic block size of the device. | |
@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 Success | |
@retval EFI_DEVICE_ERROR Hardware Error | |
@retval EFI_INVALID_PARAMETER Parameter is error | |
@retval EFI_NO_MEDIA No media | |
@retval EFI_MEDIA_CHANGED Media Change | |
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MMCSDBlockReadBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA LBA, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Address; | |
CARD_DATA *CardData; | |
EFI_SD_HOST_IO_PROTOCOL *SDHostIo; | |
UINT32 RemainingLength; | |
UINT32 TransferLength; | |
UINT8 *BufferPointer; | |
BOOLEAN SectorAddressing; | |
UINTN TotalBlock; | |
DEBUG((EFI_D_INFO, "Read(LBA=%08lx, Buffer=%08x, Size=%08x)\n", LBA, Buffer, BufferSize)); | |
Status = EFI_SUCCESS; | |
CardData = CARD_DATA_FROM_THIS(This); | |
SDHostIo = CardData->SDHostIo; | |
if (MediaId != CardData->BlockIoMedia.MediaId) { | |
return EFI_MEDIA_CHANGED; | |
} | |
if (ModU64x32 (BufferSize,CardData->BlockIoMedia.BlockSize) != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if ((CardData->CardType == SDMemoryCard2High) || (CardData->CardType == MMCCardHighCap)) { | |
SectorAddressing = TRUE; | |
} else { | |
SectorAddressing = FALSE; | |
} | |
if (SectorAddressing) { | |
// | |
//Block Address | |
// | |
Address = (UINT32)DivU64x32 (MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize), 512); | |
} else { | |
// | |
//Byte Address | |
// | |
Address = (UINT32)MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize); | |
} | |
TotalBlock = (UINTN) DivU64x32 (BufferSize, CardData->BlockIoMedia.BlockSize); | |
if (LBA + TotalBlock > CardData->BlockIoMedia.LastBlock + 1) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!Buffer) { | |
Status = EFI_INVALID_PARAMETER; | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks:Invalid parameter \r\n")); | |
goto Done; | |
} | |
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) { | |
Status = EFI_BAD_BUFFER_SIZE; | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: Bad buffer size \r\n")); | |
goto Done; | |
} | |
if (BufferSize == 0) { | |
Status = EFI_SUCCESS; | |
goto Done; | |
} | |
BufferPointer = Buffer; | |
RemainingLength = (UINT32)BufferSize; | |
while (RemainingLength > 0) { | |
if ((BufferSize > CardData->BlockIoMedia.BlockSize)) { | |
if (RemainingLength > SDHostIo->HostCapability.BoundarySize) { | |
TransferLength = SDHostIo->HostCapability.BoundarySize; | |
} else { | |
TransferLength = RemainingLength; | |
} | |
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) { | |
if (!(CardData->ExtCSDRegister.CARD_TYPE & (BIT2 | BIT3))) { | |
Status = SendCommand ( | |
CardData, | |
SET_BLOCKLEN, | |
CardData->BlockIoMedia.BlockSize, | |
NoData, | |
NULL, | |
0, | |
ResponseR1, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} | |
Status = SendCommand ( | |
CardData, | |
SET_BLOCK_COUNT, | |
TransferLength / CardData->BlockIoMedia.BlockSize, | |
NoData, | |
NULL, | |
0, | |
ResponseR1, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} | |
Status = SendCommand ( | |
CardData, | |
READ_MULTIPLE_BLOCK, | |
Address, | |
InData, | |
CardData->AlignedBuffer, | |
TransferLength, | |
ResponseR1, | |
TIMEOUT_DATA, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_MULTIPLE_BLOCK -> Fail\n")); | |
break; | |
} | |
} else { | |
if (RemainingLength > CardData->BlockIoMedia.BlockSize) { | |
TransferLength = CardData->BlockIoMedia.BlockSize; | |
} else { | |
TransferLength = RemainingLength; | |
} | |
Status = SendCommand ( | |
CardData, | |
READ_SINGLE_BLOCK, | |
Address, | |
InData, | |
CardData->AlignedBuffer, | |
(UINT32)TransferLength, | |
ResponseR1, | |
TIMEOUT_DATA, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockReadBlocks: READ_SINGLE_BLOCK -> Fail\n")); | |
break; | |
} | |
} | |
CopyMem (BufferPointer, CardData->AlignedBuffer, TransferLength); | |
if (SectorAddressing) { | |
// | |
//Block Address | |
// | |
Address += TransferLength / 512; | |
} else { | |
// | |
//Byte Address | |
// | |
Address += TransferLength; | |
} | |
BufferPointer += TransferLength; | |
RemainingLength -= TransferLength; | |
} | |
if (EFI_ERROR (Status)) { | |
if ((CardData->CardType == SDMemoryCard) || | |
(CardData->CardType == SDMemoryCard2)|| | |
(CardData->CardType == SDMemoryCard2High)) { | |
SendCommand ( | |
CardData, | |
STOP_TRANSMISSION, | |
0, | |
NoData, | |
NULL, | |
0, | |
ResponseR1b, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
} else { | |
SendCommand ( | |
CardData, | |
STOP_TRANSMISSION, | |
0, | |
NoData, | |
NULL, | |
0, | |
ResponseR1, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
} | |
} | |
Done: | |
DEBUG((EFI_D_INFO, "MMCSDBlockReadBlocks: Status = %r\n", Status)); | |
return Status; | |
} | |
/** | |
Implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks() function. | |
@param This The EFI_BLOCK_IO_PROTOCOL instance. | |
@param MediaId The media id that the write request is for. | |
@param LBA The starting logical block address to read from on the device. | |
The caller is responsible for writing to only legitimate locations. | |
@param BufferSize The size of the Buffer in bytes. This must be a multiple of the | |
intrinsic block size of the device. | |
@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 Success | |
@retval EFI_DEVICE_ERROR Hardware Error | |
@retval EFI_INVALID_PARAMETER Parameter is error | |
@retval EFI_NO_MEDIA No media | |
@retval EFI_MEDIA_CHANGED Media Change | |
@retval EFI_BAD_BUFFER_SIZE Buffer size is bad | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MMCSDBlockWriteBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA LBA, | |
IN UINTN BufferSize, | |
IN VOID *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Address; | |
CARD_DATA *CardData; | |
EFI_SD_HOST_IO_PROTOCOL *SDHostIo; | |
UINT32 RemainingLength; | |
UINT32 TransferLength; | |
UINT8 *BufferPointer; | |
BOOLEAN SectorAddressing; | |
DEBUG((EFI_D_INFO, "Write(LBA=%08lx, Buffer=%08x, Size=%08x)\n", LBA, Buffer, BufferSize)); | |
Status = EFI_SUCCESS; | |
CardData = CARD_DATA_FROM_THIS(This); | |
SDHostIo = CardData->SDHostIo; | |
if ((CardData->CardType == SDMemoryCard2High) || (CardData->CardType == MMCCardHighCap)) { | |
SectorAddressing = TRUE; | |
} else { | |
SectorAddressing = FALSE; | |
} | |
if (SectorAddressing) { | |
// | |
//Block Address | |
// | |
Address = (UINT32)DivU64x32 (MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize), 512); | |
} else { | |
// | |
//Byte Address | |
// | |
Address = (UINT32)MultU64x32 (LBA, CardData->BlockIoMedia.BlockSize); | |
} | |
if (!Buffer) { | |
Status = EFI_INVALID_PARAMETER; | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Invalid parameter \r\n")); | |
goto Done; | |
} | |
if ((BufferSize % CardData->BlockIoMedia.BlockSize) != 0) { | |
Status = EFI_BAD_BUFFER_SIZE; | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Bad buffer size \r\n")); | |
goto Done; | |
} | |
if (BufferSize == 0) { | |
Status = EFI_SUCCESS; | |
goto Done; | |
} | |
if (This->Media->ReadOnly == TRUE) { | |
Status = EFI_WRITE_PROTECTED; | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: Write protected \r\n")); | |
goto Done; | |
} | |
BufferPointer = Buffer; | |
RemainingLength = (UINT32)BufferSize; | |
while (RemainingLength > 0) { | |
if ((BufferSize > CardData->BlockIoMedia.BlockSize) ) { | |
if (RemainingLength > SDHostIo->HostCapability.BoundarySize) { | |
TransferLength = SDHostIo->HostCapability.BoundarySize; | |
} else { | |
TransferLength = RemainingLength; | |
} | |
if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) { | |
if (!(CardData->ExtCSDRegister.CARD_TYPE & (BIT2 | BIT3))) { | |
Status = SendCommand ( | |
CardData, | |
SET_BLOCKLEN, | |
CardData->BlockIoMedia.BlockSize, | |
NoData, | |
NULL, | |
0, | |
ResponseR1, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} | |
Status = SendCommand ( | |
CardData, | |
SET_BLOCK_COUNT, | |
TransferLength / CardData->BlockIoMedia.BlockSize, | |
NoData, | |
NULL, | |
0, | |
ResponseR1, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} | |
CopyMem (CardData->AlignedBuffer, BufferPointer, TransferLength); | |
Status = SendCommand ( | |
CardData, | |
WRITE_MULTIPLE_BLOCK, | |
Address, | |
OutData, | |
CardData->AlignedBuffer, | |
(UINT32)TransferLength, | |
ResponseR1, | |
TIMEOUT_DATA, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "MMCSDBlockWriteBlocks: WRITE_MULTIPLE_BLOCK -> Fail\n")); | |
break; | |
} | |
} else { | |
if (RemainingLength > CardData->BlockIoMedia.BlockSize) { | |
TransferLength = CardData->BlockIoMedia.BlockSize; | |
} else { | |
TransferLength = RemainingLength; | |
} | |
CopyMem (CardData->AlignedBuffer, BufferPointer, TransferLength); | |
Status = SendCommand ( | |
CardData, | |
WRITE_BLOCK, | |
Address, | |
OutData, | |
CardData->AlignedBuffer, | |
(UINT32)TransferLength, | |
ResponseR1, | |
TIMEOUT_DATA, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
} | |
if (SectorAddressing) { | |
// | |
//Block Address | |
// | |
Address += TransferLength / 512; | |
} else { | |
// | |
//Byte Address | |
// | |
Address += TransferLength; | |
} | |
BufferPointer += TransferLength; | |
RemainingLength -= TransferLength; | |
} | |
if (EFI_ERROR (Status)) { | |
SendCommand ( | |
CardData, | |
STOP_TRANSMISSION, | |
0, | |
NoData, | |
NULL, | |
0, | |
ResponseR1b, | |
TIMEOUT_COMMAND, | |
(UINT32*)&(CardData->CardStatus) | |
); | |
} | |
Done: | |
return EFI_SUCCESS; | |
} | |
/** | |
Implements EFI_BLOCK_IO_PROTOCOL.FlushBlocks() function. | |
(In this driver, this function just returns EFI_SUCCESS.) | |
@param This The EFI_BLOCK_IO_PROTOCOL instance. | |
@retval EFI_SUCCESS | |
@retval Others | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MMCSDBlockFlushBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
MMC/SD card BlockIo init function. | |
@param CardData Pointer to CARD_DATA. | |
@retval EFI_SUCCESS | |
@retval Others | |
**/ | |
EFI_STATUS | |
MMCSDBlockIoInit ( | |
IN CARD_DATA *CardData | |
) | |
{ | |
// | |
//BlockIO protocol | |
// | |
CardData->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION; | |
CardData->BlockIo.Media = &(CardData->BlockIoMedia); | |
CardData->BlockIo.Reset = MMCSDBlockReset; | |
CardData->BlockIo.ReadBlocks = MMCSDBlockReadBlocks ; | |
CardData->BlockIo.WriteBlocks = MMCSDBlockWriteBlocks; | |
CardData->BlockIo.FlushBlocks = MMCSDBlockFlushBlocks; | |
CardData->BlockIoMedia.MediaId = 0; | |
CardData->BlockIoMedia.RemovableMedia = FALSE; | |
CardData->BlockIoMedia.MediaPresent = TRUE; | |
CardData->BlockIoMedia.LogicalPartition = FALSE; | |
if (CardData->CSDRegister.PERM_WRITE_PROTECT || CardData->CSDRegister.TMP_WRITE_PROTECT) { | |
CardData->BlockIoMedia.ReadOnly = TRUE; | |
} else { | |
CardData->BlockIoMedia.ReadOnly = FALSE; | |
} | |
CardData->BlockIoMedia.WriteCaching = FALSE; | |
CardData->BlockIoMedia.BlockSize = CardData->BlockLen; | |
CardData->BlockIoMedia.IoAlign = 1; | |
CardData->BlockIoMedia.LastBlock = (EFI_LBA)(CardData->BlockNumber - 1); | |
return EFI_SUCCESS; | |
} | |