aboutsummaryrefslogtreecommitdiff
path: root/sdk-modifications/libsrc/fs/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'sdk-modifications/libsrc/fs/cache.c')
-rw-r--r--sdk-modifications/libsrc/fs/cache.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/sdk-modifications/libsrc/fs/cache.c b/sdk-modifications/libsrc/fs/cache.c
new file mode 100644
index 0000000..2c66e1f
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/cache.c
@@ -0,0 +1,273 @@
+/*
+ cache.c
+ The cache is not visible to the user. It should be flushed
+ when any file is closed or changes are made to the filesystem.
+
+ This cache implements a least-used-page replacement policy. This will
+ distribute sectors evenly over the pages, so if less than the maximum
+ pages are used at once, they should all eventually remain in the cache.
+ This also has the benefit of throwing out old sectors, so as not to keep
+ too many stale pages around.
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string.h>
+
+#include "fs_common.h"
+#include "fs_cache.h"
+#include "disc_io/disc.h"
+
+#include "mem_allocate.h"
+
+#define CACHE_FREE 0xFFFFFFFF
+
+CACHE* _FAT_cache_constructor (u32 numberOfPages, const IO_INTERFACE* discInterface) {
+ CACHE* cache;
+ u32 i;
+ CACHE_ENTRY* cacheEntries;
+
+ if (numberOfPages < 2) {
+ numberOfPages = 2;
+ }
+
+ cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
+ if (cache == NULL) {
+ return NULL;
+ }
+
+ cache->disc = discInterface;
+ cache->numberOfPages = numberOfPages;
+
+
+ cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages);
+ if (cacheEntries == NULL) {
+ _FAT_mem_free (cache);
+ return NULL;
+ }
+
+ for (i = 0; i < numberOfPages; i++) {
+ cacheEntries[i].sector = CACHE_FREE;
+ cacheEntries[i].count = 0;
+ cacheEntries[i].dirty = 0;
+ }
+
+ cache->cacheEntries = cacheEntries;
+
+ cache->pages = (u8*) _FAT_mem_allocate ( CACHE_PAGE_SIZE * numberOfPages);
+ if (cache->pages == NULL) {
+ _FAT_mem_free (cache->cacheEntries);
+ _FAT_mem_free (cache);
+ return NULL;
+ }
+
+ return cache;
+}
+
+void _FAT_cache_destructor (CACHE* cache) {
+ // Clear out cache before destroying it
+ _FAT_cache_flush(cache);
+
+ // Free memory in reverse allocation order
+ _FAT_mem_free (cache->pages);
+ _FAT_mem_free (cache->cacheEntries);
+ _FAT_mem_free (cache);
+
+ return;
+}
+
+/*
+Retrieve a sector's page from the cache. If it is not found in the cache,
+load it into the cache and return the page it was loaded to.
+Return CACHE_FREE on error.
+*/
+static u32 _FAT_cache_getSector (CACHE* cache, u32 sector) {
+ u32 i;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ u32 numberOfPages = cache->numberOfPages;
+
+ u32 leastUsed = 0;
+ u32 lowestCount = 0xFFFFFFFF;
+
+ for (i = 0; (i < numberOfPages) && (cacheEntries[i].sector != sector); i++) {
+ // While searching for the desired sector, also search for the leased used page
+ if ( (cacheEntries[i].sector == CACHE_FREE) || (cacheEntries[i].count < lowestCount) ) {
+ leastUsed = i;
+ lowestCount = cacheEntries[i].count;
+ }
+ }
+
+ // If it found the sector in the cache, return it
+ if ((i < numberOfPages) && (cacheEntries[i].sector == sector)) {
+ // Increment usage counter
+ cacheEntries[i].count += 1;
+ return i;
+ }
+
+ // If it didn't, replace the least used cache page with the desired sector
+ if ((cacheEntries[leastUsed].sector != CACHE_FREE) && (0xC33CA55A == cacheEntries[leastUsed].dirty)) {
+ // Write the page back to disc if it has been written to
+ if (!_FAT_disc_writeSectors (cache->disc, cacheEntries[leastUsed].sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
+ return CACHE_FREE;
+ }
+ cacheEntries[leastUsed].dirty = 0;
+ }
+
+ // Load the new sector into the cache
+ if (!_FAT_disc_readSectors (cache->disc, sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
+ return CACHE_FREE;
+ }
+ cacheEntries[leastUsed].sector = sector;
+ // Increment the usage count, don't reset it
+ // This creates a paging policy of least used PAGE, not sector
+ cacheEntries[leastUsed].count += 1;
+ return leastUsed;
+}
+
+/*
+Reads some data from a cache page, determined by the sector number
+*/
+bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memcpy (buffer, cache->pages + (CACHE_PAGE_SIZE * page) + offset, size);
+ return true;
+}
+
+/*
+Writes some data to a cache page, making sure it is loaded into memory first.
+*/
+bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
+ cache->cacheEntries[page].dirty = 0xC33CA55A;
+
+ return true;
+}
+
+/*
+some where call _FAT_cache_writePartialSector to cache sector m , but later, another
+place(in fwrite function) directly write data to sector m, in this case, need to
+cancel the dirty state of sector m
+*/
+void _FAT_cache_writePartialSector_check (CACHE* cache, u32 sector, u32 num, const void* buffer)
+{
+ u32 i, m;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ u32 numberOfPages = cache->numberOfPages;
+
+ m = 0;
+ while(m++ < num)
+ {
+ for (i = 0; i < numberOfPages; i++)
+ {
+ if(cacheEntries[i].sector == sector)
+ break;
+ }
+
+ if(i >= numberOfPages)
+ return;
+
+ //cache the data
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * i), buffer, CACHE_PAGE_SIZE);
+ //cancel the dirty state
+ cache->cacheEntries[i].dirty = 0;
+
+ sector += 1;
+ buffer += CACHE_PAGE_SIZE;
+ }
+}
+
+/*
+Writes some data to a cache page, zeroing out the page first
+*/
+bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memset (cache->pages + (CACHE_PAGE_SIZE * page), 0, CACHE_PAGE_SIZE);
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
+ cache->cacheEntries[page].dirty = 0xC33CA55A;
+
+ return true;
+}
+
+
+/*
+Flushes all dirty pages to disc, clearing the dirty flag.
+Also resets all pages' page count to 0.
+*/
+bool _FAT_cache_flush (CACHE* cache) {
+ u32 i;
+
+ for (i = 0; i < cache->numberOfPages; i++) {
+ if (0xC33CA55A == cache->cacheEntries[i].dirty) {
+ if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, 1, cache->pages + CACHE_PAGE_SIZE * i)) {
+ return CACHE_FREE;
+ }
+ }
+ cache->cacheEntries[i].count = 0;
+ cache->cacheEntries[i].dirty = 0;
+ }
+
+ _FAT_disc_clearStatus( cache->disc );
+ return true;
+}
+
+void _FAT_cache_invalidate (CACHE* cache) {
+ int i;
+ for (i = 0; i < cache->numberOfPages; i++) {
+ cache->cacheEntries[i].sector = CACHE_FREE;
+ cache->cacheEntries[i].count = 0;
+ cache->cacheEntries[i].dirty = 0;
+ }
+}
+