diff --git a/firmware/common/dir.c b/firmware/common/dir.c index 70aa4946dc..6bf9a53cad 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c @@ -105,8 +105,7 @@ struct dirent* readdir(DIR* dir) return NULL; if ( !entry.name[0] ) - return NULL; - + return NULL; strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) ); theent->attribute = entry.attr; diff --git a/firmware/common/file.c b/firmware/common/file.c index 2c00c3e3f6..27258e4603 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c @@ -42,23 +42,25 @@ struct filedesc { int size; struct fat_file fatfile; bool busy; + bool write; }; static struct filedesc openfiles[MAX_OPEN_FILES]; -int open(char* pathname, int flags) +int creat(const char *pathname, int mode) +{ + (void)mode; + return open(pathname, O_WRONLY); +} + +int open(const char* pathname, int flags) { DIR* dir; struct dirent* entry; int fd; char* name; - /* For now, we don't support writing */ - if(flags & (O_WRONLY | O_RDWR)) - { - errno = EROFS; - return -1; - } + LDEBUGF("open(\"%s\",%d)\n",pathname,flags); if ( pathname[0] != '/' ) { DEBUGF("'%s' is not an absolute path.\n",pathname); @@ -75,44 +77,74 @@ int open(char* pathname, int flags) if ( fd == MAX_OPEN_FILES ) { DEBUGF("Too many files open\n"); errno = EMFILE; - return -1; + return -2; } + switch ( flags ) { + case O_RDONLY: + openfiles[fd].write = false; + break; + + case O_WRONLY: + openfiles[fd].write = true; + break; + + default: + DEBUGF("Only O_RDONLY and O_WRONLY is supported\n"); + errno = EROFS; + return -3; + } openfiles[fd].busy = true; /* locate filename */ name=strrchr(pathname+1,'/'); if ( name ) { *name = 0; - dir = opendir(pathname); + dir = opendir((char*)pathname); *name = '/'; name++; } else { dir = opendir("/"); - name = pathname+1; + name = (char*)pathname+1; } if (!dir) { DEBUGF("Failed opening dir\n"); errno = EIO; openfiles[fd].busy = false; - return -1; + return -4; } /* scan dir for name */ while ((entry = readdir(dir))) { if ( !strcasecmp(name, entry->d_name) ) { - fat_open(entry->startcluster, &(openfiles[fd].fatfile)); + fat_open(entry->startcluster, + &(openfiles[fd].fatfile), + &(dir->fatdir)); openfiles[fd].size = entry->size; break; } } closedir(dir); if ( !entry ) { - DEBUGF("Couldn't find %s in %s\n",name,pathname); - errno = ENOENT; - openfiles[fd].busy = false; - return -1; + LDEBUGF("Didn't find file %s\n",name); + if ( openfiles[fd].write ) { + if (fat_create_file(name, + &(openfiles[fd].fatfile), + &(dir->fatdir)) < 0) { + DEBUGF("Couldn't create %s in %s\n",name,pathname); + errno = EIO; + openfiles[fd].busy = false; + return -5; + } + openfiles[fd].size = 0; + } + else { + DEBUGF("Couldn't find %s in %s\n",name,pathname); + errno = ENOENT; + openfiles[fd].busy = false; + return -6; + } } openfiles[fd].cacheoffset = -1; @@ -122,19 +154,37 @@ int open(char* pathname, int flags) int close(int fd) { + int rc = 0; + + LDEBUGF("close(%d)\n",fd); + if (fd < 0 || fd > MAX_OPEN_FILES-1) { errno = EINVAL; return -1; } if (!openfiles[fd].busy) { errno = EBADF; - return -1; + return -2; + } + if (openfiles[fd].write) { + /* flush sector cache */ + if ( openfiles[fd].cacheoffset != -1 ) { + if ( fat_readwrite(&(openfiles[fd].fatfile), 1, + &(openfiles[fd].cache),true) < 0 ) { + DEBUGF("Failed flushing cache\n"); + errno = EIO; + rc = -1; + } + } + + /* tie up all loose ends */ + fat_closewrite(&(openfiles[fd].fatfile), openfiles[fd].size); } openfiles[fd].busy = false; - return 0; + return rc; } -int read(int fd, void* buf, int count) +static int readwrite(int fd, void* buf, int count, bool write) { int sectors; int nread=0; @@ -144,9 +194,16 @@ int read(int fd, void* buf, int count) return -1; } - /* attempt to read past EOF? */ - if ( count > openfiles[fd].size - openfiles[fd].fileoffset ) - count = openfiles[fd].size - openfiles[fd].fileoffset; + LDEBUGF( "readwrite(%d,%x,%d,%s)\n", + fd,buf,count,write?"write":"read"); + + /* attempt to access past EOF? */ + if (count > openfiles[fd].size - openfiles[fd].fileoffset) { + if ( write ) + openfiles[fd].size = openfiles[fd].fileoffset + count; + else + count = openfiles[fd].size - openfiles[fd].fileoffset; + } /* any head bytes? */ if ( openfiles[fd].cacheoffset != -1 ) { @@ -163,23 +220,38 @@ int read(int fd, void* buf, int count) openfiles[fd].cacheoffset = -1; } - /* eof? */ - if ( openfiles[fd].fileoffset + headbytes > openfiles[fd].size ) - headbytes = openfiles[fd].size - openfiles[fd].fileoffset; + if (write) { + memcpy( openfiles[fd].cache + offs, buf, headbytes ); + if (offs+headbytes == SECTOR_SIZE) { + int rc = fat_readwrite(&(openfiles[fd].fatfile), 1, + openfiles[fd].cache, true ); + if ( rc < 0 ) { + DEBUGF("Failed read/writing %d sectors\n",sectors); + errno = EIO; + return -2; + } + openfiles[fd].cacheoffset = -1; + } + } + else { + memcpy( buf, openfiles[fd].cache + offs, headbytes ); + } - memcpy( buf, openfiles[fd].cache + offs, headbytes ); nread = headbytes; count -= headbytes; + + LDEBUGF("readwrite incache: offs=%d\n",openfiles[fd].cacheoffset); } /* read whole sectors right into the supplied buffer */ sectors = count / SECTOR_SIZE; if ( sectors ) { - int rc = fat_read(&(openfiles[fd].fatfile), sectors, buf+nread ); + int rc = fat_readwrite(&(openfiles[fd].fatfile), sectors, + buf+nread, write ); if ( rc < 0 ) { - DEBUGF("Failed reading %d sectors\n",sectors); + DEBUGF("Failed read/writing %d sectors\n",sectors); errno = EIO; - return -1; + return -2; } else { if ( rc > 0 ) { @@ -197,18 +269,19 @@ int read(int fd, void* buf, int count) /* any tail bytes? */ if ( count ) { - if ( fat_read(&(openfiles[fd].fatfile), 1, - &(openfiles[fd].cache)) < 0 ) { - DEBUGF("Failed caching sector\n"); - errno = EIO; - return -1; + if (write) { + memcpy( openfiles[fd].cache, buf + nread, count ); } - - /* eof? */ - if ( openfiles[fd].fileoffset + count > openfiles[fd].size ) - count = openfiles[fd].size - openfiles[fd].fileoffset; - - memcpy( buf + nread, openfiles[fd].cache, count ); + else { + if ( fat_readwrite(&(openfiles[fd].fatfile), 1, + &(openfiles[fd].cache),false) < 0 ) { + DEBUGF("Failed caching sector\n"); + errno = EIO; + return -1; + } + memcpy( buf + nread, openfiles[fd].cache, count ); + } + nread += count; openfiles[fd].cacheoffset = count; } @@ -217,6 +290,17 @@ int read(int fd, void* buf, int count) return nread; } +int write(int fd, void* buf, int count) +{ + return readwrite(fd, buf, count, true); +} + +int read(int fd, void* buf, int count) +{ + return readwrite(fd, buf, count, false); +} + + int lseek(int fd, int offset, int whence) { int pos; @@ -225,6 +309,8 @@ int lseek(int fd, int offset, int whence) int sectoroffset; int rc; + LDEBUGF("lseek(%d,%d,%d)\n",fd,offset,whence); + if ( !openfiles[fd].busy ) { errno = EBADF; return -1; @@ -245,11 +331,11 @@ int lseek(int fd, int offset, int whence) default: errno = EINVAL; - return -1; + return -2; } if ((pos < 0) || (pos > openfiles[fd].size)) { errno = EINVAL; - return -1; + return -3; } /* new sector? */ @@ -263,14 +349,14 @@ int lseek(int fd, int offset, int whence) rc = fat_seek(&(openfiles[fd].fatfile), newsector); if ( rc < 0 ) { errno = EIO; - return -1; + return -4; } } - rc = fat_read(&(openfiles[fd].fatfile), 1, - &(openfiles[fd].cache)); + rc = fat_readwrite(&(openfiles[fd].fatfile), 1, + &(openfiles[fd].cache),false); if ( rc < 0 ) { errno = EIO; - return -1; + return -5; } openfiles[fd].cacheoffset = sectoroffset; } diff --git a/firmware/common/file.h b/firmware/common/file.h index c95811fc55..7a7c63d665 100644 --- a/firmware/common/file.h +++ b/firmware/common/file.h @@ -40,24 +40,22 @@ #endif #if defined(__MINGW32__) && defined(SIMULATOR) -int open (const char*, int, ...); +extern int open(const char*, int, ...); extern int close(int fd); -int read (int, void*, unsigned int); -long lseek (int, long, int); +extern int read(int, void*, unsigned int); +extern long lseek(int, long, int); #else #ifndef SIMULATOR -extern int open(char* pathname, int flags); +extern int open(const char* pathname, int flags); extern int close(int fd); extern int read(int fd, void* buf, int count); extern int lseek(int fd, int offset, int whence); - -#ifdef DISK_WRITE +extern int creat(const char *pathname, int mode); extern int write(int fd, void* buf, int count); -extern int remove(char* pathname); -extern int rename(char* oldname, char* newname); -#endif +extern int remove(const char* pathname); +extern int rename(const char* oldname, const char* newname); #else #ifdef WIN32 diff --git a/firmware/debug.c b/firmware/debug.c index 83eb149706..2ebcd86778 100644 --- a/firmware/debug.c +++ b/firmware/debug.c @@ -195,7 +195,9 @@ static void debug(char *msg) } #endif /* end of DEBUG section */ +#ifdef __GNUC__ void debugf(char *fmt, ...) +#endif { #ifdef DEBUG va_list ap; @@ -223,4 +225,13 @@ void debugf(char *fmt, ...) vfprintf( stderr, fmt, ap ); va_end( ap ); } + +void ldebugf(char* file, int line, char *fmt, ...) +{ + va_list ap; + va_start( ap, fmt ); + fprintf( stderr, "%s:%d ", file, line ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); +} #endif diff --git a/firmware/debug.h b/firmware/debug.h index e9eec27e86..878da295bc 100644 --- a/firmware/debug.h +++ b/firmware/debug.h @@ -21,20 +21,24 @@ extern void debug_init(void); extern void debugf(char* fmt,...); +extern void ldebugf(char* file, int line, char *fmt, ...); #ifdef __GNUC__ /* */ #if defined(DEBUG) || defined(SIMULATOR) -//#define DEBUGF(...) debugf(__VA_ARGS__) #define DEBUGF debugf +#define LDEBUGF(...) ldebugf(__FILE__, __LINE__, __VA_ARGS__) #else #define DEBUGF(...) +#define LDEBUGF(...) #endif + #else #define DEBUGF debugf +#define LDEBUGF debugf #endif /* GCC */ diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index 7bed896748..322ae76a2d 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c @@ -22,14 +22,11 @@ #include #include #include -#ifdef DISK_WRITE -#include -#include -#endif #include #include "fat.h" #include "ata.h" #include "debug.h" +#include "panic.h" #include "system.h" #define BYTES2INT16(array,pos) \ @@ -38,11 +35,6 @@ (array[pos] | (array[pos+1] << 8 ) | \ (array[pos+2] << 16 ) | (array[pos+3] << 24 )) -#define NUM_ROOT_DIR_ENTRIES 512 -#define NUM_FATS 2 -#define NUM_RESERVED_SECTORS 1 -#define NUM_BLOCKS 10000 - #define FATTYPE_FAT12 0 #define FATTYPE_FAT16 1 #define FATTYPE_FAT32 2 @@ -107,6 +99,12 @@ #define FATDIR_FSTCLUSLO 26 #define FATDIR_FILESIZE 28 +#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) +#define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / 32) +#define DIR_ENTRY_SIZE 32 +#define FAT_BAD_MARK 0x0ffffff7 +#define FAT_EOF_MARK 0x0ffffff8 + struct fsinfo { int freecount; /* last known free cluster count */ int nextfree; /* first cluster to start looking for free clusters, @@ -155,6 +153,7 @@ struct bpb int rootdirsector; int firstdatasector; int startsector; + struct fsinfo fsinfo; }; static struct bpb fat_bpb; @@ -167,6 +166,7 @@ static unsigned int getcurrdostime(unsigned short *dosdate, unsigned short *dostime, unsigned char *dostenth); static int create_dos_name(unsigned char *name, unsigned char *newname); +static int find_free_cluster(int start); #endif #define FAT_CACHE_SIZE 0x20 @@ -256,7 +256,6 @@ int fat_mount(int startsector) fat_bpb.bpb_secperclus = buf[BPB_SECPERCLUS]; fat_bpb.bpb_rsvdseccnt = BYTES2INT16(buf,BPB_RSVDSECCNT); fat_bpb.bpb_numfats = buf[BPB_NUMFATS]; - fat_bpb.bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); fat_bpb.bpb_totsec16 = BYTES2INT16(buf,BPB_TOTSEC16); fat_bpb.bpb_media = buf[BPB_MEDIA]; fat_bpb.bpb_fatsz16 = BYTES2INT16(buf,BPB_FATSZ16); @@ -295,7 +294,7 @@ int fat_mount(int startsector) #endif { DEBUGF("This is not FAT32. Go away!\n"); - return -1; + return -2; } fat_bpb.bpb_extflags = BYTES2INT16(buf,BPB_EXTFLAGS); @@ -316,11 +315,23 @@ int fat_mount(int startsector) if (bpb_is_sane() < 0) { DEBUGF( "fat_mount() - BPB is not sane\n"); - return -1; + return -3; } fat_bpb.rootdirsector = cluster2sec(fat_bpb.bpb_rootclus); + /* Read the fsinfo sector */ + err = ata_read_sectors(startsector + fat_bpb.bpb_fsinfo, 1, buf); + if (err) + { + DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", err); + return -1; + } + fat_bpb.fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); + fat_bpb.fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); + LDEBUGF("Freecount: %x\n",fat_bpb.fsinfo.freecount); + LDEBUGF("Nextfree: %x\n",fat_bpb.fsinfo.nextfree); + return 0; } @@ -339,9 +350,9 @@ static int bpb_is_sane(void) fat_bpb.bpb_bytspersec, fat_bpb.bpb_secperclus, fat_bpb.bpb_bytspersec * fat_bpb.bpb_secperclus); } - if(fat_bpb.bpb_rsvdseccnt != 1) + if (fat_bpb.bpb_rsvdseccnt != 32) { - DEBUGF( "bpb_is_sane() - Warning: Reserved sectors is not 1 (%d)\n", + DEBUGF( "bpb_is_sane() - Warning: Reserved sectors is not 32 (%d)\n", fat_bpb.bpb_rsvdseccnt); } if(fat_bpb.bpb_numfats != 2) @@ -349,11 +360,6 @@ static int bpb_is_sane(void) DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n", fat_bpb.bpb_numfats); } - if(fat_bpb.bpb_rootentcnt != 512) - { - DEBUGF( "bpb_is_sane() - Warning: RootEntCnt is not 512 (%d)\n", - fat_bpb.bpb_rootentcnt); - } if(fat_bpb.bpb_media != 0xf0 && fat_bpb.bpb_media < 0xf8) { DEBUGF( "bpb_is_sane() - Warning: Non-standard " @@ -364,33 +370,42 @@ static int bpb_is_sane(void) { DEBUGF( "bpb_is_sane() - Error: Last word is not " "0xaa55 (0x%04x)\n", fat_bpb.last_word); - return -1; + return -4; } + + if (fat_bpb.fsinfo.freecount > + (fat_bpb.totalsectors - fat_bpb.firstdatasector)/ + fat_bpb.bpb_secperclus) + { + DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " + "(0x%04x)\n", fat_bpb.fsinfo.freecount); + return -5; + } + return 0; } -static void *cache_fat_sector(int secnum) +static void *cache_fat_sector(int fatsector) { + int secnum = fatsector + fat_bpb.bpb_rsvdseccnt; int cache_index = secnum & FAT_CACHE_MASK; /* Delete the cache entry if it isn't the sector we want */ if(fat_cache[cache_index].inuse && fat_cache[cache_index].secnum != secnum) { -#ifdef WRITE /* Write back if it is dirty */ if(fat_cache[cache_index].dirty) { - if(ata_write_sectors(secnum + fat_bpb.startsector, 1, sec)) + if(ata_write_sectors(secnum + fat_bpb.startsector, 1, + fat_cache_sectors[cache_index])) { - panic("cache_fat_sector() - Could" - " not write sector %d\n", - secnum); + panicf("cache_fat_sector() - Could not write sector %d\n", + secnum); } } fat_cache[cache_index].secnum = 8; /* Normally an unused sector */ fat_cache[cache_index].dirty = false; -#endif fat_cache[cache_index].inuse = false; } @@ -409,101 +424,107 @@ static void *cache_fat_sector(int secnum) return fat_cache_sectors[cache_index]; } -#ifdef DISK_WRITE -static int update_entry(int entry, unsigned int val) +static int find_free_cluster(int startcluster) { - unsigned long *sec; - int fatoffset; - int thisfatsecnum; - int thisfatentoffset; + int sector = startcluster / CLUSTERS_PER_FAT_SECTOR; + int offset = startcluster % CLUSTERS_PER_FAT_SECTOR; + int i; - fatoffset = entry * 4; - thisfatsecnum = fatoffset / fat_bpb.bpb_bytspersec + fat_bpb.bpb_rsvdseccnt; - thisfatentoffset = fatoffset % fat_bpb.bpb_bytspersec; + LDEBUGF("find_free_cluster(%x)\n",startcluster); - /* Load the sector if it is not cached */ - sec = cache_fat_sector(thisfatsecnum); - if(!sec) + for (i = sector; i= 0x0ffffff8 ) + if ( next_cluster >= FAT_EOF_MARK ) return 0; else return next_cluster; } -#ifdef DISK_WRITE -static int flush_fat(struct bpb *bpb) +static int flush_fat(void) { int i; int err; unsigned char *sec; int secnum; - int fatsz; - unsigned short d, t; - char m; + unsigned char fsinfo[SECTOR_SIZE]; + unsigned int* intptr; - fatsz = fat_bpb.fatsize; + LDEBUGF("flush_fat()\n"); for(i = 0;i < FAT_CACHE_SIZE;i++) { - if(fat_cache[i].ptr && fat_cache[i].dirty) + if(fat_cache[i].inuse && fat_cache[i].dirty) { - secnum = fat_cache[i].secnum + fat_bpb.bpb_rsvdseccnt + - fat_bpb.startsector; - DEBUGF("Flushing FAT sector %d\n", secnum); - sec = fat_cache[i].ptr; + secnum = fat_cache[i].secnum + fat_bpb.startsector; + LDEBUGF("Flushing FAT sector %x\n", secnum); + sec = fat_cache_sectors[i]; /* Write to the first FAT */ err = ata_write_sectors(secnum, 1, sec); @@ -514,19 +535,41 @@ static int flush_fat(struct bpb *bpb) return -1; } - /* Write to the second FAT */ - err = ata_write_sectors(secnum + fatsz, 1, sec); - if(err) + if(fat_bpb.bpb_numfats > 1 ) { - DEBUGF( "flush_fat() - Couldn't write" - " sector (%d)\n", secnum + fatsz); - return -1; + /* Write to the second FAT */ + err = ata_write_sectors(secnum + fat_bpb.fatsize, 1, sec); + if (err) + { + DEBUGF( "flush_fat() - Couldn't write" + " sector (%d)\n", secnum + fat_bpb.fatsize); + return -2; + } } - fat_cache[i].dirty = 0; + fat_cache[i].dirty = false; } } - getcurrdostime(&d, &t, &m); + /* update fsinfo */ + err = ata_read_sectors(fat_bpb.startsector + fat_bpb.bpb_fsinfo, 1,fsinfo); + if (err) + { + DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", err); + return -1; + } + intptr = (int*)&(fsinfo[FSINFO_FREECOUNT]); + *intptr = SWAB32(fat_bpb.fsinfo.freecount); + + intptr = (int*)&(fsinfo[FSINFO_NEXTFREE]); + *intptr = SWAB32(fat_bpb.fsinfo.nextfree); + + err = ata_write_sectors(fat_bpb.startsector + fat_bpb.bpb_fsinfo,1,fsinfo); + if (err) + { + DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", err); + return -1; + } + return 0; } @@ -534,10 +577,9 @@ static unsigned int getcurrdostime(unsigned short *dosdate, unsigned short *dostime, unsigned char *dostenth) { - struct timeb tb; +#if 0 struct tm *tm; - - ftime(&tb); + unsigned long now = time(); tm = localtime(&tb.time); *dosdate = ((tm->tm_year - 80) << 9) | @@ -549,102 +591,92 @@ static unsigned int getcurrdostime(unsigned short *dosdate, (tm->tm_sec >> 1); *dostenth = (tm->tm_sec & 1) * 100 + tb.millitm / 10; +#else + *dosdate = 0; + *dostime = 0; + *dostenth = 0; +#endif return 0; } -static int add_dir_entry(unsigned int currdir, struct fat_direntry *de) +static int add_dir_entry(struct fat_dir* dir, + struct fat_direntry* de, + struct fat_file* file) { unsigned char buf[SECTOR_SIZE]; unsigned char *eptr; int i; int err; - unsigned int sec; - unsigned int sec_cnt; - int need_to_update_last_empty_marker = 0; - int is_rootdir = (currdir == 0); - int done = 0; + int sec; + int sec_cnt; + bool need_to_update_last_empty_marker = false; + bool done = false; unsigned char firstbyte; + int currdir = dir->startcluster; + bool is_rootdir = (currdir == 0); - if(is_rootdir) - { + LDEBUGF( "add_dir_entry()\n"); + + if (is_rootdir) sec = fat_bpb.rootdirsector; - } else - { sec = first_sector_of_cluster(currdir); - } sec_cnt = 0; while(!done) { - /* The root dir has a fixed size */ - if(is_rootdir) + if (sec_cnt >= fat_bpb.bpb_secperclus) { - if(sec_cnt >= fat_bpb.bpb_rootentcnt * 32 / fat_bpb.bpb_bytspersec) + int oldcluster = currdir; + /* We have reached the end of this cluster */ + LDEBUGF("Moving to the next cluster..."); + currdir = get_next_cluster(currdir); + LDEBUGF("new cluster is %d\n", currdir); + + if (!currdir) { - /* We have reached the last sector of the root dir */ - if(need_to_update_last_empty_marker) - { - /* Since the directory is full, there is no room for - a marker, so we just exit */ - return 0; - } - else - { - DEBUGF( "add_dir_entry() -" - " Root dir is full\n"); - return -1; - } - } - } - else - { - if(sec_cnt >= fat_bpb.bpb_secperclus) - { - /* We have reached the end of this cluster */ - DEBUGF("Moving to the next cluster..."); - currdir = get_next_cluster(currdir); - DEBUGF("new cluster is %d\n", currdir); - - if(!currdir) - { - /* This was the last in the chain, - we have to allocate a new cluster */ - /* TODO */ + currdir = find_free_cluster(fat_bpb.fsinfo.nextfree); + if (!currdir) { + currdir = find_free_cluster(0); + if (!currdir) { + DEBUGF("add_dir_entry(): Disk full!\n"); + return -1; + } } + update_fat_entry(oldcluster, currdir); } } - DEBUGF("Reading sector %d...\n", sec); + LDEBUGF("Reading sector %d...\n", sec); /* Read the next sector in the current dir */ err = ata_read_sectors(sec + fat_bpb.startsector,1,buf); - if(err) + if (err) { DEBUGF( "add_dir_entry() - Couldn't read dir sector" " (error code %d)\n", err); - return -1; + return -2; } - if(need_to_update_last_empty_marker) + if (need_to_update_last_empty_marker) { /* All we need to do is to set the first entry to 0 */ - DEBUGF("Clearing the first entry in sector %d\n", sec); + LDEBUGF("Clearing the first entry in sector %x\n", sec); buf[0] = 0; - done = 1; + done = true; } else { /* Look for a free slot */ - for(i = 0;i < SECTOR_SIZE;i+=32) + for (i = 0; i < SECTOR_SIZE; i += DIR_ENTRY_SIZE) { firstbyte = buf[i]; - if(firstbyte == 0xe5 || firstbyte == 0) + if (firstbyte == 0xe5 || firstbyte == 0) { - DEBUGF("Found free slot at entry %d in sector %d\n", - i/32, sec); + LDEBUGF("Found free slot at entry %d in sector %x\n", + i/DIR_ENTRY_SIZE, sec); eptr = &buf[i]; - memset(eptr, 0, 32); + memset(eptr, 0, DIR_ENTRY_SIZE); strncpy(&eptr[FATDIR_NAME], de->name, 11); eptr[FATDIR_ATTR] = de->attr; eptr[FATDIR_NTRES] = 0; @@ -665,31 +697,37 @@ static int add_dir_entry(unsigned int currdir, struct fat_direntry *de) eptr[FATDIR_FILESIZE+2] = (de->filesize >> 16) & 0xff; eptr[FATDIR_FILESIZE+3] = (de->filesize >> 24) & 0xff; + /* remember where the dir entry is located */ + file->dirsector = sec; + file->direntry = i/DIR_ENTRY_SIZE; + /* Advance the last_empty_entry marker */ - if(firstbyte == 0) + if (firstbyte == 0) { - i += 32; - if(i < SECTOR_SIZE) + i += DIR_ENTRY_SIZE; + if (i < SECTOR_SIZE) { buf[i] = 0; /* We are done */ - done = 1; + done = true; } else { /* We must fill in the first entry in the next sector */ - need_to_update_last_empty_marker = 1; + need_to_update_last_empty_marker = true; } } + else + done = true; err = ata_write_sectors(sec + fat_bpb.startsector,1,buf); - if(err) + if (err) { DEBUGF( "add_dir_entry() - " " Couldn't write dir" " sector (error code %d)\n", err); - return -1; + return -3; } break; } @@ -746,11 +784,6 @@ static int create_dos_name(unsigned char *name, unsigned char *newname) int i; char *ext; - if(strlen(name) > 12) - { - return -1; - } - strcpy(n, name); ext = strchr(n, '.'); @@ -763,7 +796,7 @@ static int create_dos_name(unsigned char *name, unsigned char *newname) In either case it is illegal. */ if(n[0] == 0) { - return -1; + return -2; } /* Name part */ @@ -781,10 +814,10 @@ static int create_dos_name(unsigned char *name, unsigned char *newname) } /* Extension part */ - for(i = 0;ext && ext[i] && (i < 3);i++) + for (i = 0;ext && ext[i] && (i < 3);i++) { c = char2dos(ext[i]); - if(c) + if (c) { newname[8+i] = toupper(c); } @@ -796,50 +829,48 @@ static int create_dos_name(unsigned char *name, unsigned char *newname) return 0; } -int fat_create_dir(unsigned int currdir, char *name) +static void update_dir_entry( struct fat_file* file, int size ) { - struct fat_direntry de; + unsigned char buf[SECTOR_SIZE]; + int sector = file->dirsector + fat_bpb.startsector; + unsigned char* entry = buf + file->direntry * DIR_ENTRY_SIZE; + unsigned int* sizeptr; + unsigned short* clusptr; int err; - DEBUGF("fat_create_file()\n"); - memset(&de, 0, sizeof(struct fat_direntry)); - if(create_dos_name(name, de.name) < 0) - { - DEBUGF( "fat_create_file() - Illegal file name (%s)\n", name); - return -1; + LDEBUGF("update_dir_entry(cluster:%x entry:%d size:%d)\n", + file->firstcluster,file->direntry,size); + + if ( file->direntry >= (SECTOR_SIZE / DIR_ENTRY_SIZE) ) { + DEBUGF("update_dir_entry(): Illegal entry %d!\n",file->direntry); + return; } - getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); - de.wrtdate = de.crtdate; - de.wrttime = de.crttime; - de.filesize = 0; - de.attr = FAT_ATTR_DIRECTORY; - - err = add_dir_entry(currdir, &de); - return 0; -} - -int fat_create_file(unsigned int currdir, char *name) -{ - struct fat_direntry de; - int err; - - DEBUGF("fat_create_file()\n"); - memset(&de, 0, sizeof(struct fat_direntry)); - if(create_dos_name(name, de.name) < 0) + err = ata_read_sectors(sector, 1, buf); + if (err) { - DEBUGF( "fat_create_file() - Illegal file name (%s)\n", name); - return -1; + DEBUGF( "update_dir_entry() - Couldn't read dir sector %d" + " (error code %d)\n", sector, err); + return; + } + + clusptr = (short*)(entry + FATDIR_FSTCLUSHI); + *clusptr = SWAB16(file->firstcluster >> 16); + + clusptr = (short*)(entry + FATDIR_FSTCLUSLO); + *clusptr = SWAB16(file->firstcluster & 0xffff); + + sizeptr = (int*)(entry + FATDIR_FILESIZE); + *sizeptr = SWAB32(size); + + err = ata_write_sectors(sector, 1, buf); + if (err) + { + DEBUGF( "update_file_size() - Couldn't write dir sector %d" + " (error code %d)\n", sector, err); + return; } - getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); - de.wrtdate = de.crtdate; - de.wrttime = de.crttime; - de.filesize = 0; - - err = add_dir_entry(currdir, &de); - return err; } -#endif static int parse_direntry(struct fat_direntry *de, unsigned char *buf) { @@ -866,37 +897,138 @@ static int parse_direntry(struct fat_direntry *de, unsigned char *buf) return 1; } -int fat_open( unsigned int startcluster, struct fat_file *file) +int fat_open(unsigned int startcluster, + struct fat_file *file, + struct fat_dir* dir) { file->firstcluster = startcluster; file->nextcluster = startcluster; file->nextsector = cluster2sec(startcluster); file->sectornum = 0; + + /* remember where the file's dir entry is located */ + file->dirsector = dir->cached_sec; + file->direntry = (dir->entry % DIR_ENTRIES_PER_SECTOR) - 1; + LDEBUGF("fat_open: entry %d\n",file->direntry); return 0; } -int fat_read( struct fat_file *file, int sectorcount, void* buf ) +int fat_create_file(char* name, + struct fat_file* file, + struct fat_dir* dir) +{ + struct fat_direntry de; + int err; + + LDEBUGF("fat_create_file(\"%s\",%x,%x)\n",name,file,dir); + memset(&de, 0, sizeof(struct fat_direntry)); + if (create_dos_name(name, de.name) < 0) + { + DEBUGF( "fat_create_file() - Illegal file name (%s)\n", name); + return -1; + } + getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); + de.wrtdate = de.crtdate; + de.wrttime = de.crttime; + de.filesize = 0; + + err = add_dir_entry(dir, &de, file); + if (!err) { + file->firstcluster = 0; + file->nextcluster = 0; + file->nextsector = 0; + file->sectornum = 0; + } + + return err; +} + +int fat_closewrite(struct fat_file *file, int size) +{ + int endcluster = file->nextcluster; + int next, last = endcluster; + + /* free unused clusters, if any */ + for ( next = get_next_cluster(last); next; last = next ) { + LDEBUGF("Clearing cluster %x\n",last); + update_fat_entry(last,0); + } + + /* mark last used cluster as last in chain */ + update_fat_entry(endcluster, FAT_EOF_MARK); + + flush_fat(); + + update_dir_entry(file, size); + + return 0; +} + +int fat_readwrite( struct fat_file *file, int sectorcount, + void* buf, bool write ) { int cluster = file->nextcluster; int sector = file->nextsector; int numsec = file->sectornum; - int first = sector, last = sector; + int first, last; int err, i; + LDEBUGF( "fat_readwrite(file:%x,count:%d,buf:%x,%s)\n", + cluster,sectorcount,buf,write?"write":"read"); + if ( sector == -1 ) return 0; - /* find sequential sectors and read them all at once */ + if ( write && !cluster) { + /* new file */ + cluster = find_free_cluster(fat_bpb.fsinfo.nextfree); + if (!cluster) { + cluster = find_free_cluster(0); + if (!cluster) { + DEBUGF("fat_readwrite(): Disk full!\n"); + return -3; + } + } + file->firstcluster = cluster; + sector = cluster2sec(cluster); + if (sector<0) + return -1; + } + + first = sector; + last = sector; + + /* find sequential sectors and read/write them all at once */ for (i=0; i=0; i++ ) { numsec++; if ( numsec >= fat_bpb.bpb_secperclus ) { + int oldcluster = cluster; cluster = get_next_cluster(cluster); if (!cluster) { - /* end of file */ - sector = -1; + if ( write ) { + /* writing past end-of-file, + find a new free cluster to use. */ + cluster = find_free_cluster(cluster); + if (!cluster) { + /* no cluster found after last, + search from beginning */ + cluster = find_free_cluster(0); + if (!cluster) { + /* no free clusters. disk is full. */ + sector = -1; + DEBUGF("fat_readwrite(): Disk full!\n"); + } + } + if ( cluster ) + update_fat_entry(oldcluster, cluster); + } + else { + /* reading past end-of-file */ + sector = -1; + } } - else - { + + if (cluster) { sector = cluster2sec(cluster); if (sector<0) return -1; @@ -910,11 +1042,14 @@ int fat_read( struct fat_file *file, int sectorcount, void* buf ) (i == sectorcount-1) || /* last sector requested? */ (last-first+1 == 256) ) { /* max 256 sectors per ata request */ int count = last-first+1; - err = ata_read_sectors(first + fat_bpb.startsector, count, buf); - if(err) { - DEBUGF( "fat_read() - Couldn't read sector %d" + if (write) + err = ata_write_sectors(first + fat_bpb.startsector, count, buf); + else + err = ata_read_sectors(first + fat_bpb.startsector, count, buf); + if (err) { + DEBUGF( "fat_readwrite() - Couldn't read sector %d" " (error code %d)\n", sector,err); - return -1; + return -2; } ((char*)buf) += count * SECTOR_SIZE; first = sector; @@ -970,9 +1105,9 @@ int fat_seek(struct fat_file *file, int seeksector ) return 0; } -int fat_opendir(struct fat_dir *dir, unsigned int currdir) +int fat_opendir(struct fat_dir *dir, unsigned int startcluster) { - int is_rootdir = (currdir == 0); + int is_rootdir = (startcluster == 0); unsigned int sec; int err; @@ -982,7 +1117,7 @@ int fat_opendir(struct fat_dir *dir, unsigned int currdir) } else { - sec = first_sector_of_cluster(currdir); + sec = first_sector_of_cluster(startcluster); } /* Read the first sector in the current dir */ @@ -997,13 +1132,14 @@ int fat_opendir(struct fat_dir *dir, unsigned int currdir) dir->entry = 0; dir->cached_sec = sec; dir->num_sec = 0; + dir->startcluster = startcluster; return 0; } int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) { - int done = 0; + bool done = false; int i; int err; unsigned char firstbyte; @@ -1013,29 +1149,30 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) while(!done) { - for(i = dir->entry;i < SECTOR_SIZE/32;i++) + for (i = dir->entry; i < SECTOR_SIZE/DIR_ENTRY_SIZE; i++) { - firstbyte = dir->cached_buf[i*32]; + firstbyte = dir->cached_buf[i*DIR_ENTRY_SIZE]; - if(firstbyte == 0xe5) { + if (firstbyte == 0xe5) { /* free entry */ sectoridx = 0; continue; } - if(firstbyte == 0) { + if (firstbyte == 0) { /* last entry */ entry->name[0] = 0; return 0; } /* longname entry? */ - if ( ( dir->cached_buf[i*32 + FATDIR_ATTR] & + if ( ( dir->cached_buf[i*DIR_ENTRY_SIZE + FATDIR_ATTR] & FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { - longarray[longs++] = i*32 + sectoridx; + longarray[longs++] = i*DIR_ENTRY_SIZE + sectoridx; } else { - if ( parse_direntry(entry, &dir->cached_buf[i*32]) ) { + if ( parse_direntry(entry, + &dir->cached_buf[i*DIR_ENTRY_SIZE]) ) { /* don't return volume id entry */ if ( entry->attr == FAT_ATTR_VOLUME_ID ) @@ -1076,7 +1213,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) } entry->name[l]=0; } - done = 1; + done = true; sectoridx = 0; break; } @@ -1091,7 +1228,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) sectoridx += SECTOR_SIZE; /* Next sector? */ - if(i < SECTOR_SIZE/32) + if (i < SECTOR_SIZE / DIR_ENTRY_SIZE) { i++; } @@ -1100,7 +1237,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) dir->num_sec++; /* Do we need to advance one cluster? */ - if(dir->num_sec < fat_bpb.bpb_secperclus) + if (dir->num_sec < fat_bpb.bpb_secperclus) { dir->cached_sec++; } @@ -1113,16 +1250,16 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) } dir->num_sec = 0; cluster = get_next_cluster( cluster ); - if(!cluster) + if (!cluster) { DEBUGF("End of cluster chain.\n"); - return -1; + return -2; } dir->cached_sec = cluster2sec(cluster); if ( dir->cached_sec < 0 ) { DEBUGF("Invalid cluster: %d\n",cluster); - return -1; + return -3; } } @@ -1130,11 +1267,11 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) /* Read the next sector */ err = ata_read_sectors(dir->cached_sec + fat_bpb.startsector, 1, dir->cached_buf); - if(err) + if (err) { DEBUGF( "fat_getnext() - Couldn't read dir sector" " (error code %d)\n", err); - return -1; + return -4; } i = 0; diff --git a/firmware/drivers/fat.h b/firmware/drivers/fat.h index 836d5c6866..3052488c5a 100644 --- a/firmware/drivers/fat.h +++ b/firmware/drivers/fat.h @@ -20,6 +20,8 @@ #ifndef FAT_H #define FAT_H +#include + #define SECTOR_SIZE 512 struct fat_direntry @@ -50,6 +52,7 @@ struct fat_dir int cached_sec; int num_sec; unsigned char cached_buf[SECTOR_SIZE]; + int startcluster; }; struct fat_file @@ -58,17 +61,23 @@ struct fat_file int nextcluster; /* cluster of last access */ int nextsector; /* sector of last access */ int sectornum; /* sector number in this cluster */ + int dirsector; /* sector where the dir entry is located */ + int direntry; /* dir entry index in sector */ }; extern int fat_mount(int startsector); -#ifdef DISK_WRITE -extern int fat_create_file(unsigned int currdir, char *name); extern int fat_create_dir(unsigned int currdir, char *name); -#endif extern int fat_startsector(void); -extern int fat_open(unsigned int cluster, struct fat_file *ent); -extern int fat_read(struct fat_file *ent, int sectorcount, void* buf ); +extern int fat_open(unsigned int cluster, + struct fat_file* ent, + struct fat_dir* dir); +extern int fat_create_file(char* name, + struct fat_file* ent, + struct fat_dir* dir); +extern int fat_readwrite(struct fat_file *ent, int sectorcount, + void* buf, bool write ); +extern int fat_closewrite(struct fat_file *ent, int size); extern int fat_seek(struct fat_file *ent, int sector ); extern int fat_opendir(struct fat_dir *ent, unsigned int currdir); diff --git a/firmware/test/fat/README b/firmware/test/fat/README new file mode 100644 index 0000000000..76141c0044 --- /dev/null +++ b/firmware/test/fat/README @@ -0,0 +1,25 @@ +This code is for testing the Rockbox fat code on a dummy drive image file. + +Dummy image +----------- +Here's how to create a 1 gig dummy drive image in linux: + +# dd if=/dev/hda of=disk.img bs=1M count=1024 + +You can then format disk.img as a FAT32 partition: + +# mkdosfs -F 32 disk.img + +To mount the image, your linux kernel must include the loopback device: + +# mount -o loop disk.img /mnt/image + +Now copy some test data to the disk, umount it and start testing. + + +Test code +--------- +The files in this dir build the 'fat' program. It will read 'disk.img' and +treat is as a real disk, thanks to the ata-sim.c module. + +Modify the main.c source code to make it perform the tests you want. diff --git a/firmware/test/fat/ata-sim.c b/firmware/test/fat/ata-sim.c index ab7266d6a0..a37fabcfc0 100644 --- a/firmware/test/fat/ata-sim.c +++ b/firmware/test/fat/ata-sim.c @@ -24,6 +24,13 @@ int ata_read_sectors(unsigned long start, unsigned char count, void* buf) int ata_write_sectors(unsigned long start, unsigned char count, void* buf) { + DEBUGF("Writing block 0x%lx\n",start); + + if (start == 0) { + DEBUGF("Holy crap! You're writing on sector 0!\n"); + exit(0); + } + if(fseek(file,start*BLOCK_SIZE,SEEK_SET)) { perror("fseek"); return -1; diff --git a/firmware/test/fat/main.c b/firmware/test/fat/main.c index 4a11e0a08a..1b9fd22183 100644 --- a/firmware/test/fat/main.c +++ b/firmware/test/fat/main.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "fat.h" #include "debug.h" #include "disk.h" @@ -14,6 +15,15 @@ void dbg_dump_sector(int sec); void dbg_dump_buffer(unsigned char *buf); void dbg_console(void); +void panicf( char *fmt, ...) +{ + va_list ap; + va_start( ap, fmt ); + vprintf( fmt, ap ); + va_end( ap ); + exit(0); +} + void dbg_dump_sector(int sec) { unsigned char buf[512]; @@ -75,14 +85,16 @@ void dbg_dir(char* currdir) void dbg_mkfile(char* name) { char* text = "Detta är en dummy-text\n"; + int i; int fd = open(name,O_WRONLY); if (fd<0) { DEBUGF("Failed creating file\n"); return; } - if (write(fd, text, strlen(text)) < 0) - DEBUGF("Failed writing data\n"); - + for (i=0;i<200;i++) + if (write(fd, text, strlen(text)) < 0) + DEBUGF("Failed writing data\n"); + close(fd); } @@ -168,6 +180,33 @@ void dbg_tail(char* name) close(fd); } +void dbg_head(char* name) +{ + unsigned char buf[SECTOR_SIZE*5]; + int fd,rc; + + fd = open(name,O_RDONLY); + if (fd<0) + return; + DEBUGF("Got file descriptor %d\n",fd); + + rc = read(fd, buf, SECTOR_SIZE); + if( rc > 0 ) + { + buf[rc]=0; + printf("%d: %s\n", strlen(buf), buf); + } + else if ( rc == 0 ) { + DEBUGF("EOF\n"); + } + else + { + DEBUGF("Failed reading file: %d\n",rc); + } + + close(fd); +} + char current_directory[256] = "\\"; int last_secnum = 0; @@ -284,9 +323,12 @@ int main(int argc, char *argv[]) } //dbg_console(); - //dbg_tail("/fat.h"); //dbg_dir("/"); - dbg_mkfile("/apa.txt"); +#if 1 + dbg_head("/bepa.txt"); +#else + dbg_mkfile("/bepa.txt"); +#endif dbg_dir("/"); return 0;