in FreeRTOS-Plus/Source/Reliance-Edge/core/driver/buffer.c [205:369]
REDSTATUS RedBufferGet(
uint32_t ulBlock,
uint16_t uFlags,
void **ppBuffer)
{
REDSTATUS ret = 0;
uint8_t bIdx;
if((ulBlock >= gpRedVolume->ulBlockCount) || ((uFlags & BFLAG_MASK) != uFlags) || (ppBuffer == NULL))
{
REDERROR();
ret = -RED_EINVAL;
}
else
{
if(BufferFind(ulBlock, &bIdx))
{
/* Error if the buffer exists and BFLAG_NEW was specified, since
the new flag is used when a block is newly allocated/created, so
the block was previously free and and there should never be an
existing buffer for a free block.
Error if the buffer exists but does not have the same type as
was requested.
*/
if( ((uFlags & BFLAG_NEW) != 0U)
|| ((uFlags & BFLAG_META_MASK) != (gBufCtx.aHead[bIdx].uFlags & BFLAG_META_MASK)))
{
CRITICAL_ERROR();
ret = -RED_EFUBAR;
}
}
else if(gBufCtx.uNumUsed == REDCONF_BUFFER_COUNT)
{
/* The MINIMUM_BUFFER_COUNT is supposed to ensure that no operation
ever runs out of buffers, so this should never happen.
*/
CRITICAL_ERROR();
ret = -RED_EBUSY;
}
else
{
BUFFERHEAD *pHead;
/* Search for the least recently used buffer which is not
referenced.
*/
for(bIdx = (uint8_t)(REDCONF_BUFFER_COUNT - 1U); bIdx > 0U; bIdx--)
{
if(gBufCtx.aHead[gBufCtx.abMRU[bIdx]].bRefCount == 0U)
{
break;
}
}
bIdx = gBufCtx.abMRU[bIdx];
pHead = &gBufCtx.aHead[bIdx];
if(pHead->bRefCount == 0U)
{
/* If the LRU buffer is valid and dirty, write it out before
repurposing it.
*/
if(((pHead->uFlags & BFLAG_DIRTY) != 0U) && (pHead->ulBlock != BBLK_INVALID))
{
#if REDCONF_READ_ONLY == 1
CRITICAL_ERROR();
ret = -RED_EFUBAR;
#else
ret = BufferWrite(bIdx);
#endif
}
}
else
{
/* All the buffers are used, which should have been caught by
checking gBufCtx.uNumUsed.
*/
CRITICAL_ERROR();
ret = -RED_EBUSY;
}
if(ret == 0)
{
if((uFlags & BFLAG_NEW) == 0U)
{
/* Invalidate the LRU buffer. If the read fails, we do not
want the buffer head to continue to refer to the old
block number, since the read, even if it fails, may have
partially overwritten the buffer data (consider the case
where block size exceeds sector size, and some but not
all of the sectors are read successfully), and if the
buffer were to be used subsequently with its partially
erroneous contents, bad things could happen.
*/
pHead->ulBlock = BBLK_INVALID;
ret = RedIoRead(gbRedVolNum, ulBlock, 1U, gBufCtx.b.aabBuffer[bIdx]);
if((ret == 0) && ((uFlags & BFLAG_META) != 0U))
{
if(!BufferIsValid(gBufCtx.b.aabBuffer[bIdx], uFlags))
{
/* A corrupt metadata node is usually a critical
error. The master block is an exception since
it might be invalid because the volume is not
mounted; that condition is expected and should
not result in an assertion.
*/
CRITICAL_ASSERT((uFlags & BFLAG_META_MASTER) == BFLAG_META_MASTER);
ret = -RED_EIO;
}
}
#ifdef REDCONF_ENDIAN_SWAP
if(ret == 0)
{
BufferEndianSwap(gBufCtx.b.aabBuffer[bIdx], uFlags);
}
#endif
}
else
{
RedMemSet(gBufCtx.b.aabBuffer[bIdx], 0U, REDCONF_BLOCK_SIZE);
}
}
if(ret == 0)
{
pHead->bVolNum = gbRedVolNum;
pHead->ulBlock = ulBlock;
pHead->uFlags = 0U;
}
}
/* Reference the buffer, update its flags, and promote it to MRU. This
happens both when BufferFind() found an existing buffer for the
block and when the LRU buffer was repurposed to create a buffer for
the block.
*/
if(ret == 0)
{
BUFFERHEAD *pHead = &gBufCtx.aHead[bIdx];
pHead->bRefCount++;
if(pHead->bRefCount == 1U)
{
gBufCtx.uNumUsed++;
}
/* BFLAG_NEW tells this function to zero the buffer instead of
reading it from disk; it has no meaning later on, and thus is
not saved.
*/
pHead->uFlags |= (uFlags & (~BFLAG_NEW));
BufferMakeMRU(bIdx);
*ppBuffer = gBufCtx.b.aabBuffer[bIdx];
}
}
return ret;
}