rtt-f030/filesystem/dfs/src/dfs_cache.c

274 lines
6.9 KiB
C

/*
+------------------------------------------------------------------------------
| Project : Device Filesystem
+------------------------------------------------------------------------------
| Copyright 2004 - 2009 www.rt-thread.org
| All rights reserved.
|------------------------------------------------------------------------------
| File : dfs_cache.c, the LUT disk cache implementation
|------------------------------------------------------------------------------
| Chang Logs:
| Date Author Notes
| 2009-04-26 bernard The first version.
+------------------------------------------------------------------------------
*/
#include "dfs_cache.h"
#define ioman_isReqRo(mode) ((mode)&(IOM_MODE_READONLY))
#define ioman_isReqRw(mode) ((mode)&(IOM_MODE_READWRITE))
#define ioman_isReqExp(mode) ((mode)&(IOM_MODE_EXP_REQ))
rt_err_t ioman_init(IOManager* ioman)
{
register rt_uint32_t index;
RT_ASSERT(ioman != RT_NULL);
ioman->numbuf = DFS_CACHE_MAX_NUM;
rt_memset(ioman->sector, 0,sizeof(ioman->sector));
rt_memset(ioman->status, 0,sizeof(ioman->status));
rt_memset(ioman->usage, 0, sizeof(ioman->usage));
rt_memset(ioman->ring_fifo, 0, sizeof(ioman->ring_fifo));
rt_memset(ioman->cache, 0, sizeof(ioman->cache));
/* init fifo */
for (index = 0; index < ioman->numbuf; index ++)
{
ioman->ring_fifo[index] = ioman->numbuf - index - 1;
}
return RT_EOK;
}
/*
* get the last fifo item and put it to the top of fifo ring
*/
static rt_uint8_t ioman_ring_fifo_swap(IOManager *ioman)
{
rt_uint8_t bp;
rt_uint32_t index;
bp = ioman->ring_fifo[ioman->numbuf - 1];
for (index = ioman->numbuf - 1; index > 0; index --)
{
ioman->ring_fifo[index] = ioman->ring_fifo[index - 1];
}
ioman->ring_fifo[0] = bp;
return bp;
}
/*
* get the index of bp in fifo ring and then put it to the top of
* fifo ring
*/
static void ioman_ring_fifo_relocate(IOManager *ioman, rt_uint32_t bp)
{
rt_uint32_t bp_index = 0;
register rt_uint32_t index;
/* find bp in fifo ring */
for (index = 0; index < ioman->numbuf; index ++)
{
if (ioman->ring_fifo[index] == bp)
{
bp_index = index;
break;
}
}
/* not found bp in fifo ring */
if (index == ioman->numbuf) return;
/* move the bp to the top of fifo ring */
for (index = bp_index; index > 0; index --)
{
ioman->ring_fifo[index] = ioman->ring_fifo[index - 1];
}
ioman->ring_fifo[0] = bp;
}
/*
* get last bp in fifo ring
*/
rt_inline rt_uint8_t ioman_ring_fifo_last(IOManager *ioman)
{
RT_ASSERT(ioman != RT_NULL);
return ioman->ring_fifo[ioman->numbuf - 1];
}
static rt_err_t ioman_readSector(IOManager *ioman, rt_uint32_t address, rt_uint8_t* buf)
{
rt_err_t result;
RT_ASSERT(buf != RT_NULL);
result = rt_device_read(ioman->device, address * DFS_SECTOR_SIZE, buf, DFS_SECTOR_SIZE);
if (result == DFS_SECTOR_SIZE) return RT_EOK;
return -RT_ERROR;
}
static rt_err_t ioman_writeSector(IOManager *ioman, rt_uint32_t address, rt_uint8_t* buf)
{
rt_err_t result;
RT_ASSERT(buf != RT_NULL);
result = rt_device_write(ioman->device, address * DFS_SECTOR_SIZE, buf, DFS_SECTOR_SIZE);
if (result == DFS_SECTOR_SIZE) return RT_EOK;
return -RT_ERROR;
}
static rt_int32_t ioman_findSectorInCache(IOManager *ioman, rt_uint32_t address)
{
rt_uint32_t c;
for(c=0;c<ioman->numbuf;c++)
{
if((ioman->status[c] & (1 << IOMAN_STATUS_ATTR_VALIDDATA)) &&
ioman->sector[c] == address)
return (c);
}
return -RT_ERROR;
}
static rt_err_t ioman_flushSector(IOManager *ioman, rt_uint32_t bp)
{
rt_err_t result;
RT_ASSERT(ioman != RT_NULL);
result = ioman_writeSector(ioman, ioman->sector[bp], &ioman->cache[bp][0]);
if (result == RT_EOK)
{
/* set status */
ioman->status[bp] &= ~(1 << IOMAN_STATUS_ATTR_WRITE);
}
return result;
}
rt_uint8_t* ioman_getSector(IOManager *ioman, rt_uint32_t address, rt_uint8_t mode)
{
rt_int32_t bp;
RT_ASSERT(ioman != RT_NULL);
bp = ioman_findSectorInCache(ioman, address);
if (bp != -RT_ERROR)
{
/* incress cache usage */
ioman->usage[bp] ++;
/* relocate bp in fifo ring */
ioman_ring_fifo_relocate(ioman, bp);
if (ioman_isReqRw(mode))
ioman->status[bp] |= (1 << IOMAN_STATUS_ATTR_WRITE);
return &(ioman->cache[bp][0]);
}
/* not find in cache, get the last bp in fifo ring */
bp = ioman_ring_fifo_last(ioman);
if ((ioman->status[bp] & (1 << IOMAN_STATUS_ATTR_WRITE)) &&
ioman->usage[bp] == 0)
{
/* it's a writable buffer, flush it */
ioman_flushSector(ioman, bp);
}
/* strip last bp in fifo ring */
bp = ioman_ring_fifo_swap(ioman);
/* read sector */
ioman_readSector(ioman, address, &ioman->cache[bp][0]);
ioman->sector[bp] = address;
ioman->usage [bp] = 1;
ioman->status[bp] = (1 << IOMAN_STATUS_ATTR_VALIDDATA);
if (ioman_isReqRw(mode))
ioman->status[bp] |= (1 << IOMAN_STATUS_ATTR_WRITE);
return &ioman->cache[bp][0];
}
rt_err_t ioman_releaseSector(IOManager *ioman, rt_uint8_t* buf)
{
rt_uint32_t bp;
/* get buffer place */
bp = ((rt_uint32_t)buf - (rt_uint32_t)&ioman->cache[0]) / DFS_SECTOR_SIZE;
/* decrease usage */
if (ioman->usage[bp] > 0) ioman->usage[bp] --;
if (ioman->usage[bp] == 0)
{
if(ioman->status[bp] & (1 << IOMAN_STATUS_ATTR_WRITE))
{
ioman_flushSector(ioman,bp);
}
}
return RT_EOK;
}
rt_err_t ioman_flushRange(IOManager *ioman, rt_uint32_t address_low, rt_uint32_t address_high)
{
rt_uint32_t c;
if(address_low > address_high)
{
c = address_low; address_low = address_high; address_high = c;
}
for(c = 0; c < ioman->numbuf; c++)
{
if((ioman->sector[c]>=address_low)
&& (ioman->sector[c]<=address_high)
&& (ioman->status[c] & (1 << IOMAN_STATUS_ATTR_WRITE)))
{
if(ioman_flushSector(ioman,c) != RT_EOK)
return -RT_ERROR;
/* remove writable status */
if(ioman->usage[c]==0) ioman->status[c] &= ~IOMAN_STATUS_ATTR_WRITE;
}
}
return RT_EOK;
}
/*
* read multi-sectors directly (none-cachable)
*/
rt_err_t ioman_directSectorRead(IOManager *ioman, rt_uint32_t address, rt_uint8_t* buf, rt_uint32_t numsector)
{
rt_err_t result;
RT_ASSERT(buf != RT_NULL);
result = rt_device_read(ioman->device, address * DFS_SECTOR_SIZE, buf, DFS_SECTOR_SIZE * numsector);
if (result == DFS_SECTOR_SIZE * numsector) return RT_EOK;
return -RT_ERROR;
}
/*
* write multi-sectors directly (none-cachable)
*/
rt_err_t ioman_directSectorWrite(IOManager *ioman, rt_uint32_t address, rt_uint8_t* buf, rt_uint32_t numsector)
{
rt_err_t result;
RT_ASSERT(buf != RT_NULL);
result = rt_device_write(ioman->device, address * DFS_SECTOR_SIZE, buf, DFS_SECTOR_SIZE * numsector);
if (result == DFS_SECTOR_SIZE * numsector) return RT_EOK;
return -RT_ERROR;
}