Index: apps/main.c =================================================================== --- apps/main.c (revision 27026) +++ apps/main.c (working copy) @@ -21,6 +21,7 @@ #include "config.h" #include "storage.h" +#include "sectorcache.h" #include "disk.h" #include "fat.h" #include "lcd.h" @@ -526,6 +527,8 @@ panicf("ata: %d", rc); } + sectorcache_init(); + #ifdef HAVE_EEPROM_SETTINGS CHART(">eeprom_settings_init"); eeprom_settings_init(); Index: firmware/common/dir_uncached.c =================================================================== --- firmware/common/dir_uncached.c (revision 27026) +++ firmware/common/dir_uncached.c (working copy) @@ -27,6 +27,7 @@ #include "dir.h" #include "debug.h" #include "filefuncs.h" +#include "sectorcache.h" #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8) #define MAX_OPEN_DIRS 12 @@ -51,7 +52,9 @@ (void)volume; #endif { - pdir->busy = false; /* mark as available, no further action */ + pdir->busy = false; + if (pdir->fatdir.cache) + sectorcache_unlock(&pdir->fatdir.cache, 1, 1); closed++; } } @@ -147,6 +150,7 @@ int closedir_uncached(DIR_UNCACHED* dir) { dir->busy=false; + fat_closedir(&dir->fatdir); return 0; } Index: firmware/common/dircache.c =================================================================== --- firmware/common/dircache.c (revision 27026) +++ firmware/common/dircache.c (working copy) @@ -32,6 +32,7 @@ #include #include "debug.h" #include "system.h" +#include "sectorcache.h" #include "logf.h" #include "dircache.h" #include "thread.h" @@ -74,7 +75,7 @@ static unsigned long appflags = 0; static struct event_queue dircache_queue; -static long dircache_stack[(DEFAULT_STACK_SIZE + 0x400)/sizeof(long)]; +static long dircache_stack[(DEFAULT_STACK_SIZE)/sizeof(long)]; static const char dircache_thread_name[] = "dircache"; static struct fdbind_queue fdbind_cache[MAX_PENDING_BINDINGS]; @@ -267,6 +268,8 @@ ce = ce->next; } + + fat_closedir(sab.dir); return rc; } Index: firmware/common/disk.c =================================================================== --- firmware/common/disk.c (revision 27026) +++ firmware/common/disk.c (working copy) @@ -21,6 +21,8 @@ #include #include "kernel.h" #include "storage.h" +#include "sectorcache.h" +#include "system.h" #include "debug.h" #include "fat.h" #ifdef HAVE_HOTSWAP @@ -69,7 +71,8 @@ struct partinfo* disk_init(IF_MD_NONVOID(int drive)) { int i; - unsigned char sector[SECTOR_SIZE]; + int rc; + struct sectorcache_entry* sector; #ifdef HAVE_MULTIDRIVE /* For each drive, start at a different position, in order not to destroy the first entry of drive 0. @@ -83,17 +86,22 @@ (void)drive; #endif - storage_read_sectors(IF_MD2(drive,) 0,1, sector); + if ((rc = sectorcache_lock(IF_MD2(drive,) 0, §or, false))) + { + DEBUGF("Can't read MBR: rc = %d\n", rc); + return NULL; + } + /* check that the boot sector is initialized */ - if ( (sector[510] != 0x55) || - (sector[511] != 0xaa)) { + if ( (sector->buffer[510] != 0x55) || + (sector->buffer[511] != 0xaa)) { DEBUGF("Bad boot sector signature\n"); return NULL; } /* parse partitions */ for ( i=0; i<4; i++ ) { - unsigned char* ptr = sector + 0x1be + 16*i; + unsigned char* ptr = sector->buffer + 0x1be + 16*i; pinfo[i].type = ptr[4]; pinfo[i].start = BYTES2INT32(ptr, 8); pinfo[i].size = BYTES2INT32(ptr, 12); @@ -106,6 +114,9 @@ /* not handled yet */ } } + + sectorcache_unlock(§or, 0, 0); + return pinfo; } @@ -172,6 +183,7 @@ mutex_lock(&disk_mutex); #endif + sectorcache_invalidate(IF_MD(drive)); volume = get_free_volume(); pinfo = disk_init(IF_MD(drive)); Index: firmware/common/file.c =================================================================== --- firmware/common/file.c (revision 27026) +++ firmware/common/file.c (working copy) @@ -28,7 +28,9 @@ #include "dircache.h" #include "filefuncs.h" #include "system.h" +#include "sectorcache.h" + /* These functions provide a roughly POSIX-compatible file IO API. @@ -39,7 +41,7 @@ */ struct filedesc { - unsigned char cache[SECTOR_SIZE]; + struct sectorcache_entry* cache; int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */ long fileoffset; long size; @@ -47,14 +49,11 @@ struct fat_file fatfile; bool busy; bool write; - bool dirty; bool trunc; }; static struct filedesc openfiles[MAX_OPEN_FILES]; -static int flush_cache(int fd); - int file_creat(const char *pathname) { return open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666); @@ -259,6 +258,8 @@ #endif } + if (file->cache) sectorcache_unlock(&file->cache, 1, 0); + file->busy = false; return 0; } @@ -280,9 +281,9 @@ } if (file->write) { /* flush sector cache */ - if ( file->dirty ) { - rc = flush_cache(fd); - if (rc < 0) + if ( file->cache ) { + rc = sectorcache_clean(file->cache); + if (rc) { /* when failing, try to close the file anyway */ fat_closewrite(&(file->fatfile), file->size, file->attr); @@ -465,37 +466,11 @@ return 0; } -static int flush_cache(int fd) -{ - int rc; - struct filedesc* file = &openfiles[fd]; - long sector = file->fileoffset / SECTOR_SIZE; - - DEBUGF("Flushing dirty sector cache\n"); - - /* make sure we are on correct sector */ - rc = fat_seek(&(file->fatfile), sector); - if ( rc < 0 ) - return rc * 10 - 3; - - rc = fat_readwrite(&(file->fatfile), 1, file->cache, true ); - - if ( rc < 0 ) { - if(file->fatfile.eof) - errno = ENOSPC; - - return rc * 10 - 2; - } - - file->dirty = false; - - return 0; -} - static int readwrite(int fd, void* buf, long count, bool write) { long sectors; long nread=0; + unsigned long secnum; struct filedesc* file; int rc; @@ -511,6 +486,11 @@ return -1; } + if ( !file->write && write ) { + errno = EINVAL; + return -1; + } + if(file->attr & FAT_ATTR_DIRECTORY) { errno = EISDIR; return -1; @@ -529,20 +509,21 @@ int headbytes = MIN(count, SECTOR_SIZE - offs); if (write) { - memcpy( file->cache + offs, buf, headbytes ); - file->dirty = true; + memcpy( file->cache->buffer + offs, buf, headbytes ); + sectorcache_markdirty(file->cache); } else { - memcpy( buf, file->cache + offs, headbytes ); + memcpy( buf, file->cache->buffer + offs, headbytes ); } if (offs + headbytes == SECTOR_SIZE) { - if (file->dirty) { - rc = flush_cache(fd); - if ( rc < 0 ) { + if (file->cache) { + rc = sectorcache_clean(file->cache); + if (rc) { errno = EIO; return rc * 10 - 2; } + sectorcache_unlock(&file->cache, 1, 0); } file->cacheoffset = -1; } @@ -604,58 +585,64 @@ /* any tail bytes? */ if ( count ) { if (write) { - if ( file->fileoffset + nread < file->size ) { - /* sector is only partially filled. copy-back from disk */ - LDEBUGF("Copy-back tail cache\n"); - rc = fat_readwrite(&(file->fatfile), 1, file->cache, false ); - if ( rc < 0 ) { - DEBUGF("Failed writing\n"); - errno = EIO; - file->fileoffset += nread; - file->cacheoffset = -1; - /* adjust file size to length written */ - if ( file->fileoffset > file->size ) - { - file->size = file->fileoffset; + /* sector is only partially filled. copy-back from disk */ + LDEBUGF("Copy-back tail cache\n"); + secnum = fat_get_next_sector(&file->fatfile, true); + if (!secnum) + { + DEBUGF("Out of disk space\n"); + errno = ENOSPC; + file->fileoffset += nread; + file->cacheoffset = -1; + /* adjust file size to length written */ + if ( file->fileoffset > file->size ) + { + file->size = file->fileoffset; #ifdef HAVE_DIRCACHE - dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); + dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); #endif - } - return nread ? nread : rc * 10 - 5; } - /* seek back one sector to put file position right */ - rc = fat_seek(&(file->fatfile), - (file->fileoffset + nread) / - SECTOR_SIZE); - if ( rc < 0 ) { - DEBUGF("fat_seek() failed\n"); - errno = EIO; - file->fileoffset += nread; - file->cacheoffset = -1; - /* adjust file size to length written */ - if ( file->fileoffset > file->size ) - { - file->size = file->fileoffset; + return nread ? nread : -6; + } + rc = sectorcache_lock(IF_MD2(file->fatfile.drive,) secnum, &file->cache, 1); + if (rc) { + DEBUGF("Failed writing\n"); + errno = EIO; + file->fileoffset += nread; + file->cacheoffset = -1; + /* adjust file size to length written */ + if ( file->fileoffset > file->size ) + { + file->size = file->fileoffset; #ifdef HAVE_DIRCACHE - dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); + dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); #endif - } - return nread ? nread : rc * 10 - 6; } + return nread ? nread : rc * 10 - 5; } - memcpy( file->cache, (unsigned char*)buf + nread, count ); - file->dirty = true; + memcpy( file->cache->buffer, (unsigned char*)buf + nread, count ); + sectorcache_markdirty(file->cache); } else { - rc = fat_readwrite(&(file->fatfile), 1, file->cache,false); - if (rc < 1 ) { + secnum = fat_get_next_sector(&file->fatfile, false); + if (!secnum) + { + DEBUGF("EOF while caching tail sector\n"); + errno = EIO; + file->fileoffset += nread; + file->cacheoffset = -1; + return nread ? nread : -8; + } + rc = sectorcache_lock(IF_MD2(file->fatfile.drive,) + secnum, &file->cache, file->write); + if (rc) { DEBUGF("Failed caching sector\n"); errno = EIO; file->fileoffset += nread; file->cacheoffset = -1; return nread ? nread : rc * 10 - 7; } - memcpy( (unsigned char*)buf + nread, file->cache, count ); + memcpy( (unsigned char*)buf + nread, file->cache->buffer, count ); } nread += count; @@ -697,6 +684,7 @@ off_t pos; long newsector; long oldsector; + unsigned long secnum; int sectoroffset; int rc; struct filedesc* file = &openfiles[fd]; @@ -743,10 +731,11 @@ ((file->cacheoffset==-1) && sectoroffset) ) { if ( newsector != oldsector ) { - if (file->dirty) { - rc = flush_cache(fd); - if (rc < 0) + if (file->cache) { + rc = sectorcache_clean(file->cache); + if (rc) return rc * 10 - 5; + sectorcache_unlock(&file->cache, 1, 0); } rc = fat_seek(&(file->fatfile), newsector); @@ -756,8 +745,10 @@ } } if ( sectoroffset ) { - rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false); - if ( rc < 0 ) { + secnum = fat_get_next_sector(&(file->fatfile), false); + if (!secnum) return -7; + rc = sectorcache_lock(IF_MD2(file->fatfile.drive,) secnum, &file->cache, file->write); + if (rc) { errno = EIO; return rc * 10 - 6; } Index: firmware/common/sectorcache.c =================================================================== --- firmware/common/sectorcache.c (revision 0) +++ firmware/common/sectorcache.c (revision 0) @@ -0,0 +1,418 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Michael Sparmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "sectorcache.h" +#include "mv.h" +#include "panic.h" +#include "string.h" +#include "kernel.h" +#include "storage.h" +#include "system.h" +#include "ata_idle_notify.h" + +#define SECTORCACHE_SIZE 64 + +static struct mutex sectorcache_mutex SHAREDBSS_ATTR; +static struct wakeup sectorcache_block SHAREDBSS_ATTR; +static int sectorcache_replace_idx; +static struct sectorcache_entry sectorcache_entries[SECTORCACHE_SIZE]; +static unsigned char sectorcache_buffers[SECTORCACHE_SIZE][SECTOR_SIZE] STORAGE_ALIGN_ATTR; + +void sectorcache_init(void) +{ + int i; + sectorcache_replace_idx = 0; + memset(sectorcache_entries, 0, sizeof(sectorcache_entries)); + mutex_init(§orcache_mutex); + wakeup_init(§orcache_block); + for (i = 0; i < SECTORCACHE_SIZE; i++) + sectorcache_entries[i].buffer = sectorcache_buffers[i]; +} + +/* Blocks the current thread until a buffer becomes free, unlocked or consistent, + up to the specified number of ticks. Use TIMEOUT_BLOCK to block forever. + Please note that this will release the mutex while blocking! */ +static void sectorcache_wait_block(int timeout) +{ + mutex_unlock(§orcache_mutex); + wakeup_wait(§orcache_block, timeout); + mutex_lock(§orcache_mutex); +} + +/* Calls sectorcache_clean on the handle if neccessary + and panics if an error is hit */ +static void sectorcache_clean_safe(struct sectorcache_entry* handle) +{ + int rc; + if (!handle->valid) return; + if (!handle->dirty) return; + if (!handle->consistent) + panicf("Dirty, unused but inconsistent sector in the cache"); + if ((rc = sectorcache_clean(handle))) + panicf("Error while writing back dirty sector from the cache: rc = %d", rc); +} + +/* Tries to find a valid buffer for the specified sector. + Returns its handle, or null if it couldn't find one. + Please note that this doesn't check whether that buffer is locked. + The mutex must have been locked by the caller before calling this. */ +static struct sectorcache_entry* sectorcache_find_entry(IF_MD2(int drive,) unsigned long sector) +{ + int i; + for (i = 0; i < SECTORCACHE_SIZE; i++) + if (sectorcache_entries[i].valid && sectorcache_entries[i].sector == sector) +#ifdef HAVE_MULTIDRIVE + if (sectorcache_entries[i].drive == drive) +#endif + return §orcache_entries[i]; + return (struct sectorcache_entry*)0; +} + +/* Tries to find a reclaimable buffer. + Returns its handle, or null if it couldn't find one. + Please note that this doesn't mark the buffer as used and doesn't + check whether that buffer needs to be written back first. + The mutex must have been locked by the caller before calling this. */ +static struct sectorcache_entry* sectorcache_find_free(void) +{ + int i, idx; + sectorcache_replace_idx++; + for (i = 0; i < SECTORCACHE_SIZE; i++) + { + idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE; + if (!sectorcache_entries[idx].valid) + return §orcache_entries[idx]; + } + for (i = 0; i < SECTORCACHE_SIZE; i++) + { + idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE; + if (sectorcache_entries[idx].free) + return §orcache_entries[idx]; + } + for (i = 0; i < SECTORCACHE_SIZE; i++) + { + idx = (sectorcache_replace_idx + i) % SECTORCACHE_SIZE; + if (!sectorcache_entries[idx].locks) + return §orcache_entries[idx]; + } + return (struct sectorcache_entry*)0; +} + +/* Read sectors from a storage device. If all requested sectors are present + in the cache (and marked as consistent), no read will be issued to the + storage layer. Otherwise, one big read will be issued, and consistent + dirty sectors will be copied to the buffer afterwards + (overwriting the data that was just being read from the device). */ +int sectorcache_readthrough(IF_MD2(int drive,) unsigned long start, int count, void* buf, int keepincache) +{ + unsigned long sector; + unsigned long readstart = 0; + unsigned long readcount = 0; + int readneeded = 0; + void* readbuf = (void*)0; + int rc = 0; + int hasdirty = 0; + struct sectorcache_entry* handle; +#ifdef STORAGE_NEEDS_ALIGN + if ((uintptr_t)buf & (CACHEALIGN_SIZE - 1)) + { + for (sector = start; sector < start + count; sector++) + { + rc = sectorcache_lock(IF_MD2(int drive,) sector, &handle, 0); + if (rc) return rc; + memcpy((void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), + handle->buffer, SECTOR_SIZE); + sectorcache_unlock(&handle, 0, keepincache); + } + return 0; + } +#endif + mutex_lock(§orcache_mutex); + for (sector = start; sector < start + count; sector++) + { + handle = sectorcache_find_entry(IF_MD2(drive,) sector); + if (handle && handle->consistent) + { + memcpy((void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), + handle->buffer, SECTOR_SIZE); + hasdirty |= handle->dirty; + } + else if (!readneeded) + { + readstart = sector; + readcount = 1; + readneeded = 1; + readbuf = (void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE); + } + else readcount = sector - readstart + 1; + } + if (readneeded) + { + rc = storage_read_sectors(IF_MD2(drive,) readstart, readcount, readbuf); + if (!rc && (hasdirty || keepincache)) + for (sector = readstart; sector < readstart + readcount; sector++) + { + handle = sectorcache_find_entry(IF_MD2(drive,) sector); + if (handle && handle->consistent && handle->dirty) + memcpy((void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), + handle->buffer, SECTOR_SIZE); + else if (!handle && keepincache && count < SECTORCACHE_SIZE / 4 + && (handle = sectorcache_find_free())) + { + sectorcache_clean_safe(handle); +#ifdef HAVE_MULTIDRIVE + handle->drive = drive; +#endif + handle->sector = sector; + handle->valid = 1; + handle->consistent = 1; + handle->dirty = 0; + handle->free = 0; + memcpy(handle->buffer, + (void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), SECTOR_SIZE); + } + } + } + mutex_unlock(§orcache_mutex); + return rc; +} + +/* Writes sectors to a storage device. The data might be cached for further + read accesses, but is always guaranteed to be written to the storage device + immediately. If there are open handles to those sectors, this will panic. */ +int sectorcache_writethrough(IF_MD2(int drive,) unsigned long start, int count, const void* buf, int keepincache) +{ + int rc; + unsigned long sector; + struct sectorcache_entry* handle; +#ifdef STORAGE_NEEDS_ALIGN + if ((uintptr_t)buf & (CACHEALIGN_SIZE - 1)) + { + for (sector = start; sector < start + count; sector++) + { + rc = sectorcache_lock(IF_MD2(int drive,) sector, &handle, 1); + if (rc) return rc; + memcpy(handle->buffer, + (void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), SECTOR_SIZE); + sectorcache_markdirty(handle); + rc = sectorcache_clean(handle); + if (rc) return rc; + sectorcache_unlock(&handle, 1, keepincache); + } + return 0; + } +#endif + mutex_lock(§orcache_mutex); + for (sector = start; sector < start + count; sector++) + { + if ((handle = sectorcache_find_entry(IF_MD2(drive,) sector))) + { + if (handle->locks) + panicf("Trying to writethrough to a sector that " + "has open sector cache handles!"); + handle->dirty = 0; + handle->consistent = 1; + handle->free = !keepincache; + memcpy(handle->buffer, + (void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), SECTOR_SIZE); + } + else if (keepincache && count < SECTORCACHE_SIZE / 4 + && (handle = sectorcache_find_free())) + { + sectorcache_clean_safe(handle); +#ifdef HAVE_MULTIDRIVE + handle->drive = drive; +#endif + handle->sector = sector; + handle->valid = 1; + handle->consistent = 1; + handle->dirty = 0; + handle->free = 0; + memcpy(handle->buffer, + (void*)((uintptr_t)buf + (sector - start) * SECTOR_SIZE), SECTOR_SIZE); + } + } + rc = storage_write_sectors(IF_MD2(drive,) start, count, buf); + if (rc) + for (sector = start; sector < start + count; sector++) + if ((handle = sectorcache_find_entry(IF_MD2(drive,) sector))) + handle->valid = 0; + mutex_unlock(§orcache_mutex); + return rc; +} + +/* Get a handle for a sector. If write access is desired, exclusive access + *must* be requested. Will block until there is buffer space available. */ +int sectorcache_lock(IF_MD2(int drive,) unsigned long sector, struct sectorcache_entry** handle, int exclusive) +{ + int rc = 0; + int retry = 1; + mutex_lock(§orcache_mutex); + if ((*handle = sectorcache_find_entry(IF_MD2(drive,) sector))) + { + if (((*handle)->locks && exclusive) || (*handle)->exclusive) + panicf("Sector cache exclusive access collision " + "(oldexcl = %d, newexcl = %d, sector = %lu)", + (*handle)->exclusive, exclusive, sector); + } + else + { + while (retry) + { + retry = 0; + if (!(*handle = sectorcache_find_free())) + { + sectorcache_wait_block(TIMEOUT_BLOCK); + retry = 1; + } + } + sectorcache_clean_safe(*handle); +#ifdef HAVE_MULTIDRIVE + (*handle)->drive = drive; +#endif + (*handle)->sector = sector; + rc = storage_read_sectors(IF_MD2(drive,) sector, 1, (*handle)->buffer); + } + (*handle)->exclusive = exclusive; + (*handle)->locks++; + (*handle)->free = 0; + (*handle)->consistent = 1; + (*handle)->dirty = 0; + (*handle)->valid = !rc; + if (rc) *handle = (struct sectorcache_entry*)0; + mutex_unlock(§orcache_mutex); + return rc; +} + +/* Unlock a sector and clean it if requested. + The sector will be marked as consistent. */ +void sectorcache_unlock(struct sectorcache_entry** handle, int clean, int keepincache) +{ + int rc = 0; + mutex_lock(§orcache_mutex); + if (clean) rc = sectorcache_clean(*handle); + if (rc) panicf("Error while writing back dirty sector from the cache " + "while releasing handle: rc = %d", rc); + (*handle)->consistent = 1; + (*handle)->locks--; + (*handle)->exclusive = 0; + (*handle)->free = !keepincache; + *handle = (struct sectorcache_entry*)0; + mutex_unlock(§orcache_mutex); + wakeup_signal(§orcache_block); +} + +/* Indicate that the buffer will need to be written back before it can be reclaimed */ +void sectorcache_markdirty(struct sectorcache_entry* handle) +{ + mutex_lock(§orcache_mutex); + handle->dirty = 1; + mutex_unlock(§orcache_mutex); +} + +/* Indicate that the buffer may not be written back until it has been + marked as consistent again. (Prevents syncing during inactivity) + Release this as soon as possible. */ +void sectorcache_markinconsistent(struct sectorcache_entry* handle) +{ + mutex_lock(§orcache_mutex); + handle->consistent = 0; + mutex_unlock(§orcache_mutex); +} + +/* Indicate that the buffer may not be written back again. */ +void sectorcache_markconsistent(struct sectorcache_entry* handle) +{ + mutex_lock(§orcache_mutex); + handle->consistent = 1; + mutex_unlock(§orcache_mutex); + wakeup_signal(§orcache_block); +} + +/* Write back the sector. Will mark it as consistent. + Make sure it is marked as consistent before calling this internally. */ +int sectorcache_clean(struct sectorcache_entry* handle) +{ + int rc = 0; + mutex_lock(§orcache_mutex); + handle->consistent = 1; + if (handle->dirty) + rc = storage_write_sectors(IF_MD2(handle->drive,) handle->sector, 1, handle->buffer); + if (!rc) handle->dirty = 0; + mutex_unlock(§orcache_mutex); + wakeup_signal(§orcache_block); + return rc; +} + +/* Write back everything that's dirty, + and flush the storage layer if wanted. + This may block for several seconds! */ +void sectorcache_clean_all(int flushstorage) +{ + int i; + int retry = 1; + int rc; + call_storage_idle_notifys(true); + mutex_lock(§orcache_mutex); + while (retry) + { + retry = 0; + for (i = 0; i < SECTORCACHE_SIZE; i++) + if (sectorcache_entries[i].dirty) + { + if (!sectorcache_entries[i].consistent) + { + sectorcache_wait_block(TIMEOUT_BLOCK); + retry = 1; + } + if ((rc = sectorcache_clean(§orcache_entries[i]))) + panicf("I/O error while cleaning sector cache: rc = %d", rc); + } + } +#ifdef HAVE_STORAGE_FLUSH + if (flushstorage) + if ((rc = storage_flush())) + panicf("Flushing storage layer failed while " + "cleaning sector cache: rc = %d", rc); +#endif + mutex_unlock(§orcache_mutex); +} + +/* Invalidate the whole cache for the drive, used by hotplug and USB. + Will panic if there are still handles open for this drive. */ +void sectorcache_invalidate(IF_MD_NONVOID(int drive)) +{ + int i; + for (i = 0; i < SECTORCACHE_SIZE; i++) +#ifdef HAVE_MULTIDRIVE + if (sectorcache_entries[i].drive == drive) +#endif + { + if (sectorcache_entries[i].locks || sectorcache_entries[i].dirty) + panicf("Open handles or dirty sectors found while " + "invalidating sector cache! i=%d, sector=%u, " + "locks=%d, dirty=%d, consistent=%d", i, + (unsigned int)sectorcache_entries[i].sector, + sectorcache_entries[i].locks, sectorcache_entries[i].dirty, + sectorcache_entries[i].consistent); + else sectorcache_entries[i].valid = 0; + } +} Index: firmware/drivers/fat.c =================================================================== --- firmware/drivers/fat.c (revision 27026) +++ firmware/drivers/fat.c (working copy) @@ -25,6 +25,7 @@ #include #include "fat.h" #include "storage.h" +#include "sectorcache.h" #include "debug.h" #include "panic.h" #include "system.h" @@ -198,7 +199,7 @@ static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned long start, long count, char* buf, bool write ); -#define FAT_CACHE_SIZE 0x20 +#define FAT_CACHE_SIZE 8 #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) struct fat_cache_entry @@ -211,7 +212,7 @@ #endif }; -static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE]; +static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE] STORAGE_ALIGN_ATTR; static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; static struct mutex cache_mutex SHAREDBSS_ATTR; @@ -261,6 +262,109 @@ *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); } +static void flush_fat_sector(struct fat_cache_entry *fce, + unsigned char *sectorbuf) +{ + int rc; + long secnum; + + /* With multivolume, use only the FAT info from the cached sector! */ +#ifdef HAVE_MULTIVOLUME + secnum = fce->secnum + fce->fat_vol->startsector; +#else + secnum = fce->secnum + fat_bpbs[0].startsector; +#endif + + /* Write to the first FAT */ + rc = sectorcache_writethrough(IF_MD2(fce->fat_vol->drive,) + secnum, 1, + sectorbuf, 1); + if(rc < 0) + { + panicf("flush_fat_sector() - Could not write sector %ld" + " (error %d)\n", + secnum, rc); + } +#ifdef HAVE_MULTIVOLUME + if(fce->fat_vol->bpb_numfats > 1) +#else + if(fat_bpbs[0].bpb_numfats > 1) +#endif + { + /* Write to the second FAT */ +#ifdef HAVE_MULTIVOLUME + secnum += fce->fat_vol->fatsize; +#else + secnum += fat_bpbs[0].fatsize; +#endif + rc = sectorcache_writethrough(IF_MD2(fce->fat_vol->drive,) + secnum, 1, sectorbuf, 1); + if(rc < 0) + { + panicf("flush_fat_sector() - Could not write sector %ld" + " (error %d)\n", + secnum, rc); + } + } + fce->dirty = false; +} + +/* Note: The returned pointer is only safely valid until the next + task switch! (Any subsequent ata read/write may yield.) */ +static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) + long fatsector, bool dirty) +{ +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; + int cache_index = secnum & FAT_CACHE_MASK; + struct fat_cache_entry *fce = &fat_cache[cache_index]; + unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; + int rc; + + mutex_lock(&cache_mutex); /* make changes atomic */ + + /* Delete the cache entry if it isn't the sector we want */ + if(fce->inuse && (fce->secnum != secnum +#ifdef HAVE_MULTIVOLUME + || fce->fat_vol != fat_bpb +#endif + )) + { + /* Write back if it is dirty */ + if(fce->dirty) + { + flush_fat_sector(fce, sectorbuf); + } + fce->inuse = false; + } + + /* Load the sector if it is not cached */ + if(!fce->inuse) + { + rc = sectorcache_readthrough(IF_MD2(fat_bpb->drive,) + secnum + fat_bpb->startsector,1, + sectorbuf, 0); + if(rc < 0) + { + DEBUGF( "cache_fat_sector() - Could not read sector %ld" + " (error %d)\n", secnum, rc); + mutex_unlock(&cache_mutex); + return NULL; + } + fce->inuse = true; + fce->secnum = secnum; +#ifdef HAVE_MULTIVOLUME + fce->fat_vol = fat_bpb; +#endif + } + if (dirty) + fce->dirty = true; /* dirt remains, sticky until flushed */ + mutex_unlock(&cache_mutex); + return sectorbuf; +} + void fat_init(void) { unsigned int i; @@ -302,7 +406,7 @@ const int volume = 0; #endif struct bpb* fat_bpb = &fat_bpbs[volume]; - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; int rc; int secmult; long datasec; @@ -310,8 +414,8 @@ int rootdirsectors; #endif - /* Read the sector */ - rc = storage_read_sectors(IF_MD2(drive,) startsector,1,buf); + /* Read the boot sector */ + rc = sectorcache_lock(IF_MD2(drive,) startsector, §orbuf, 0); if(rc) { DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc); @@ -324,19 +428,19 @@ fat_bpb->drive = drive; #endif - fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); + fat_bpb->bpb_bytspersec = BYTES2INT16(sectorbuf->buffer, BPB_BYTSPERSEC); secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; /* Sanity check is performed later */ - fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; - fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT); - fat_bpb->bpb_numfats = buf[BPB_NUMFATS]; - fat_bpb->bpb_media = buf[BPB_MEDIA]; - fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16); - fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32); - fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16); - fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32); - fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD); + fat_bpb->bpb_secperclus = secmult * sectorbuf->buffer[BPB_SECPERCLUS]; + fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(sectorbuf->buffer,BPB_RSVDSECCNT); + fat_bpb->bpb_numfats = sectorbuf->buffer[BPB_NUMFATS]; + fat_bpb->bpb_media = sectorbuf->buffer[BPB_MEDIA]; + fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(sectorbuf->buffer,BPB_FATSZ16); + fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(sectorbuf->buffer,BPB_FATSZ32); + fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(sectorbuf->buffer,BPB_TOTSEC16); + fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(sectorbuf->buffer,BPB_TOTSEC32); + fat_bpb->last_word = BYTES2INT16(sectorbuf->buffer,BPB_LAST_WORD); /* calculate a few commonly used values */ if (fat_bpb->bpb_fatsz16 != 0) @@ -350,9 +454,15 @@ fat_bpb->totalsectors = fat_bpb->bpb_totsec32; #ifdef HAVE_FAT16SUPPORT - fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); + fat_bpb->bpb_rootentcnt = BYTES2INT16(sectorbuf->buffer, BPB_ROOTENTCNT); +#endif + +#ifdef HAVE_FAT16SUPPORT if (!fat_bpb->bpb_bytspersec) + { + sectorcache_unlock(§orbuf, 0, 0); return -2; + } rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); #endif /* #ifdef HAVE_FAT16SUPPORT */ @@ -368,7 +478,10 @@ if (fat_bpb->bpb_secperclus) fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; else - return -2; + { + sectorcache_unlock(§orbuf, 0, 0); + return -2; + } #ifdef TEST_FAT /* @@ -385,10 +498,12 @@ if (fat_bpb->dataclusters < 4085) { /* FAT12 */ DEBUGF("This is FAT12. Go away!\n"); + sectorcache_unlock(§orbuf, 0, 0); return -2; } #else /* #ifdef HAVE_FAT16SUPPORT */ DEBUGF("This is not FAT32. Go away!\n"); + sectorcache_unlock(§orbuf, 0, 0); return -2; #endif /* #ifndef HAVE_FAT16SUPPORT */ } @@ -410,11 +525,13 @@ else #endif /* #ifdef HAVE_FAT16SUPPORT */ { /* FAT32 specific part of BPB */ - fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS); - fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO); + fat_bpb->bpb_rootclus = BYTES2INT32(sectorbuf->buffer,BPB_ROOTCLUS); + fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(sectorbuf->buffer,BPB_FSINFO); fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) fat_bpb->bpb_rootclus); } + + sectorcache_unlock(§orbuf, 0, 0); rc = bpb_is_sane(IF_MV(fat_bpb)); if (rc < 0) @@ -433,15 +550,18 @@ #endif /* #ifdef HAVE_FAT16SUPPORT */ { /* Read the fsinfo sector */ - rc = storage_read_sectors(IF_MD2(drive,) - startsector + fat_bpb->bpb_fsinfo, 1, buf); + + rc = sectorcache_lock(IF_MD2(drive,) + startsector + fat_bpb->bpb_fsinfo, §orbuf, 0); if (rc < 0) { DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc); return rc * 10 - 4; } - fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); - fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); + fat_bpb->fsinfo.freecount = BYTES2INT32(sectorbuf->buffer, FSINFO_FREECOUNT); + fat_bpb->fsinfo.nextfree = BYTES2INT32(sectorbuf->buffer, FSINFO_NEXTFREE); + + sectorcache_unlock(§orbuf, 0, 0); } /* calculate freecount if unset */ @@ -604,109 +724,6 @@ return 0; } -static void flush_fat_sector(struct fat_cache_entry *fce, - unsigned char *sectorbuf) -{ - int rc; - long secnum; - - /* With multivolume, use only the FAT info from the cached sector! */ -#ifdef HAVE_MULTIVOLUME - secnum = fce->secnum + fce->fat_vol->startsector; -#else - secnum = fce->secnum + fat_bpbs[0].startsector; -#endif - - /* Write to the first FAT */ - rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) - secnum, 1, - sectorbuf); - if(rc < 0) - { - panicf("flush_fat_sector() - Could not write sector %ld" - " (error %d)\n", - secnum, rc); - } -#ifdef HAVE_MULTIVOLUME - if(fce->fat_vol->bpb_numfats > 1) -#else - if(fat_bpbs[0].bpb_numfats > 1) -#endif - { - /* Write to the second FAT */ -#ifdef HAVE_MULTIVOLUME - secnum += fce->fat_vol->fatsize; -#else - secnum += fat_bpbs[0].fatsize; -#endif - rc = storage_write_sectors(IF_MD2(fce->fat_vol->drive,) - secnum, 1, sectorbuf); - if(rc < 0) - { - panicf("flush_fat_sector() - Could not write sector %ld" - " (error %d)\n", - secnum, rc); - } - } - fce->dirty = false; -} - -/* Note: The returned pointer is only safely valid until the next - task switch! (Any subsequent ata read/write may yield.) */ -static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) - long fatsector, bool dirty) -{ -#ifndef HAVE_MULTIVOLUME - struct bpb* fat_bpb = &fat_bpbs[0]; -#endif - long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; - int cache_index = secnum & FAT_CACHE_MASK; - struct fat_cache_entry *fce = &fat_cache[cache_index]; - unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; - int rc; - - mutex_lock(&cache_mutex); /* make changes atomic */ - - /* Delete the cache entry if it isn't the sector we want */ - if(fce->inuse && (fce->secnum != secnum -#ifdef HAVE_MULTIVOLUME - || fce->fat_vol != fat_bpb -#endif - )) - { - /* Write back if it is dirty */ - if(fce->dirty) - { - flush_fat_sector(fce, sectorbuf); - } - fce->inuse = false; - } - - /* Load the sector if it is not cached */ - if(!fce->inuse) - { - rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) - secnum + fat_bpb->startsector,1, - sectorbuf); - if(rc < 0) - { - DEBUGF( "cache_fat_sector() - Could not read sector %ld" - " (error %d)\n", secnum, rc); - mutex_unlock(&cache_mutex); - return NULL; - } - fce->inuse = true; - fce->secnum = secnum; -#ifdef HAVE_MULTIVOLUME - fce->fat_vol = fat_bpb; -#endif - } - if (dirty) - fce->dirty = true; /* dirt remains, sticky until flushed */ - mutex_unlock(&cache_mutex); - return sectorbuf; -} - static unsigned long find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned long startcluster) { @@ -934,7 +951,7 @@ #ifndef HAVE_MULTIVOLUME struct bpb* fat_bpb = &fat_bpbs[0]; #endif - unsigned char fsinfo[SECTOR_SIZE]; + struct sectorcache_entry* fsinfo; unsigned long* intptr; int rc; @@ -944,27 +961,29 @@ #endif /* #ifdef HAVE_FAT16SUPPORT */ /* update fsinfo */ - rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) - fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); + rc = sectorcache_lock(IF_MD2(fat_bpb->drive,) + fat_bpb->startsector + fat_bpb->bpb_fsinfo, &fsinfo, 1); if (rc < 0) { DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc); return rc * 10 - 1; } - intptr = (long*)&(fsinfo[FSINFO_FREECOUNT]); + intptr = (long*)&(fsinfo->buffer[FSINFO_FREECOUNT]); *intptr = htole32(fat_bpb->fsinfo.freecount); - intptr = (long*)&(fsinfo[FSINFO_NEXTFREE]); + intptr = (long*)&(fsinfo->buffer[FSINFO_NEXTFREE]); *intptr = htole32(fat_bpb->fsinfo.nextfree); - rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) - fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); + sectorcache_markdirty(fsinfo); + rc = sectorcache_clean(fsinfo); if (rc < 0) { DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc); return rc * 10 - 2; } + sectorcache_unlock(&fsinfo, 1, 1); + return 0; } @@ -1103,7 +1122,8 @@ const unsigned char* shortname, bool is_directory) { - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; + unsigned long secnum; unsigned char* entry; unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; @@ -1112,6 +1132,7 @@ unsigned int nameidx=0, namelen = utf8length(name); int rc; unsigned short name_utf16[namelen + 1]; + bool appended; LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n", file->firstcluster, firstentry, numentries, name); @@ -1120,9 +1141,10 @@ if (rc<0) return rc * 10 - 1; - rc = fat_readwrite(file, 1, buf, false); - if (rc<1) - return rc * 10 - 2; + secnum = fat_get_next_sector(file, false); + if (!secnum) return -3; + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) return rc * 10 - 2; /* calculate shortname checksum */ for (i=11; i>0; i--) @@ -1143,29 +1165,33 @@ /* new sector? */ if ( idx >= DIR_ENTRIES_PER_SECTOR ) { /* update current sector */ - rc = fat_seek(file, sector); - if (rc<0) - return rc * 10 - 3; + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 4; + sectorcache_unlock(§orbuf, 1, 1); - rc = fat_readwrite(file, 1, buf, true); - if (rc<1) - return rc * 10 - 4; - /* read next sector */ - rc = fat_readwrite(file, 1, buf, false); - if (rc<0) { + appended = false; + secnum = fat_get_next_sector(file, false); + if (!secnum) /* end of dir */ + { + secnum = fat_get_next_sector(file, true); + if (!secnum) return -6; + appended = true; + } + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) + { LDEBUGF("Failed writing new sector: %d\n",rc); return rc * 10 - 5; } - if (rc==0) - /* end of dir */ - memset(buf, 0, sizeof buf); + if (appended) memset(sectorbuf->buffer, 0, SECTOR_SIZE); sector++; idx = 0; } - entry = buf + idx * DIR_ENTRY_SIZE; + entry = sectorbuf->buffer + idx * DIR_ENTRY_SIZE; /* verify this entry is free */ if (entry[0] && entry[0] != 0xe5 ) @@ -1230,14 +1256,11 @@ } /* update last sector */ - rc = fat_seek(file, sector); - if (rc<0) - return rc * 10 - 6; + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 7; + sectorcache_unlock(§orbuf, 1, 1); - rc = fat_readwrite(file, 1, buf, true); - if (rc<1) - return rc * 10 - 7; - return 0; } @@ -1274,7 +1297,8 @@ #else struct bpb* fat_bpb = &fat_bpbs[0]; #endif - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf = (struct sectorcache_entry*)0; + unsigned long secnum; unsigned char shortname[12]; int rc; unsigned int sector; @@ -1297,6 +1321,9 @@ #ifdef HAVE_MULTIVOLUME file->volume = dir->file.volume; /* inherit the volume, to make sure */ #endif +#ifdef HAVE_MULTIDRIVE + file->drive = fat_bpb->drive; +#endif /* The "." and ".." directory entries must not be long names */ if(dotdir) { @@ -1327,22 +1354,25 @@ { unsigned int i; - rc = fat_readwrite(&dir->file, 1, buf, false); - if (rc < 0) { + secnum = fat_get_next_sector(&dir->file, false); + if (!secnum) { /* current end of dir reached */ + LDEBUGF("End of dir on cluster boundary\n"); + break; + } + + if (sectorbuf) sectorcache_unlock(§orbuf, 1, 1); + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) { DEBUGF( "add_dir_entry() - Couldn't read dir" " (error code %d)\n", rc); return rc * 10 - 3; } - if (rc == 0) { /* current end of dir reached */ - LDEBUGF("End of dir on cluster boundary\n"); - break; - } /* look for free slots */ for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) { - switch (buf[i * DIR_ENTRY_SIZE]) { + switch (sectorbuf->buffer[i * DIR_ENTRY_SIZE]) { case 0: entries_found += DIR_ENTRIES_PER_SECTOR - i; LDEBUGF("Found end of dir %d\n", @@ -1362,7 +1392,7 @@ entries_found = 0; /* check that our intended shortname doesn't already exist */ - if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) { + if (!strncmp(shortname, sectorbuf->buffer + i * DIR_ENTRY_SIZE, 11)) { /* shortname exists already, make a new one */ randomize_dos_name(shortname); LDEBUGF("Duplicate shortname, changing to %s\n", @@ -1378,15 +1408,12 @@ - entries_found; } } + if (sectorbuf) sectorcache_unlock(§orbuf, 1, 1); /* step 2: extend the dir if necessary */ if (firstentry < 0) { LDEBUGF("Adding new sector(s) to dir\n"); - rc = fat_seek(&dir->file, sector); - if (rc < 0) - return rc * 10 - 4; - memset(buf, 0, sizeof buf); /* we must clear whole clusters */ for (; (entries_found < entries_needed) || @@ -1395,9 +1422,15 @@ if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) return -5; /* dir too large -- FAT specification */ - rc = fat_readwrite(&dir->file, 1, buf, true); - if (rc < 1) /* No more room or something went wrong */ - return rc * 10 - 6; + secnum = fat_get_next_sector(&dir->file, true); + if (!secnum) return rc * 10 - 4; + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) return rc * 10 - 5; + memset(sectorbuf->buffer, 0, SECTOR_SIZE); + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 6; + sectorcache_unlock(§orbuf, 1, 1); entries_found += DIR_ENTRIES_PER_SECTOR; } @@ -1531,10 +1564,10 @@ static int update_short_entry( struct fat_file* file, long size, int attr ) { - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; + unsigned long secnum; int sector = file->direntry / DIR_ENTRIES_PER_SECTOR; - unsigned char* entry = - buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); + unsigned char* entry; unsigned long* sizeptr; unsigned short* clusptr; struct fat_file dir; @@ -1552,10 +1585,16 @@ if (rc<0) return rc * 10 - 2; - rc = fat_readwrite(&dir, 1, buf, false); - if (rc < 1) - return rc * 10 - 3; + secnum = fat_get_next_sector(&dir, true); + if (!secnum) + return -3; + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) + return 10 * rc - 4; + + entry = sectorbuf->buffer + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); + if (!entry[0] || entry[0] == 0xe5) panicf("Updating size on empty dir entry %d\n", file->direntry); @@ -1585,14 +1624,13 @@ *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); } - rc = fat_seek( &dir, sector ); - if (rc < 0) - return rc * 10 - 4; - - rc = fat_readwrite(&dir, 1, buf, true); - if (rc < 1) + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 5; + sectorcache_unlock(§orbuf, 1, 1); + return 0; } @@ -1665,6 +1703,9 @@ return -1; } #endif +#ifdef HAVE_MULTIDRIVE + file->drive = fat_bpbs[volume].drive; +#endif LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry); return 0; @@ -1699,7 +1740,7 @@ #else struct bpb* fat_bpb = &fat_bpbs[0]; #endif - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; int i; long sector; int rc; @@ -1724,12 +1765,15 @@ update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); /* Clear the entire cluster */ - memset(buf, 0, sizeof buf); sector = cluster2sec(IF_MV2(fat_bpb,) newdir->file.firstcluster); for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) { - rc = transfer(IF_MV2(fat_bpb,) sector + i, 1, buf, true ); - if (rc < 0) - return rc * 10 - 2; + rc = sectorcache_lock(IF_MD2(fat_bpb->drive,) sector + i, §orbuf, 1); + if (rc) return rc * 10 - 2; + memset(sectorbuf->buffer, 0, SECTOR_SIZE); + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 6; + sectorcache_unlock(§orbuf, 1, 1); } /* Then add the "." entry */ @@ -1835,9 +1879,15 @@ return 0; } +void fat_closedir(struct fat_dir* dir) +{ + if (dir->cache) sectorcache_unlock(&dir->cache, 1, 1); +} + static int free_direntries(struct fat_file* file) { - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; + unsigned long secnum; struct fat_file dir; int numentries = file->direntries; unsigned int entry = file->direntry - numentries + 1; @@ -1854,45 +1904,40 @@ if (rc < 0) return rc * 10 - 2; - rc = fat_readwrite(&dir, 1, buf, false); - if (rc < 1) - return rc * 10 - 3; + secnum = fat_get_next_sector(&dir, false); + if (!secnum) return -3; + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) return rc * 10 - 4; for (i=0; i < numentries; i++) { LDEBUGF("Clearing dir entry %d (%d/%d)\n", entry, i+1, numentries); - buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; + sectorbuf->buffer[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; entry++; if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) { /* flush this sector */ - rc = fat_seek(&dir, sector); - if (rc < 0) - return rc * 10 - 4; + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 5; + sectorcache_unlock(§orbuf, 1, 1); - rc = fat_readwrite(&dir, 1, buf, true); - if (rc < 1) - return rc * 10 - 5; - if ( i+1 < numentries ) { /* read next sector */ - rc = fat_readwrite(&dir, 1, buf, false); - if (rc < 1) - return rc * 10 - 6; + secnum = fat_get_next_sector(&dir, false); + if (!secnum) return -6; + rc = sectorcache_lock(IF_MD2(file->drive,) secnum, §orbuf, 0); + if (rc) return rc * 10 - 7; } - sector++; } } if ( entry % DIR_ENTRIES_PER_SECTOR ) { /* flush this sector */ - rc = fat_seek(&dir, sector); - if (rc < 0) - return rc * 10 - 7; - - rc = fat_readwrite(&dir, 1, buf, true); - if (rc < 1) - return rc * 10 - 8; + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 8; + sectorcache_unlock(§orbuf, 1, 1); } return 0; @@ -1939,7 +1984,8 @@ int rc; struct fat_dir olddir; struct fat_file newfile = *file; - unsigned char buf[SECTOR_SIZE]; + struct sectorcache_entry* sectorbuf; + unsigned long secnum; unsigned char* entry = NULL; unsigned short* clusptr = NULL; unsigned int parentcluster; @@ -1987,7 +2033,7 @@ it points to its parent directory (we don't check if it was a move) */ if(FAT_ATTR_DIRECTORY == attr) { /* open the dir that was renamed, we re-use the olddir struct */ - rc = fat_opendir(IF_MV2(file->volume,) &olddir, newfile.firstcluster, + rc = fat_opendir(IF_MV2(file->drive,) &olddir, newfile.firstcluster, NULL); if (rc < 0) return rc * 10 - 6; @@ -1997,9 +2043,10 @@ if (rc < 0) return rc * 10 - 7; - rc = fat_readwrite(&olddir.file, 1, buf, false); - if (rc < 0) - return rc * 10 - 8; + secnum = fat_get_next_sector(&olddir.file, false); + if (!secnum) return -7; + rc = sectorcache_lock(IF_MD2(file->volume,) secnum, §orbuf, 0); + if (rc) return rc * 10 - 8; /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ if(dir->file.firstcluster == fat_bpb->bpb_rootclus) @@ -2007,7 +2054,7 @@ else parentcluster = dir->file.firstcluster; - entry = buf + DIR_ENTRY_SIZE; + entry = sectorbuf->buffer + DIR_ENTRY_SIZE; if(strncmp(".. ", entry, 11)) { /* .. entry must be second entry according to FAT spec (p.29) */ @@ -2020,14 +2067,10 @@ clusptr = (short*)(entry + FATDIR_FSTCLUSLO); *clusptr = htole16(parentcluster & 0xffff); - /* write back this sector */ - rc = fat_seek(&olddir.file, 0); - if (rc < 0) - return rc * 10 - 7; - - rc = fat_readwrite(&olddir.file, 1, buf, true); - if (rc < 1) - return rc * 10 - 8; + sectorcache_markdirty(sectorbuf); + rc = sectorcache_clean(sectorbuf); + if (rc) return rc * 10 - 8; + sectorcache_unlock(§orbuf, 1, 1); } return 0; @@ -2110,12 +2153,12 @@ if (start + count > fat_bpb->totalsectors) panicf("Write %ld after data\n", start + count - fat_bpb->totalsectors); - rc = storage_write_sectors(IF_MD2(fat_bpb->drive,) - start + fat_bpb->startsector, count, buf); + rc = sectorcache_writethrough(IF_MD2(fat_bpb->drive,) + start + fat_bpb->startsector, count, buf, 0); } else - rc = storage_read_sectors(IF_MD2(fat_bpb->drive,) - start + fat_bpb->startsector, count, buf); + rc = sectorcache_readthrough(IF_MD2(fat_bpb->drive,) + start + fat_bpb->startsector, count, buf, 0); if (rc < 0) { DEBUGF( "transfer() - Couldn't %s sector %lx" " (error code %d)\n", @@ -2126,6 +2169,62 @@ } +unsigned long fat_get_next_sector(struct fat_file *file, bool write) +{ +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + long cluster = file->lastcluster; + long sector = file->lastsector; + long clusternum = file->clusternum; + long numsec = file->sectornum + 1; + bool eof = file->eof; + + if (eof && !write) return 0; + + if (numsec > (long)fat_bpb->bpb_secperclus || !cluster) + { + if (write) cluster = next_write_cluster(file, cluster, §or); + else + { + cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); + sector = cluster2sec(IF_MV2(fat_bpb,) cluster); + } + + if (!cluster) + { + file->eof = true; + return 0; + } + + clusternum++; + numsec = 1; + } + else if (sector) sector++; + else + { + /* look up first sector of file */ + sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster); + numsec = 1; +#ifdef HAVE_FAT16SUPPORT + if (file->firstcluster < 0) + { /* FAT16 root dir */ + sector += fat_bpb->rootdiroffset; + numsec += fat_bpb->rootdiroffset; + } +#endif + } + + file->lastcluster = cluster; + file->lastsector = sector; + file->clusternum = clusternum; + file->sectornum = numsec; + + return sector; +} + long fat_readwrite( struct fat_file *file, long sectorcount, void* buf, bool write ) { @@ -2338,6 +2437,7 @@ int i, j; int rc; int order; + unsigned long secnum; unsigned char firstbyte; /* Long file names are stored in special entries. Each entry holds up to 13 characters. Names can be max 255 chars (not bytes!) long */ @@ -2353,13 +2453,15 @@ { if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) { - rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false); - if (rc == 0) { + if (dir->cache) sectorcache_unlock(&dir->cache, 0, 1); + secnum = fat_get_next_sector(&dir->file, false); + if (!secnum) { /* eof */ entry->name[0] = 0; break; } - if (rc < 0) { + rc = sectorcache_lock(IF_MD2(dir->file->drive,) secnum, &dir->cache, 0); + if (rc) { DEBUGF( "fat_getnext() - Couldn't read dir" " (error code %d)\n", rc); return rc * 10 - 1; @@ -2371,7 +2473,7 @@ i < DIR_ENTRIES_PER_SECTOR; i++) { unsigned int entrypos = i * DIR_ENTRY_SIZE; - firstbyte = dir->sectorcache[entrypos]; + firstbyte = dir->cache->buffer[entrypos]; dir->entry++; if (firstbyte == 0xe5) { @@ -2390,12 +2492,12 @@ dir->entrycount++; /* LFN entry? */ - if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] & + if ( ( dir->cache->buffer[entrypos + FATDIR_ATTR] & FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { /* extract ordinal */ - order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; + order = dir->cache->buffer[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; /* is this entry the first long entry ? (first in order but containing last part) */ - if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { + if (dir->cache->buffer[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { /* check that order is not too big ! (and non-zero) */ if(order <= 0 || order > FATLONG_MAX_ORDER) continue; /* ignore the whole LFN, will trigger lots of warnings */ @@ -2424,13 +2526,13 @@ order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY; for(j = 0; j < FATLONG_NAME_CHUNKS; j++) { memcpy(dir->longname + order, - dir->sectorcache + entrypos + FATLONG_NAME_POS[j], + dir->cache->buffer + entrypos + FATLONG_NAME_POS[j], FATLONG_NAME_SIZE[j]); order += FATLONG_NAME_SIZE[j]; } } else { - if ( parse_direntry(entry, dir->sectorcache + entrypos) ) { + if ( parse_direntry(entry, dir->cache->buffer + entrypos) ) { /* don't return volume id entry */ if ( (entry->attr & Index: firmware/export/fat.h =================================================================== --- firmware/export/fat.h (revision 27026) +++ firmware/export/fat.h (working copy) @@ -24,6 +24,8 @@ #include #include "mv.h" /* for volume definitions */ +#include "system.h" /* for STORAGE_ALIGN_ATTR */ +#include "sectorcache.h" #include "config.h" /* This value can be overwritten by a target in config-[target].h, but @@ -77,6 +79,9 @@ #ifdef HAVE_MULTIVOLUME int volume; /* file resides on which volume */ #endif +#ifdef HAVE_MULTIDRIVE + int drive; /* volume resides on which drive */ +#endif }; struct fat_dir @@ -85,7 +90,7 @@ unsigned int entrycount; long sector; struct fat_file file; - unsigned char sectorcache[SECTOR_SIZE]; + struct sectorcache_entry* cache; /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most * 13 characters, that a total of 260 characters. So we keep a buffer of that size. @@ -115,10 +120,12 @@ extern int fat_create_file(const char* name, struct fat_file* ent, struct fat_dir* dir); +extern unsigned long fat_get_next_sector(struct fat_file *ent, bool write); extern long fat_readwrite(struct fat_file *ent, long sectorcount, - void* buf, bool write ); + void* buf, bool write); extern int fat_closewrite(struct fat_file *ent, long size, int attr); -extern int fat_seek(struct fat_file *ent, unsigned long sector ); +extern void fat_closedir(struct fat_dir *dir); +extern int fat_seek(struct fat_file *ent, unsigned long sector); extern int fat_remove(struct fat_file *ent); extern int fat_truncate(const struct fat_file *ent); extern int fat_rename(struct fat_file* file, Index: firmware/export/sectorcache.h =================================================================== --- firmware/export/sectorcache.h (revision 0) +++ firmware/export/sectorcache.h (revision 0) @@ -0,0 +1,91 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Michael Sparmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _SECTORCACHE_H_ +#define _SECTORCACHE_H_ + +#include "system.h" +#include "mv.h" + +struct sectorcache_entry +{ + int valid; /* Contains valid and up-to-date data for the specified sector */ + int free; /* Not expected to be needed again, should be reclaimed preferably */ + int locks; /* Number of users of this buffer */ + int exclusive; /* Locked for exclusive access, *must* be used for write accesses */ + int dirty; /* Needs to be written back before it can be reclaimed */ + int consistent; /* Allowed to be written back in its current state */ +#ifdef HAVE_MULTIDRIVE + int drive; +#endif + unsigned long sector; + unsigned char* buffer; +}; + +void sectorcache_init(void) INIT_ATTR; + +/* Read sectors from a storage device. If all requested sectors are present + in the cache (and marked as consistent), no read will be issued to the + storage layer. Otherwise, one big read will be issued, and consistent + dirty sectors will be copied to the buffer afterwards + (overwriting the data that was just being read from the device). */ +int sectorcache_readthrough(IF_MD2(int drive,) unsigned long start, int count, + void* buf, int keepincache); + +/* Writes sectors to a storage device. The data might be cached for further + read accesses, but is always guaranteed to be written to the storage device + immediately. If there are open handles to those sectors, this will panic. */ +int sectorcache_writethrough(IF_MD2(int drive,) unsigned long start, int count, + const void* buf, int keepincache); + +/* Get a handle for a sector. If write access is desired, exclusive access + *must* be requested. Will block until there is buffer space available. */ +int sectorcache_lock(IF_MD2(int drive,) unsigned long sector, + struct sectorcache_entry** handle, int exclusive); + +/* Unlock a sector and clean it if requested. + The sector will be marked as consistent. */ +void sectorcache_unlock(struct sectorcache_entry** handle, int clean, int keepincache); + +/* Indicate that the buffer will need to be written back before it can be reclaimed */ +void sectorcache_markdirty(struct sectorcache_entry* handle); + +/* Indicate that the buffer may not be written back until it has been + marked as consistent again. (Prevents syncing during inactivity) + Release this as soon as possible. */ +void sectorcache_markinconsistent(struct sectorcache_entry* handle); + +/* Indicate that the buffer may not be written back again. */ +void sectorcache_markconsistent(struct sectorcache_entry* handle); + +/* Write back the sector. Will mark it as consistent. + Make sure it is marked as consistent before calling this internally. */ +int sectorcache_clean(struct sectorcache_entry* handle); + +/* Write back everything that's dirty, + and flush the storage layer if wanted. + This may block for several seconds! */ +void sectorcache_clean_all(int flushstorage); + +/* Invalidate the whole cache for the drive, used by hotplug and USB. + Will panic if there are still handles open for this drive. */ +void sectorcache_invalidate(IF_MD_NONVOID(int drive)); + +#endif \ No newline at end of file Index: firmware/powermgmt.c =================================================================== --- firmware/powermgmt.c (revision 27026) +++ firmware/powermgmt.c (working copy) @@ -27,6 +27,7 @@ #include "adc.h" #include "string.h" #include "storage.h" +#include "sectorcache.h" #include "power.h" #include "audio.h" #include "mp3_playback.h" @@ -750,12 +751,7 @@ glyph_cache_save(NULL); #endif -/* Commit pending writes if needed. Even though we don't do write caching, - things like flash translation layers may need this to commit scattered - pages to there final locations. So far only used for iPod Nano 2G. */ -#ifdef HAVE_STORAGE_FLUSH - storage_flush(); -#endif + sectorcache_clean_all(1); if (storage_disk_is_active()) storage_spindown(1); Index: firmware/rolo.c =================================================================== --- firmware/rolo.c (revision 27026) +++ firmware/rolo.c (working copy) @@ -28,6 +28,7 @@ #include "file.h" #include "audio.h" #include "system.h" +#include "sectorcache.h" #include "i2c.h" #include "adc.h" #include "string.h" @@ -283,11 +284,9 @@ return -1; } -#ifdef HAVE_STORAGE_FLUSH lcd_puts(0, 1, "Flushing storage buffers"); lcd_update(); - storage_flush(); -#endif + sectorcache_clean_all(1); lcd_puts(0, 1, "Executing"); lcd_update(); Index: firmware/target/arm/s5l8700/app.lds =================================================================== --- firmware/target/arm/s5l8700/app.lds (revision 27026) +++ firmware/target/arm/s5l8700/app.lds (working copy) @@ -107,7 +107,7 @@ *(.stack) stackbegin = .; _stackbegin = .; - . += 0x4000; + . += 0x2000; stackend = .; _stackend = .; _irqstackbegin = .; Index: firmware/target/arm/s5l8700/ipodnano2g/ftl-nano2g.c =================================================================== --- firmware/target/arm/s5l8700/ipodnano2g/ftl-nano2g.c (revision 27026) +++ firmware/target/arm/s5l8700/ipodnano2g/ftl-nano2g.c (working copy) @@ -1201,7 +1201,10 @@ uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks; uint32_t error = 0; - if (sector + count > ftl_nand_type->userblocks * ppb) + if ((uint32_t)buffer & 0xf) + panicf("ftl_read: Misaligned data buffer at %08X (sector %lu, count %lu)", (unsigned int)buffer, sector, count); + + if (sector + count > ftl_nand_type->userblocks * ppb) return 1; if (count == 0) return 0; @@ -1834,7 +1837,10 @@ uint32_t i, j, k; uint32_t ppb = ftl_nand_type->pagesperblock * ftl_banks; - if (sector + count > ftl_nand_type->userblocks * ppb) + if ((uint32_t)buffer & 0xf) + panicf("ftl_write: Misaligned data buffer at %08X (sector %lu, count %lu)", (unsigned int)buffer, sector, count); + + if (sector + count > ftl_nand_type->userblocks * ppb) return 1; if (count == 0) return 0; Index: firmware/target/arm/s5l8700/ipodnano2g/nand-nano2g.c =================================================================== --- firmware/target/arm/s5l8700/ipodnano2g/nand-nano2g.c (revision 27026) +++ firmware/target/arm/s5l8700/ipodnano2g/nand-nano2g.c (working copy) @@ -387,6 +387,10 @@ { uint8_t* data = nand_data; uint8_t* spare = nand_spare; + if ((uint32_t)databuffer & 0xf) + panicf("nand_read_page: Misaligned data buffer at %08X (bank %lu, page %lu)", (unsigned int)databuffer, bank, page); + if ((uint32_t)sparebuffer & 0xf) + panicf("nand_read_page: Misaligned spare buffer at %08X (bank %lu, page %lu)", (unsigned int)sparebuffer, bank, page); if (databuffer && !((uint32_t)databuffer & 0xf)) data = (uint8_t*)databuffer; if (sparebuffer && !((uint32_t)sparebuffer & 0xf)) @@ -448,6 +452,10 @@ { uint8_t* data = nand_data; uint8_t* spare = nand_spare; + if ((uint32_t)databuffer & 0xf) + panicf("nand_write_page: Misaligned data buffer at %08X (bank %lu, page %lu)", (unsigned int)databuffer, bank, page); + if ((uint32_t)sparebuffer & 0xf) + panicf("nand_write_page: Misaligned spare buffer at %08X (bank %lu, page %lu)", (unsigned int)sparebuffer, bank, page); if (databuffer && !((uint32_t)databuffer & 0xf)) data = (uint8_t*)databuffer; if (sparebuffer && !((uint32_t)sparebuffer & 0xf)) Index: firmware/target/arm/s5l8700/system-target.h =================================================================== --- firmware/target/arm/s5l8700/system-target.h (revision 27026) +++ firmware/target/arm/s5l8700/system-target.h (working copy) @@ -30,6 +30,7 @@ #define CPUFREQ_MAX 191692800 #define STORAGE_WANTS_ALIGN +#define STORAGE_NEEDS_ALIGN #define inl(a) (*(volatile unsigned long *) (a)) #define outl(a,b) (*(volatile unsigned long *) (b) = (a)) Index: firmware/usb.c =================================================================== --- firmware/usb.c (revision 27026) +++ firmware/usb.c (working copy) @@ -68,10 +68,12 @@ static int usb_mmc_countdown = 0; #endif -/* FIXME: The extra 0x800 is consumed by fat_mount() when the fsinfo - needs updating */ #ifdef USB_FULL_INIT -static long usb_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)]; +#ifdef HAVE_LCD_BITMAP /* additional space only needed for screendump */ +static long usb_stack[(DEFAULT_STACK_SIZE + 0x400)/sizeof(long)]; +#else +static long usb_stack[DEFAULT_STACK_SIZE/sizeof(long)]; +#endif static const char usb_thread_name[] = "usb"; static unsigned int usb_thread_entry = 0; #ifndef USB_STATUS_BY_EVENT @@ -172,6 +174,7 @@ if(on) { DEBUGF("Entering USB slave mode\n"); + sectorcache_clean_all(0); storage_soft_reset(); storage_init(); storage_enable(false); Index: firmware/usbstack/usb_storage.c =================================================================== --- firmware/usbstack/usb_storage.c (revision 27026) +++ firmware/usbstack/usb_storage.c (working copy) @@ -26,6 +26,7 @@ /*#define LOGF_ENABLE*/ #include "logf.h" #include "storage.h" +#include "sectorcache.h" #include "disk.h" /* Needed to get at the audio buffer */ #include "audio.h" @@ -354,8 +355,12 @@ #ifdef USB_USE_RAMDISK return true; #else - unsigned char sector[SECTOR_SIZE]; - return storage_read_sectors(IF_MD2(volume,)0,1,sector) == 0; + int rc; + struct sectorcache_entry* sector; + sectorcache_invalidate(IF_MD(volume)); + rc = sectorcache_lock(IF_MD2(volume,) 0, §or, 0); + if (!rc) sectorcache_unlock(§or, 0, 0); + return !rc; #endif }