/* +------------------------------------------------------------------------------ | 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;cnumbuf;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; }