From da848576312800dc229624e928d132d0702c1854 Mon Sep 17 00:00:00 2001 From: Jörg Hohensohn Date: Tue, 28 Dec 2004 22:16:07 +0000 Subject: prepared to mount multiple partitions into one logical file system (most useful for Ondio, internal memory + external MMC) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5514 a1c6a512-1295-4272-9138-f99709370657 --- firmware/common/dir.c | 83 ++++- firmware/common/disk.c | 26 +- firmware/common/file.c | 3 +- firmware/drivers/ata.c | 11 +- firmware/drivers/ata_mmc.c | 32 +- firmware/drivers/fat.c | 734 ++++++++++++++++++++++++++++----------------- firmware/export/ata.h | 19 +- firmware/export/disk.h | 4 +- firmware/export/fat.h | 23 +- firmware/include/dir.h | 4 + firmware/test/fat/main.c | 4 +- firmware/test/i2c/main.c | 641 ++++++++++++++++++++++++++++++++++++++- firmware/usb.c | 27 +- 13 files changed, 1291 insertions(+), 320 deletions(-) (limited to 'firmware') diff --git a/firmware/common/dir.c b/firmware/common/dir.c index 1bad9327f6..5fa5f9db6e 100644 --- a/firmware/common/dir.c +++ b/firmware/common/dir.c @@ -23,11 +23,48 @@ #include "fat.h" #include "dir.h" #include "debug.h" +#include "atoi.h" #define MAX_OPEN_DIRS 8 static DIR opendirs[MAX_OPEN_DIRS]; +#ifdef HAVE_MULTIVOLUME + +/* how to name volumes, first char must be outside of legal file names, + a number gets appended to enumerate, if applicable */ +#ifdef HAVE_MMC +static const char* vol_names = ":MMC"; +#else +static const char* vol_names = ":HD"; +#endif + +/* returns on which volume this is, and copies the reduced name + (sortof a preprocessor for volume-decorated pathnames) */ +static int strip_volume(const char* name, char* namecopy) +{ + int volume = 0; + + if (name[1] == vol_names[0] ) /* a colon identifies our volumes */ + { + const char* temp; + temp = name + 1 + strlen(vol_names); /* behind special name */ + volume = atoi(temp); /* number is following */ + temp = strchr(temp, '/'); /* search for slash behind */ + if (temp != NULL) + name = temp; /* use the part behind the volume */ + else + name = "/"; /* else this must be the root dir */ + } + + strncpy(namecopy, name, MAX_PATH); + namecopy[MAX_PATH-1] = '\0'; + + return volume; +} +#endif /* #ifdef HAVE_MULTIVOLUME */ + + DIR* opendir(const char* name) { char namecopy[MAX_PATH]; @@ -35,6 +72,9 @@ DIR* opendir(const char* name) char* end; struct fat_direntry entry; int dd; +#ifdef HAVE_MULTIVOLUME + int volume; +#endif /* find a free dir descriptor */ for ( dd=0; ddtheent); - +#ifdef HAVE_MULTIVOLUME + /* Volumes (secondary file systems) get inserted into the root directory + of the first volume, since we have no separate top level. */ + if (dir->volumecounter >= 0 /* on a root dir */ + && dir->volumecounter < NUM_VOLUMES /* in range */ + && dir->fatdir.file.volume == 0) /* at volume 0 */ + { /* fake special directories, which don't really exist, but + will get redirected upon opendir() */ + while (++dir->volumecounter < NUM_VOLUMES) + { + if (fat_ismounted(dir->volumecounter)) + { + memset(theent, 0, sizeof(*theent)); + theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; + snprintf(theent->d_name, sizeof(theent->d_name), + "%s%d", vol_names, dir->volumecounter); + return theent; + } + } + } +#endif + /* normal directory entry fetching follows here */ if (fat_getnext(&(dir->fatdir),&entry) < 0) return NULL; diff --git a/firmware/common/disk.c b/firmware/common/disk.c index b85f460a69..cfe15984f9 100644 --- a/firmware/common/disk.c +++ b/firmware/common/disk.c @@ -41,12 +41,22 @@ static struct partinfo part[8]; -struct partinfo* disk_init(void) +struct partinfo* disk_init(IF_MV_NONVOID(int drive)) { int i; unsigned char sector[512]; +#ifdef HAVE_MULTIVOLUME + /* For each drive, start at a different position, in order not to destroy + the first entry of drive 0. + That one is needed to calculate config sector position. */ + struct partinfo* pinfo = &part[drive*4]; + if ((size_t)drive >= sizeof(part)/sizeof(*part)/4) + return NULL; /* out of space in table */ +#else + struct partinfo* pinfo = part; +#endif - ata_read_sectors(0,1,§or); + ata_read_sectors(IF_MV2(drive,) 0,1,§or); /* check that the boot sector is initialized */ if ( (sector[510] != 0x55) || @@ -58,20 +68,20 @@ struct partinfo* disk_init(void) /* parse partitions */ for ( i=0; i<4; i++ ) { unsigned char* ptr = sector + 0x1be + 16*i; - part[i].type = ptr[4]; - part[i].start = BYTES2INT32(ptr, 8); - part[i].size = BYTES2INT32(ptr, 12); + pinfo[i].type = ptr[4]; + pinfo[i].start = BYTES2INT32(ptr, 8); + pinfo[i].size = BYTES2INT32(ptr, 12); DEBUGF("Part%d: Type %02x, start: %08x size: %08x\n", - i,part[i].type,part[i].start,part[i].size); + i,pinfo[i].type,pinfo[i].start,pinfo[i].size); /* extended? */ - if ( part[i].type == 5 ) { + if ( pinfo[i].type == 5 ) { /* not handled yet */ } } - return part; + return pinfo; } struct partinfo* disk_partinfo(int partition) diff --git a/firmware/common/file.c b/firmware/common/file.c index 81b5a194c0..6714b9982c 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c @@ -132,7 +132,8 @@ int open(const char* pathname, int flags) /* scan dir for name */ while ((entry = readdir(dir))) { if ( !strcasecmp(name, entry->d_name) ) { - fat_open(entry->startcluster, + fat_open(IF_MV2(dir->fatdir.file.volume,) + entry->startcluster, &(file->fatfile), &(dir->fatdir)); file->size = file->trunc ? 0 : entry->size; diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 9a0476f5cc..349ba077dd 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c @@ -310,7 +310,8 @@ static void copy_read_sectors(unsigned char* buf, int wordcount) #endif } -int ata_read_sectors(unsigned long start, +int ata_read_sectors(IF_MV((int drive,)) + unsigned long start, int incount, void* inbuf) { @@ -576,7 +577,8 @@ static void copy_write_sectors(const unsigned char* buf, int wordcount) #endif } -int ata_write_sectors(unsigned long start, +int ata_write_sectors(IF_MV((int drive,)) + unsigned long start, int count, const void* buf) { @@ -669,6 +671,8 @@ int ata_write_sectors(unsigned long start, return ret; } +/* schedule a single sector write, executed with the the next spinup + (volume 0 only, used for config sector) */ extern void ata_delayed_write(unsigned long sector, const void* buf) { memcpy(delayed_sector, buf, SECTOR_SIZE); @@ -676,12 +680,13 @@ extern void ata_delayed_write(unsigned long sector, const void* buf) delayed_write = true; } +/* write the delayed sector to volume 0 */ extern void ata_flush(void) { if ( delayed_write ) { DEBUGF("ata_flush()\n"); delayed_write = false; - ata_write_sectors(delayed_sector_num, 1, delayed_sector); + ata_write_sectors(IF_MV2(0,) delayed_sector_num, 1, delayed_sector); } } diff --git a/firmware/drivers/ata_mmc.c b/firmware/drivers/ata_mmc.c index a61d28cb07..d75309547f 100644 --- a/firmware/drivers/ata_mmc.c +++ b/firmware/drivers/ata_mmc.c @@ -608,17 +608,24 @@ static int send_single_sector(const unsigned char *buf, int timeout) return ret; } -int ata_read_sectors(unsigned long start, - int incount, - void* inbuf) +int ata_read_sectors( +#ifdef HAVE_MULTIVOLUME + int drive, +#endif + unsigned long start, + int incount, + void* inbuf) { int ret = 0; int i; unsigned long addr; unsigned char response; void *inbuf_prev = NULL; - tCardInfo *card = &card_info[current_card]; - + tCardInfo *card; +#ifdef HAVE_MULTIVOLUME + current_card = drive; +#endif + card = &card_info[current_card]; addr = start * SECTOR_SIZE; mutex_lock(&mmc_mutex); @@ -663,7 +670,11 @@ int ata_read_sectors(unsigned long start, return ret; } -int ata_write_sectors(unsigned long start, +int ata_write_sectors( +#ifdef HAVE_MULTIVOLUME + int drive, +#endif + unsigned long start, int count, const void* buf) { @@ -671,7 +682,11 @@ int ata_write_sectors(unsigned long start, int i; unsigned long addr; unsigned char response; - tCardInfo *card = &card_info[current_card]; + tCardInfo *card; +#ifdef HAVE_MULTIVOLUME + current_card = drive; +#endif + card = &card_info[current_card]; if (start == 0) panicf("Writing on sector 0\n"); @@ -733,12 +748,13 @@ extern void ata_delayed_write(unsigned long sector, const void* buf) delayed_write = true; } +/* write the delayed sector to volume 0 */ extern void ata_flush(void) { if ( delayed_write ) { DEBUGF("ata_flush()\n"); delayed_write = false; - ata_write_sectors(delayed_sector_num, 1, delayed_sector); + ata_write_sectors(IF_MV2(0,) delayed_sector_num, 1, delayed_sector); } } diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index 9519ea9825..f0865eb985 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c @@ -219,17 +219,21 @@ struct bpb bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ unsigned int rootdirsectors; /* fixed # of sectors occupied by root dir */ #endif /* #ifdef HAVE_FAT16SUPPORT */ +#ifdef HAVE_MULTIVOLUME + int drive; /* on which physical device is this located */ + bool mounted; /* flag if this volume is mounted */ +#endif }; -static struct bpb fat_bpb; +static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ -static int update_fsinfo(void); -static int first_sector_of_cluster(int cluster); -static int bpb_is_sane(void); -static void *cache_fat_sector(int secnum); +static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); +static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); +static int first_sector_of_cluster(IF_MV2(struct bpb* fat_bpb,) int cluster); +static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) int secnum); static int create_dos_name(const unsigned char *name, unsigned char *newname); -static unsigned int find_free_cluster(unsigned int start); -static int transfer( unsigned int start, int count, char* buf, bool write ); +static unsigned int find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned int start); +static int transfer(IF_MV2(struct bpb* fat_bpb,) unsigned int start, int count, char* buf, bool write ); #define FAT_CACHE_SIZE 0x20 #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) @@ -239,26 +243,35 @@ struct fat_cache_entry int secnum; bool inuse; bool dirty; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_vol ; /* shared cache for all volumes */ +#endif }; static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE]; static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; -static int sec2cluster(unsigned int sec) +static int sec2cluster(IF_MV2(struct bpb* fat_bpb,) unsigned int sec) { - if ( sec < fat_bpb.firstdatasector ) +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + if ( sec < fat_bpb->firstdatasector ) { DEBUGF( "sec2cluster() - Bad sector number (%d)\n", sec); return -1; } - return ((sec - fat_bpb.firstdatasector) / fat_bpb.bpb_secperclus) + 2; + return ((sec - fat_bpb->firstdatasector) / fat_bpb->bpb_secperclus) + 2; } -static int cluster2sec(int cluster) +static int cluster2sec(IF_MV2(struct bpb* fat_bpb,) int cluster) { - int max_cluster = fat_bpb.totalsectors - - fat_bpb.firstdatasector / fat_bpb.bpb_secperclus + 1; +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + int max_cluster = fat_bpb->totalsectors - + fat_bpb->firstdatasector / fat_bpb->bpb_secperclus + 1; if(cluster > max_cluster) { @@ -267,109 +280,141 @@ static int cluster2sec(int cluster) return -1; } - return first_sector_of_cluster(cluster); + return first_sector_of_cluster(IF_MV2(fat_bpb,) cluster); } -static int first_sector_of_cluster(int cluster) +static int first_sector_of_cluster(IF_MV2(struct bpb* fat_bpb,) int cluster) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif #ifdef HAVE_FAT16SUPPORT /* negative clusters (FAT16 root dir) don't get the 2 offset */ int zerocluster = cluster < 0 ? 0 : 2; #else const int zerocluster = 2; #endif - return (cluster - zerocluster) * fat_bpb.bpb_secperclus + fat_bpb.firstdatasector; + return (cluster - zerocluster) * fat_bpb->bpb_secperclus + fat_bpb->firstdatasector; } -int fat_startsector(void) +int fat_startsector(IF_MV_NONVOID(int volume)) { - return fat_bpb.startsector; +#ifndef HAVE_MULTIVOLUME + const int volume = 0; +#endif + struct bpb* fat_bpb = &fat_bpbs[volume]; + return fat_bpb->startsector; } -void fat_size(unsigned int* size, unsigned int* free) +void fat_size(IF_MV2(int volume,) unsigned int* size, unsigned int* free) { +#ifndef HAVE_MULTIVOLUME + const int volume = 0; +#endif + struct bpb* fat_bpb = &fat_bpbs[volume]; if (size) - *size = fat_bpb.dataclusters * fat_bpb.bpb_secperclus / 2; + *size = fat_bpb->dataclusters * fat_bpb->bpb_secperclus / 2; if (free) - *free = fat_bpb.fsinfo.freecount * fat_bpb.bpb_secperclus / 2; + *free = fat_bpb->fsinfo.freecount * fat_bpb->bpb_secperclus / 2; } -int fat_mount(int startsector) +void fat_init(void) { - unsigned char buf[SECTOR_SIZE]; - int rc; - int datasec; unsigned int i; - + /* mark the FAT cache as unused */ for(i = 0;i < FAT_CACHE_SIZE;i++) { fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */ fat_cache[i].inuse = false; fat_cache[i].dirty = false; +#ifdef HAVE_MULTIVOLUME + fat_cache[i].fat_vol = NULL; +#endif + } +#ifdef HAVE_MULTIVOLUME + /* mark the possible volumes as not mounted */ + for (i=0; istartsector = startsector; +#ifdef HAVE_MULTIVOLUME + fat_bpb->drive = drive; +#endif + + fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); + 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_totsec16 = BYTES2INT16(buf,BPB_TOTSEC16); + fat_bpb->bpb_media = buf[BPB_MEDIA]; + fat_bpb->bpb_fatsz16 = BYTES2INT16(buf,BPB_FATSZ16); + fat_bpb->bpb_fatsz32 = BYTES2INT32(buf,BPB_FATSZ32); + fat_bpb->bpb_totsec32 = BYTES2INT32(buf,BPB_TOTSEC32); + fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD); /* calculate a few commonly used values */ - if (fat_bpb.bpb_fatsz16 != 0) - fat_bpb.fatsize = fat_bpb.bpb_fatsz16; + if (fat_bpb->bpb_fatsz16 != 0) + fat_bpb->fatsize = fat_bpb->bpb_fatsz16; else - fat_bpb.fatsize = fat_bpb.bpb_fatsz32; + fat_bpb->fatsize = fat_bpb->bpb_fatsz32; - if (fat_bpb.bpb_totsec16 != 0) - fat_bpb.totalsectors = fat_bpb.bpb_totsec16; + if (fat_bpb->bpb_totsec16 != 0) + fat_bpb->totalsectors = fat_bpb->bpb_totsec16; else - fat_bpb.totalsectors = fat_bpb.bpb_totsec32; + fat_bpb->totalsectors = fat_bpb->bpb_totsec32; #ifdef HAVE_FAT16SUPPORT - fat_bpb.bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); - fat_bpb.rootdirsectors = ((fat_bpb.bpb_rootentcnt * 32) - + (fat_bpb.bpb_bytspersec - 1)) / fat_bpb.bpb_bytspersec; + fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); + fat_bpb->rootdirsectors = ((fat_bpb->bpb_rootentcnt * 32) + + (fat_bpb->bpb_bytspersec - 1)) / fat_bpb->bpb_bytspersec; #endif /* #ifdef HAVE_FAT16SUPPORT */ - fat_bpb.firstdatasector = fat_bpb.bpb_rsvdseccnt + fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt #ifdef HAVE_FAT16SUPPORT - + fat_bpb.rootdirsectors + + fat_bpb->rootdirsectors #endif - + fat_bpb.bpb_numfats * fat_bpb.fatsize; + + fat_bpb->bpb_numfats * fat_bpb->fatsize; /* Determine FAT type */ - datasec = fat_bpb.totalsectors - fat_bpb.firstdatasector; - fat_bpb.dataclusters = datasec / fat_bpb.bpb_secperclus; + datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; + fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; #ifdef TEST_FAT /* we are sometimes testing with "illegally small" fat32 images, so we don't use the proper fat32 test case for test code */ - if ( fat_bpb.bpb_fatsz16 ) + if ( fat_bpb->bpb_fatsz16 ) #else - if ( fat_bpb.dataclusters < 65525 ) + if ( fat_bpb->dataclusters < 65525 ) #endif { /* FAT16 */ #ifdef HAVE_FAT16SUPPORT - fat_bpb.is_fat16 = true; - if (fat_bpb.dataclusters < 4085) + fat_bpb->is_fat16 = true; + if (fat_bpb->dataclusters < 4085) { /* FAT12 */ DEBUGF("This is FAT12. Go away!\n"); return -2; @@ -381,26 +426,26 @@ int fat_mount(int startsector) } #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { /* FAT16 specific part of BPB */ int dirclusters; - fat_bpb.rootdirsector = fat_bpb.bpb_rsvdseccnt - + fat_bpb.bpb_numfats * fat_bpb.bpb_fatsz16; - dirclusters = ((fat_bpb.rootdirsectors + fat_bpb.bpb_secperclus - 1) - / fat_bpb.bpb_secperclus); /* rounded up, to full clusters */ + fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt + + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; + dirclusters = ((fat_bpb->rootdirsectors + fat_bpb->bpb_secperclus - 1) + / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ /* I assign negative pseudo cluster numbers for the root directory, their range is counted upward until -1. */ - fat_bpb.bpb_rootclus = 0 - dirclusters; /* backwards, before the data */ + fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */ } else #endif /* #ifdef HAVE_FAT16SUPPORT */ { /* FAT32 specific part of BPB */ - fat_bpb.bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS); - fat_bpb.bpb_fsinfo = BYTES2INT16(buf,BPB_FSINFO); - fat_bpb.rootdirsector = cluster2sec(fat_bpb.bpb_rootclus); + fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS); + fat_bpb->bpb_fsinfo = BYTES2INT16(buf,BPB_FSINFO); + fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) fat_bpb->bpb_rootclus); } - rc = bpb_is_sane(); + rc = bpb_is_sane(IF_MV(fat_bpb)); if (rc < 0) { DEBUGF( "fat_mount() - BPB is not sane\n"); @@ -408,59 +453,68 @@ int fat_mount(int startsector) } #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { - fat_bpb.fsinfo.freecount = 0xffffffff; /* force recalc below */ - fat_bpb.fsinfo.nextfree = 0xffffffff; + fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ + fat_bpb->fsinfo.nextfree = 0xffffffff; } else #endif /* #ifdef HAVE_FAT16SUPPORT */ { /* Read the fsinfo sector */ - rc = ata_read_sectors(startsector + fat_bpb.bpb_fsinfo, 1, buf); + rc = ata_read_sectors(IF_MV2(drive,) + startsector + fat_bpb->bpb_fsinfo, 1, buf); 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(buf, FSINFO_FREECOUNT); + fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); } /* calculate freecount if unset */ - if ( fat_bpb.fsinfo.freecount == 0xffffffff ) + if ( fat_bpb->fsinfo.freecount == 0xffffffff ) { - fat_recalc_free(); + fat_recalc_free(IF_MV(volume)); } - LDEBUGF("Freecount: %d\n",fat_bpb.fsinfo.freecount); - LDEBUGF("Nextfree: 0x%x\n",fat_bpb.fsinfo.nextfree); - LDEBUGF("Cluster count: 0x%x\n",fat_bpb.dataclusters); - LDEBUGF("Sectors per cluster: %d\n",fat_bpb.bpb_secperclus); - LDEBUGF("FAT sectors: 0x%x\n",fat_bpb.fatsize); + LDEBUGF("Freecount: %d\n",fat_bpb->fsinfo.freecount); + LDEBUGF("Nextfree: 0x%x\n",fat_bpb->fsinfo.nextfree); + LDEBUGF("Cluster count: 0x%x\n",fat_bpb->dataclusters); + LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus); + LDEBUGF("FAT sectors: 0x%x\n",fat_bpb->fatsize); + +#ifdef HAVE_MULTIVOLUME + fat_bpb->mounted = true; +#endif return 0; } -void fat_recalc_free(void) +void fat_recalc_free(IF_MV_NONVOID(int volume)) { +#ifndef HAVE_MULTIVOLUME + const int volume = 0; +#endif + struct bpb* fat_bpb = &fat_bpbs[volume]; int free = 0; unsigned i; #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { - for (i = 0; ifatsize; i++) { unsigned int j; - unsigned short* fat = cache_fat_sector(i); + unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i); for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; - if ( c > fat_bpb.dataclusters+1 ) /* nr 0 is unused */ + if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ break; if (SWAB16(fat[j]) == 0x0000) { free++; - if ( fat_bpb.fsinfo.nextfree == 0xffffffff ) - fat_bpb.fsinfo.nextfree = c; + if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) + fat_bpb->fsinfo.nextfree = c; } } } @@ -468,116 +522,153 @@ void fat_recalc_free(void) else #endif /* #ifdef HAVE_FAT16SUPPORT */ { - for (i = 0; ifatsize; i++) { unsigned int j; - unsigned int* fat = cache_fat_sector(i); + unsigned int* fat = cache_fat_sector(IF_MV2(fat_bpb,) i); for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { unsigned int c = i * CLUSTERS_PER_FAT_SECTOR + j; - if ( c > fat_bpb.dataclusters+1 ) /* nr 0 is unused */ + if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ break; if (!(SWAB32(fat[j]) & 0x0fffffff)) { free++; - if ( fat_bpb.fsinfo.nextfree == 0xffffffff ) - fat_bpb.fsinfo.nextfree = c; + if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) + fat_bpb->fsinfo.nextfree = c; } } } } - fat_bpb.fsinfo.freecount = free; - update_fsinfo(); + fat_bpb->fsinfo.freecount = free; + update_fsinfo(IF_MV(fat_bpb)); } -static int bpb_is_sane(void) +static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) { - if(fat_bpb.bpb_bytspersec != 512) +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + if(fat_bpb->bpb_bytspersec != 512) { DEBUGF( "bpb_is_sane() - Error: sector size is not 512 (%d)\n", - fat_bpb.bpb_bytspersec); + fat_bpb->bpb_bytspersec); return -1; } - if(fat_bpb.bpb_secperclus * fat_bpb.bpb_bytspersec > 128*1024) + if(fat_bpb->bpb_secperclus * fat_bpb->bpb_bytspersec > 128*1024) { DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " "(%d * %d = %d)\n", - fat_bpb.bpb_bytspersec, fat_bpb.bpb_secperclus, - fat_bpb.bpb_bytspersec * fat_bpb.bpb_secperclus); + fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, + fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); return -2; } - if(fat_bpb.bpb_numfats != 2) + if(fat_bpb->bpb_numfats != 2) { DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n", - fat_bpb.bpb_numfats); + fat_bpb->bpb_numfats); } - if(fat_bpb.bpb_media != 0xf0 && fat_bpb.bpb_media < 0xf8) + if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) { DEBUGF( "bpb_is_sane() - Warning: Non-standard " "media type (0x%02x)\n", - fat_bpb.bpb_media); + fat_bpb->bpb_media); } - if(fat_bpb.last_word != 0xaa55) + if(fat_bpb->last_word != 0xaa55) { DEBUGF( "bpb_is_sane() - Error: Last word is not " - "0xaa55 (0x%04x)\n", fat_bpb.last_word); + "0xaa55 (0x%04x)\n", fat_bpb->last_word); return -3; } - if (fat_bpb.fsinfo.freecount > - (fat_bpb.totalsectors - fat_bpb.firstdatasector)/ - fat_bpb.bpb_secperclus) + 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); + "(0x%04x)\n", fat_bpb->fsinfo.freecount); return -4; } return 0; } -static void *cache_fat_sector(int fatsector) +static void flush_fat_sector(struct fat_cache_entry *fce, + unsigned char *sectorbuf) { - int secnum = fatsector + fat_bpb.bpb_rsvdseccnt; + int rc; + int 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 = ata_write_sectors(IF_MV2(fce->fat_vol->drive,) + secnum, 1, + sectorbuf); + if(rc < 0) + { + panicf("cache_fat_sector() - Could not write sector %d" + " (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 = ata_write_sectors(IF_MV2(fce->fat_vol->drive,) + secnum, 1, sectorbuf); + if(rc < 0) + { + panicf("cache_fat_sector() - Could not write sector %d" + " (error %d)\n", + secnum, rc); + } + } + fce->dirty = false; +} + +static void *cache_fat_sector(IF_MV2(struct bpb* fat_bpb,) int fatsector) +{ +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + int 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; /* Delete the cache entry if it isn't the sector we want */ - if(fce->inuse && fce->secnum != secnum) + if(fce->inuse && (fce->secnum != secnum +#ifdef HAVE_MULTIVOLUME + || fce->fat_vol != fat_bpb +#endif + )) { /* Write back if it is dirty */ if(fce->dirty) { - rc = ata_write_sectors(fce->secnum+fat_bpb.startsector, 1, - sectorbuf); - if(rc < 0) - { - panicf("cache_fat_sector() - Could not write sector %d" - " (error %d)\n", - secnum, rc); - } - if(fat_bpb.bpb_numfats > 1) - { - /* Write to the second FAT */ - rc = ata_write_sectors(fce->secnum+fat_bpb.startsector+ - fat_bpb.fatsize, 1, sectorbuf); - if(rc < 0) - { - panicf("cache_fat_sector() - Could not write sector %d" - " (error %d)\n", - secnum + fat_bpb.fatsize, rc); - } - } + flush_fat_sector(fce, sectorbuf); } - fce->secnum = 8; /* Normally an unused sector */ - fce->dirty = false; fce->inuse = false; } /* Load the sector if it is not cached */ if(!fce->inuse) { - rc = ata_read_sectors(secnum + fat_bpb.startsector,1, + rc = ata_read_sectors(IF_MV2(fat_bpb->drive,) + secnum + fat_bpb->startsector,1, sectorbuf); if(rc < 0) { @@ -587,26 +678,32 @@ static void *cache_fat_sector(int fatsector) } fce->inuse = true; fce->secnum = secnum; +#ifdef HAVE_MULTIVOLUME + fce->fat_vol = fat_bpb; +#endif } return sectorbuf; } -static unsigned int find_free_cluster(unsigned int startcluster) +static unsigned int find_free_cluster(IF_MV2(struct bpb* fat_bpb,) unsigned int startcluster) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif unsigned int sector; unsigned int offset; unsigned int i; #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; - for (i = 0; ifatsize; i++) { unsigned int j; - unsigned int nr = (i + sector) % fat_bpb.fatsize; - unsigned short* fat = cache_fat_sector(nr); + unsigned int nr = (i + sector) % fat_bpb->fatsize; + unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr); if ( !fat ) break; for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { @@ -615,10 +712,10 @@ static unsigned int find_free_cluster(unsigned int startcluster) unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k; /* Ignore the reserved clusters 0 & 1, and also cluster numbers out of bounds */ - if ( c < 2 || c > fat_bpb.dataclusters+1 ) + if ( c < 2 || c > fat_bpb->dataclusters+1 ) continue; LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c); - fat_bpb.fsinfo.nextfree = c; + fat_bpb->fsinfo.nextfree = c; return c; } } @@ -631,10 +728,10 @@ static unsigned int find_free_cluster(unsigned int startcluster) sector = startcluster / CLUSTERS_PER_FAT_SECTOR; offset = startcluster % CLUSTERS_PER_FAT_SECTOR; - for (i = 0; ifatsize; i++) { unsigned int j; - unsigned int nr = (i + sector) % fat_bpb.fatsize; - unsigned int* fat = cache_fat_sector(nr); + unsigned int nr = (i + sector) % fat_bpb->fatsize; + unsigned int* fat = cache_fat_sector(IF_MV2(fat_bpb,) nr); if ( !fat ) break; for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { @@ -643,10 +740,10 @@ static unsigned int find_free_cluster(unsigned int startcluster) unsigned int c = nr * CLUSTERS_PER_FAT_SECTOR + k; /* Ignore the reserved clusters 0 & 1, and also cluster numbers out of bounds */ - if ( c < 2 || c > fat_bpb.dataclusters+1 ) + if ( c < 2 || c > fat_bpb->dataclusters+1 ) continue; LDEBUGF("find_free_cluster(%x) == %x\n",startcluster,c); - fat_bpb.fsinfo.nextfree = c; + fat_bpb->fsinfo.nextfree = c; return c; } } @@ -658,10 +755,13 @@ static unsigned int find_free_cluster(unsigned int startcluster) return 0; /* 0 is an illegal cluster number */ } -static int update_fat_entry(unsigned int entry, unsigned int val) +static int update_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned int entry, unsigned int val) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { int sector = entry / CLUSTERS_PER_FAT16_SECTOR; int offset = entry % CLUSTERS_PER_FAT16_SECTOR; @@ -677,24 +777,24 @@ static int update_fat_entry(unsigned int entry, unsigned int val) if ( entry < 2 ) panicf("Updating reserved FAT entry %d.\n",entry); - sec = cache_fat_sector(sector); + sec = cache_fat_sector(IF_MV2(fat_bpb,) sector); if (!sec) { DEBUGF( "update_entry() - Could not cache sector %d\n", sector); return -1; } - fat_cache[(sector + fat_bpb.bpb_rsvdseccnt) & FAT_CACHE_MASK].dirty = true; + fat_cache[(sector + fat_bpb->bpb_rsvdseccnt) & FAT_CACHE_MASK].dirty = true; if ( val ) { - if (SWAB16(sec[offset]) == 0x0000 && fat_bpb.fsinfo.freecount > 0) - fat_bpb.fsinfo.freecount--; + if (SWAB16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) + fat_bpb->fsinfo.freecount--; } else { if (SWAB16(sec[offset])) - fat_bpb.fsinfo.freecount++; + fat_bpb->fsinfo.freecount++; } - LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb.fsinfo.freecount); + LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb->fsinfo.freecount); sec[offset] = SWAB16(val); } @@ -713,25 +813,25 @@ static int update_fat_entry(unsigned int entry, unsigned int val) if ( entry < 2 ) panicf("Updating reserved FAT entry %d.\n",entry); - sec = cache_fat_sector(sector); + sec = cache_fat_sector(IF_MV2(fat_bpb,) sector); if (!sec) { DEBUGF( "update_entry() - Could not cache sector %d\n", sector); return -1; } - fat_cache[(sector + fat_bpb.bpb_rsvdseccnt) & FAT_CACHE_MASK].dirty = true; + fat_cache[(sector + fat_bpb->bpb_rsvdseccnt) & FAT_CACHE_MASK].dirty = true; if ( val ) { if (!(SWAB32(sec[offset]) & 0x0fffffff) && - fat_bpb.fsinfo.freecount > 0) - fat_bpb.fsinfo.freecount--; + fat_bpb->fsinfo.freecount > 0) + fat_bpb->fsinfo.freecount--; } else { if (SWAB32(sec[offset]) & 0x0fffffff) - fat_bpb.fsinfo.freecount++; + fat_bpb->fsinfo.freecount++; } - LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb.fsinfo.freecount); + LDEBUGF("update_fat_entry: %d free clusters\n", fat_bpb->fsinfo.freecount); /* don't change top 4 bits */ sec[offset] &= SWAB32(0xf0000000); @@ -741,16 +841,19 @@ static int update_fat_entry(unsigned int entry, unsigned int val) return 0; } -static int read_fat_entry(unsigned int entry) +static int read_fat_entry(IF_MV2(struct bpb* fat_bpb,) unsigned int entry) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { int sector = entry / CLUSTERS_PER_FAT16_SECTOR; int offset = entry % CLUSTERS_PER_FAT16_SECTOR; unsigned short* sec; - sec = cache_fat_sector(sector); + sec = cache_fat_sector(IF_MV2(fat_bpb,) sector); if (!sec) { DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector); @@ -766,7 +869,7 @@ static int read_fat_entry(unsigned int entry) int offset = entry % CLUSTERS_PER_FAT_SECTOR; unsigned int* sec; - sec = cache_fat_sector(sector); + sec = cache_fat_sector(IF_MV2(fat_bpb,) sector); if (!sec) { DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector); @@ -777,20 +880,23 @@ static int read_fat_entry(unsigned int entry) } } -static int get_next_cluster(int cluster) +static int get_next_cluster(IF_MV2(struct bpb* fat_bpb,) int cluster) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int next_cluster; int eof_mark = FAT_EOF_MARK; #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { eof_mark &= 0xFFFF; /* only 16 bit */ if (cluster < 0) /* FAT16 root dir */ return cluster + 1; /* don't use the FAT */ } #endif - next_cluster = read_fat_entry(cluster); + next_cluster = read_fat_entry(IF_MV2(fat_bpb,) cluster); /* is this last cluster in chain? */ if ( next_cluster >= eof_mark ) @@ -799,31 +905,36 @@ static int get_next_cluster(int cluster) return next_cluster; } -static int update_fsinfo(void) +static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif unsigned char fsinfo[SECTOR_SIZE]; unsigned int* intptr; int rc; #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) return 0; /* FAT16 has no FsInfo */ #endif /* #ifdef HAVE_FAT16SUPPORT */ /* update fsinfo */ - rc = ata_read_sectors(fat_bpb.startsector + fat_bpb.bpb_fsinfo, 1,fsinfo); + rc = ata_read_sectors(IF_MV2(fat_bpb->drive,) + fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); if (rc < 0) { DEBUGF( "flush_fat() - Couldn't read FSInfo (error code %d)\n", rc); return rc * 10 - 1; } intptr = (int*)&(fsinfo[FSINFO_FREECOUNT]); - *intptr = SWAB32(fat_bpb.fsinfo.freecount); + *intptr = SWAB32(fat_bpb->fsinfo.freecount); intptr = (int*)&(fsinfo[FSINFO_NEXTFREE]); - *intptr = SWAB32(fat_bpb.fsinfo.nextfree); + *intptr = SWAB32(fat_bpb->fsinfo.nextfree); - rc = ata_write_sectors(fat_bpb.startsector + fat_bpb.bpb_fsinfo,1,fsinfo); + rc = ata_write_sectors(IF_MV2(fat_bpb->drive,) + fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); if (rc < 0) { DEBUGF( "flush_fat() - Couldn't write FSInfo (error code %d)\n", rc); @@ -833,48 +944,24 @@ static int update_fsinfo(void) return 0; } -static int flush_fat(void) +static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) { int i; int rc; unsigned char *sec; - int secnum; LDEBUGF("flush_fat()\n"); for(i = 0;i < FAT_CACHE_SIZE;i++) { - if(fat_cache[i].inuse && fat_cache[i].dirty) + struct fat_cache_entry *fce = &fat_cache[i]; + if(fce->inuse && fce->dirty) { - 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 */ - rc = ata_write_sectors(secnum, 1, sec); - if(rc) - { - DEBUGF( "flush_fat() - Couldn't write" - " sector %d (error %d)\n", secnum, rc); - return rc * 10 - 1; - } - - if(fat_bpb.bpb_numfats > 1 ) - { - /* Write to the second FAT */ - rc = ata_write_sectors(secnum + fat_bpb.fatsize, 1, sec); - if (rc) - { - DEBUGF( "flush_fat() - Couldn't write" - " sector %d (error %d)\n", - secnum + fat_bpb.fatsize, rc); - return rc * 10 - 2; - } - } - fat_cache[i].dirty = false; + flush_fat_sector(fce, sec); } } - rc = update_fsinfo(); + rc = update_fsinfo(IF_MV(fat_bpb)); if (rc < 0) return rc * 10 - 3; @@ -1108,6 +1195,11 @@ static int add_dir_entry(struct fat_dir* dir, bool is_directory, bool dotdir) { +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif unsigned char buf[SECTOR_SIZE]; unsigned char shortname[16]; int rc; @@ -1122,6 +1214,10 @@ static int add_dir_entry(struct fat_dir* dir, LDEBUGF( "add_dir_entry(%s,%x)\n", name, file->firstcluster); +#ifdef HAVE_MULTIVOLUME + file->volume = dir->file.volume; /* inherit the volume, to make sure */ +#endif + /* The "." and ".." directory entries must not be long names */ if(dotdir) { int i; @@ -1173,7 +1269,7 @@ static int add_dir_entry(struct fat_dir* dir, rc = fat_readwrite(&dir->file, 1, buf, true); if (rc < 1) return rc * 10 - 3; - } while (dir->file.sectornum < (int)fat_bpb.bpb_secperclus); + } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); } if (rc < 0) { DEBUGF( "add_dir_entry() - Couldn't read dir" @@ -1277,7 +1373,7 @@ static int add_dir_entry(struct fat_dir* dir, if (rc < 1) return rc * 10 - 9; memset(buf, 0, sizeof buf); - } while (dir->file.sectornum < (int)fat_bpb.bpb_secperclus); + } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); } else { @@ -1288,7 +1384,7 @@ static int add_dir_entry(struct fat_dir* dir, if (rc < 1) return rc * 10 - 9; /* Same error code as above, can't be more than -9 */ - } while (dir->file.sectornum < (int)fat_bpb.bpb_secperclus); + } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); } } @@ -1368,7 +1464,7 @@ static int update_short_entry( struct fat_file* file, int size, int attr ) file->firstcluster, file->direntry, size); /* create a temporary file handle for the dir holding this file */ - rc = fat_open(file->dircluster, &dir, NULL); + rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); if (rc < 0) return rc * 10 - 1; @@ -1445,7 +1541,8 @@ static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) return 1; } -int fat_open(unsigned int startcluster, +int fat_open(IF_MV2(int volume,) + unsigned int startcluster, struct fat_file *file, const struct fat_dir* dir) { @@ -1455,6 +1552,15 @@ int fat_open(unsigned int startcluster, file->clusternum = 0; file->sectornum = 0; file->eof = false; +#ifdef HAVE_MULTIVOLUME + file->volume = volume; + /* fixme: remove error check when done */ + if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) + { + LDEBUGF("fat_open() illegal volume %d\n", volume); + return -1; + } +#endif /* remember where the file's dir entry is located */ if ( dir ) { @@ -1469,19 +1575,26 @@ int fat_open(unsigned int startcluster, #ifdef HAVE_FAT16SUPPORT /* special function to open the FAT16 root dir, which may not be on cluster boundary */ -int fat_open_root(struct fat_file *file) +static int fat_open_root(IF_MV2(int volume,) + struct fat_file *file) { int dirclusters; - file->firstcluster = fat_bpb.bpb_rootclus; - file->lastcluster = fat_bpb.bpb_rootclus; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[volume]; + file->volume = volume; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif + file->firstcluster = fat_bpb->bpb_rootclus; + file->lastcluster = fat_bpb->bpb_rootclus; file->lastsector = 0; file->clusternum = 0; - dirclusters = 0 - fat_bpb.bpb_rootclus; /* bpb_rootclus is the negative */ - file->sectornum = (dirclusters * fat_bpb.bpb_secperclus) - - fat_bpb.rootdirsectors; /* cluster sectors minus real sectors */ + dirclusters = 0 - fat_bpb->bpb_rootclus; /* bpb_rootclus is the negative */ + file->sectornum = (dirclusters * fat_bpb->bpb_secperclus) + - fat_bpb->rootdirsectors; /* cluster sectors minus real sectors */ file->eof = false; - LDEBUGF("fat_open_root(), sector %d\n", cluster2sec(fat_bpb.bpb_rootclus)); + LDEBUGF("fat_open_root(), sector %d\n", cluster2sec(fat_bpb->bpb_rootclus)); return 0; } #endif /* #ifdef HAVE_FAT16SUPPORT */ @@ -1510,6 +1623,11 @@ int fat_create_dir(const char* name, struct fat_dir* newdir, struct fat_dir* dir) { +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif unsigned char buf[SECTOR_SIZE]; int i; int sector; @@ -1527,17 +1645,17 @@ int fat_create_dir(const char* name, return rc * 10 - 1; /* Allocate a new cluster for the directory */ - newdir->file.firstcluster = find_free_cluster(fat_bpb.fsinfo.nextfree); + newdir->file.firstcluster = find_free_cluster(IF_MV2(fat_bpb,) fat_bpb->fsinfo.nextfree); if(newdir->file.firstcluster == 0) return -1; - update_fat_entry(newdir->file.firstcluster, FAT_EOF_MARK); + update_fat_entry(IF_MV2(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); /* Clear the entire cluster */ memset(buf, 0, sizeof buf); - sector = cluster2sec(newdir->file.firstcluster); - for(i = 0;i < (int)fat_bpb.bpb_secperclus;i++) { - rc = transfer( sector + i, 1, buf, true ); + 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; } @@ -1555,7 +1673,7 @@ int fat_create_dir(const char* name, return rc * 10 - 4; /* The root cluster is cluster 0 in the ".." entry */ - if(dir->file.firstcluster == fat_bpb.bpb_rootclus) + if(dir->file.firstcluster == fat_bpb->bpb_rootclus) dummyfile.firstcluster = 0; else dummyfile.firstcluster = dir->file.firstcluster; @@ -1564,7 +1682,7 @@ int fat_create_dir(const char* name, /* Set the firstcluster field in the direntry */ update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY); - rc = flush_fat(); + rc = flush_fat(IF_MV(fat_bpb)); if (rc < 0) return rc * 10 - 5; @@ -1576,15 +1694,18 @@ int fat_truncate(const struct fat_file *file) /* truncate trailing clusters */ int next; int last = file->lastcluster; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#endif LDEBUGF("fat_truncate(%x, %x)\n", file->firstcluster, last); - for ( last = get_next_cluster(last); last; last = next ) { - next = get_next_cluster(last); - update_fat_entry(last,0); + for ( last = get_next_cluster(IF_MV2(fat_bpb,) last); last; last = next ) { + next = get_next_cluster(IF_MV2(fat_bpb,) last); + update_fat_entry(IF_MV2(fat_bpb,) last,0); } if (file->lastcluster) - update_fat_entry(file->lastcluster,FAT_EOF_MARK); + update_fat_entry(IF_MV2(fat_bpb,) file->lastcluster,FAT_EOF_MARK); return 0; } @@ -1592,12 +1713,15 @@ int fat_truncate(const struct fat_file *file) int fat_closewrite(struct fat_file *file, int size, int attr) { int rc; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#endif LDEBUGF("fat_closewrite(size=%d)\n",size); if (!size) { /* empty file */ if ( file->firstcluster ) { - update_fat_entry(file->firstcluster, 0); + update_fat_entry(IF_MV2(fat_bpb,) file->firstcluster, 0); file->firstcluster = 0; } } @@ -1608,21 +1732,26 @@ int fat_closewrite(struct fat_file *file, int size, int attr) return rc * 10 - 1; } - flush_fat(); + flush_fat(IF_MV(fat_bpb)); #ifdef TEST_FAT if ( file->firstcluster ) { /* debug */ +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int count = 0; int len; int next; for ( next = file->firstcluster; next; - next = get_next_cluster(next) ) + next = get_next_cluster(IF_MV2(fat_bpb,) next) ) LDEBUGF("cluster %d: %x\n", count++, next); - len = count * fat_bpb.bpb_secperclus * SECTOR_SIZE; + len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; LDEBUGF("File is %d clusters (chainlen=%d, size=%d)\n", count, len, size ); - if ( len > size + fat_bpb.bpb_secperclus * SECTOR_SIZE) + if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) panicf("Cluster chain is too long\n"); if ( len < size ) panicf("Cluster chain is too short\n"); @@ -1632,17 +1761,18 @@ int fat_closewrite(struct fat_file *file, int size, int attr) return 0; } -static int free_direntries(int dircluster, int startentry, int numentries) +static int free_direntries(struct fat_file* file) { unsigned char buf[SECTOR_SIZE]; struct fat_file dir; - unsigned int entry = startentry - numentries + 1; + int numentries = file->direntries; + unsigned int entry = file->direntry - numentries + 1; unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR; int i; int rc; /* create a temporary file handle for the dir holding this file */ - rc = fat_open(dircluster, &dir, NULL); + rc = fat_open(IF_MV2(file->volume,) file->dircluster, &dir, NULL); if (rc < 0) return rc * 10 - 1; @@ -1698,19 +1828,20 @@ int fat_remove(struct fat_file* file) { int next, last = file->firstcluster; int rc; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#endif LDEBUGF("fat_remove(%x)\n",last); while ( last ) { - next = get_next_cluster(last); - update_fat_entry(last,0); + next = get_next_cluster(IF_MV2(fat_bpb,) last); + update_fat_entry(IF_MV2(fat_bpb,) last,0); last = next; } if ( file->dircluster ) { - rc = free_direntries(file->dircluster, - file->direntry, - file->direntries); + rc = free_direntries(file); if (rc < 0) return rc * 10 - 1; } @@ -1718,7 +1849,7 @@ int fat_remove(struct fat_file* file) file->firstcluster = 0; file->dircluster = 0; - rc = flush_fat(); + rc = flush_fat(IF_MV(fat_bpb)); if (rc < 0) return rc * 10 - 2; @@ -1734,6 +1865,9 @@ int fat_rename(struct fat_file* file, int rc; struct fat_dir olddir; struct fat_file newfile = *file; +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#endif if ( !file->dircluster ) { DEBUGF("File has no dir cluster!\n"); @@ -1741,7 +1875,7 @@ int fat_rename(struct fat_file* file, } /* create a temporary file handle */ - rc = fat_opendir(&olddir, file->dircluster, NULL); + rc = fat_opendir(IF_MV2(file->volume,) &olddir, file->dircluster, NULL); if (rc < 0) return rc * 10 - 2; @@ -1756,11 +1890,11 @@ int fat_rename(struct fat_file* file, return rc * 10 - 4; /* remove old name */ - rc = free_direntries(file->dircluster, file->direntry, file->direntries); + rc = free_direntries(file); if (rc < 0) return rc * 10 - 5; - rc = flush_fat(); + rc = flush_fat(IF_MV(fat_bpb)); if (rc < 0) return rc * 10 - 6; @@ -1771,19 +1905,24 @@ static int next_write_cluster(struct fat_file* file, int oldcluster, int* newsector) { +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int cluster = 0; int sector; LDEBUGF("next_write_cluster(%x,%x)\n",file->firstcluster, oldcluster); if (oldcluster) - cluster = get_next_cluster(oldcluster); + cluster = get_next_cluster(IF_MV2(fat_bpb,) oldcluster); if (!cluster) { if (oldcluster > 0) - cluster = find_free_cluster(oldcluster+1); + cluster = find_free_cluster(IF_MV2(fat_bpb,) oldcluster+1); else if (oldcluster == 0) - cluster = find_free_cluster(fat_bpb.fsinfo.nextfree); + cluster = find_free_cluster(IF_MV2(fat_bpb,) fat_bpb->fsinfo.nextfree); #ifdef HAVE_FAT16SUPPORT else /* negative, pseudo-cluster of the root dir */ return 0; /* impossible to append something to the root */ @@ -1791,14 +1930,14 @@ static int next_write_cluster(struct fat_file* file, if (cluster) { if (oldcluster) - update_fat_entry(oldcluster, cluster); + update_fat_entry(IF_MV2(fat_bpb,) oldcluster, cluster); else file->firstcluster = cluster; - update_fat_entry(cluster, FAT_EOF_MARK); + update_fat_entry(IF_MV2(fat_bpb,) cluster, FAT_EOF_MARK); } else { #ifdef TEST_FAT - if (fat_bpb.fsinfo.freecount>0) + if (fat_bpb->fsinfo.freecount>0) panicf("There is free space, but find_free_cluster() " "didn't find it!\n"); #endif @@ -1806,7 +1945,7 @@ static int next_write_cluster(struct fat_file* file, return 0; } } - sector = cluster2sec(cluster); + sector = cluster2sec(IF_MV2(fat_bpb,) cluster); if (sector<0) return 0; @@ -1814,30 +1953,36 @@ static int next_write_cluster(struct fat_file* file, return cluster; } -static int transfer( unsigned int start, int count, char* buf, bool write ) +static int transfer(IF_MV2(struct bpb* fat_bpb,) + unsigned int start, int count, char* buf, bool write ) { +#ifndef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int rc; LDEBUGF("transfer(s=%x, c=%x, %s)\n", - start+ fat_bpb.startsector, count, write?"write":"read"); + start+ fat_bpb->startsector, count, write?"write":"read"); if (write) { unsigned int firstallowed; #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) - firstallowed = fat_bpb.rootdirsector; + if (fat_bpb->is_fat16) + firstallowed = fat_bpb->rootdirsector; else #endif - firstallowed = fat_bpb.firstdatasector; + firstallowed = fat_bpb->firstdatasector; if (start < firstallowed) panicf("Write %d before data\n", firstallowed - start); - if (start + count > fat_bpb.totalsectors) + if (start + count > fat_bpb->totalsectors) panicf("Write %d after data\n", - start + count - fat_bpb.totalsectors); - rc = ata_write_sectors(start + fat_bpb.startsector, count, buf); + start + count - fat_bpb->totalsectors); + rc = ata_write_sectors(IF_MV2(fat_bpb->drive,) + start + fat_bpb->startsector, count, buf); } else - rc = ata_read_sectors(start + fat_bpb.startsector, count, buf); + rc = ata_read_sectors(IF_MV2(fat_bpb->drive,) + start + fat_bpb->startsector, count, buf); if (rc < 0) { DEBUGF( "transfer() - Couldn't %s sector %x" " (error code %d)\n", @@ -1851,6 +1996,11 @@ static int transfer( unsigned int start, int count, char* buf, bool write ) int fat_readwrite( struct fat_file *file, int sectorcount, void* buf, bool write ) { +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int cluster = file->lastcluster; int sector = file->lastsector; int clusternum = file->clusternum; @@ -1871,13 +2021,13 @@ int fat_readwrite( struct fat_file *file, int sectorcount, /* find sequential sectors and write them all at once */ for (i=0; (i < sectorcount) && (sector > -1); i++ ) { numsec++; - if ( numsec > (int)fat_bpb.bpb_secperclus || !cluster ) { + if ( numsec > (int)fat_bpb->bpb_secperclus || !cluster ) { int oldcluster = cluster; if (write) cluster = next_write_cluster(file, cluster, §or); else { - cluster = get_next_cluster(cluster); - sector = cluster2sec(cluster); + cluster = get_next_cluster(IF_MV2(fat_bpb,) cluster); + sector = cluster2sec(IF_MV2(fat_bpb,) cluster); } clusternum++; @@ -1902,7 +2052,7 @@ int fat_readwrite( struct fat_file *file, int sectorcount, sector++; else { /* look up first sector of file */ - sector = cluster2sec(file->firstcluster); + sector = cluster2sec(IF_MV2(fat_bpb,) file->firstcluster); numsec=1; } } @@ -1913,7 +2063,7 @@ int fat_readwrite( struct fat_file *file, int sectorcount, if ( ((sector != first) && (sector != last+1)) || /* not sequential */ (last-first+1 == 256) ) { /* max 256 sectors per ata request */ int count = last - first + 1; - rc = transfer( first, count, buf, write ); + rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); if (rc < 0) return rc * 10 - 1; @@ -1925,7 +2075,7 @@ int fat_readwrite( struct fat_file *file, int sectorcount, (!eof)) { int count = sector - first + 1; - rc = transfer( first, count, buf, write ); + rc = transfer(IF_MV2(fat_bpb,) first, count, buf, write ); if (rc < 0) return rc * 10 - 2; } @@ -1949,6 +2099,11 @@ int fat_readwrite( struct fat_file *file, int sectorcount, int fat_seek(struct fat_file *file, unsigned int seeksector ) { +#ifdef HAVE_MULTIVOLUME + struct bpb* fat_bpb = &fat_bpbs[file->volume]; +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int clusternum=0, numclusters=0, sectornum=0, sector=0; int cluster = file->firstcluster; int i; @@ -1958,8 +2113,8 @@ int fat_seek(struct fat_file *file, unsigned int seeksector ) /* we need to find the sector BEFORE the requested, since the file struct stores the last accessed sector */ seeksector--; - numclusters = clusternum = seeksector / fat_bpb.bpb_secperclus; - sectornum = seeksector % fat_bpb.bpb_secperclus; + numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus; + sectornum = seeksector % fat_bpb->bpb_secperclus; if (file->clusternum && clusternum >= file->clusternum) { @@ -1968,7 +2123,7 @@ int fat_seek(struct fat_file *file, unsigned int seeksector ) } for (i=0; i= NUM_VOLUMES || !fat_bpbs[volume].mounted) + { + LDEBUGF("fat_open() illegal volume %d\n", volume); + return -1; + } +#else + struct bpb* fat_bpb = &fat_bpbs[0]; +#endif int rc; dir->entry = 0; @@ -2003,15 +2170,15 @@ int fat_opendir(struct fat_dir *dir, unsigned int startcluster, if (startcluster == 0) { #ifdef HAVE_FAT16SUPPORT - if (fat_bpb.is_fat16) + if (fat_bpb->is_fat16) { /* FAT16 has the root dir outside of the data area */ - return fat_open_root(&dir->file); + return fat_open_root(IF_MV2(volume,) &dir->file); } #endif - startcluster = sec2cluster(fat_bpb.rootdirsector); + startcluster = sec2cluster(IF_MV2(fat_bpb,) fat_bpb->rootdirsector); } - rc = fat_open(startcluster, &dir->file, parent_dir); + rc = fat_open(IF_MV2(volume,) startcluster, &dir->file, parent_dir); if(rc) { DEBUGF( "fat_opendir() - Couldn't open dir" @@ -2196,7 +2363,18 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) return 0; } -int fat_get_cluster_size(void) +int fat_get_cluster_size(IF_MV_NONVOID(int volume)) +{ +#ifndef HAVE_MULTIVOLUME + const int volume = 0; +#endif + struct bpb* fat_bpb = &fat_bpbs[volume]; + return fat_bpb->bpb_secperclus * SECTOR_SIZE; +} + +#ifdef HAVE_MULTIVOLUME +bool fat_ismounted(int volume) { - return fat_bpb.bpb_secperclus * SECTOR_SIZE; + return (volume +#include "config.h" /* for HAVE_MULTIVOLUME or not */ + +/* FixMe: These macros are a bit nasty and perhaps misplaced here. + We'll get rid of them once decided on how to proceed with multivolume. */ +#ifdef HAVE_MULTIVOLUME +#define IF_MV(x) x /* optional volume/drive parameter */ +#define IF_MV2(x,y) x,y /* same, for a list of arguments */ +#define IF_MV_NONVOID(x) x /* for prototype with sole volume parameter */ +#define NUM_VOLUMES 2 +#else /* empty definitions if no multi-volume */ +#define IF_MV(x) +#define IF_MV2(x,y) +#define IF_MV_NONVOID(x) void +#define NUM_VOLUMES 1 +#endif /* ata_spindown() time values: @@ -41,8 +56,8 @@ extern bool ata_disk_is_active(void); extern int ata_hard_reset(void); extern int ata_soft_reset(void); extern int ata_init(void); -extern int ata_read_sectors(unsigned long start, int count, void* buf); -extern int ata_write_sectors(unsigned long start, int count, const void* buf); +extern int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int count, void* buf); +extern int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, const void* buf); extern void ata_delayed_write(unsigned long sector, const void* buf); extern void ata_flush(void); extern void ata_spin(void); diff --git a/firmware/export/disk.h b/firmware/export/disk.h index 7a9697aa13..70b73c6547 100644 --- a/firmware/export/disk.h +++ b/firmware/export/disk.h @@ -19,6 +19,8 @@ #ifndef _DISK_H_ #define _DISK_H_ +#include "ata.h" /* for volume definitions */ + struct partinfo { unsigned long start; /* first sector (LBA) */ unsigned long size; /* number of sectors */ @@ -30,7 +32,7 @@ struct partinfo { #define PARTITION_TYPE_FAT16 0x06 /* returns a pointer to an array of 8 partinfo structs */ -struct partinfo* disk_init(void); +struct partinfo* disk_init(IF_MV_NONVOID(int volume)); struct partinfo* disk_partinfo(int partition); #endif diff --git a/firmware/export/fat.h b/firmware/export/fat.h index 6f9c9c5d11..7150d2b09f 100644 --- a/firmware/export/fat.h +++ b/firmware/export/fat.h @@ -21,6 +21,7 @@ #define FAT_H #include +#include "ata.h" /* for volume definitions */ #define SECTOR_SIZE 512 @@ -45,6 +46,7 @@ struct fat_direntry #define FAT_ATTR_VOLUME_ID 0x08 #define FAT_ATTR_DIRECTORY 0x10 #define FAT_ATTR_ARCHIVE 0x20 +#define FAT_ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ struct fat_file { @@ -57,6 +59,9 @@ struct fat_file unsigned int direntries; /* number of dir entries used by this file */ unsigned int dircluster; /* first cluster of dir */ bool eof; +#ifdef HAVE_MULTIVOLUME + int volume; /* file resides on which volume */ +#endif }; struct fat_dir @@ -69,14 +74,16 @@ struct fat_dir }; -extern int fat_mount(int startsector); -extern void fat_size(unsigned int* size, unsigned int* free); -extern void fat_recalc_free(void); +extern void fat_init(void); +extern int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) int startsector); +extern void fat_size(IF_MV2(int volume,) unsigned int* size, unsigned int* free); // public for info +extern void fat_recalc_free(IF_MV_NONVOID(int volume)); // public for debug info screen extern int fat_create_dir(const char* name, struct fat_dir* newdir, struct fat_dir* dir); -extern int fat_startsector(void); -extern int fat_open(unsigned int cluster, +extern int fat_startsector(IF_MV_NONVOID(int volume)); // public for config sector +extern int fat_open(IF_MV2(int volume,) + unsigned int cluster, struct fat_file* ent, const struct fat_dir* dir); extern int fat_create_file(const char* name, @@ -93,9 +100,11 @@ extern int fat_rename(struct fat_file* file, const unsigned char* newname, int size, int attr); -extern int fat_opendir(struct fat_dir *ent, unsigned int currdir, +extern int fat_opendir(IF_MV2(int volume,) + struct fat_dir *ent, unsigned int currdir, const struct fat_dir *parent_dir); extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry); -extern int fat_get_cluster_size(void); +extern int fat_get_cluster_size(IF_MV_NONVOID(int volume)); +extern bool fat_ismounted(int volume); #endif diff --git a/firmware/include/dir.h b/firmware/include/dir.h index 00c6664872..5c157e4696 100644 --- a/firmware/include/dir.h +++ b/firmware/include/dir.h @@ -30,6 +30,7 @@ #define ATTR_VOLUME_ID 0x08 #define ATTR_DIRECTORY 0x10 #define ATTR_ARCHIVE 0x20 +#define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ struct dirent { unsigned char d_name[MAX_PATH]; @@ -52,6 +53,9 @@ typedef struct { struct fat_dir fatdir; struct fat_dir parent_dir; struct dirent theent; +#ifdef HAVE_MULTIVOLUME + int volumecounter; /* running counter for faked volume entries */ +#endif } DIR; #else /* SIMULATOR */ diff --git a/firmware/test/fat/main.c b/firmware/test/fat/main.c index 5607414bcb..5420199f92 100644 --- a/firmware/test/fat/main.c +++ b/firmware/test/fat/main.c @@ -677,7 +677,7 @@ int main(int argc, char *argv[]) #endif ) { DEBUGF("*** Mounting at block %ld\n",pinfo[i].start); - rc = fat_mount(pinfo[i].start); + rc = fat_mount(IF_MV2(0,) IF_MV2(0,) pinfo[i].start); if(rc) { DEBUGF("mount: %d",rc); return -1; @@ -686,7 +686,7 @@ int main(int argc, char *argv[]) } } if ( i==4 ) { - if(fat_mount(0)) { + if(fat_mount(IF_MV2(0,) IF_MV2(0,) 0)) { DEBUGF("No FAT32 partition!"); return -1; } diff --git a/firmware/test/i2c/main.c b/firmware/test/i2c/main.c index 34d3dc68e2..4419dde171 100644 --- a/firmware/test/i2c/main.c +++ b/firmware/test/i2c/main.c @@ -1,639 +1,1278 @@ /*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + #include + #include + #include + #include + #include "i2c.h" + #include "mas.h" + #include "dac.h" + #include "sh7034.h" + #include "system.h" + #include "debug.h" + #include "kernel.h" + #include "thread.h" + #include "ata.h" + #include "disk.h" + #include "fat.h" + #include "file.h" + #include "dir.h" + #include "panic.h" + + #ifndef MIN + #define MIN(a, b) (((a)<(b))?(a):(b)) + #endif + + #define MPEG_PLAY 1 + #define MPEG_STOP 2 + #define MPEG_PAUSE 3 + #define MPEG_RESUME 4 + #define MPEG_NEED_DATA 100 + + #define MP3_LOW_WATER 0x30000 + #define MP3_CHUNK_SIZE 0x20000 + + unsigned int bass_table[] = + { + 0, + 0x800, /* 1dB */ + 0x10000, /* 2dB */ + 0x17c00, /* 3dB */ + 0x1f800, /* 4dB */ + 0x27000, /* 5dB */ + 0x2e400, /* 6dB */ + 0x35800, /* 7dB */ + 0x3c000, /* 8dB */ + 0x42800, /* 9dB */ + 0x48800, /* 10dB */ + 0x4e400, /* 11dB */ + 0x53800, /* 12dB */ + 0x58800, /* 13dB */ + 0x5d400, /* 14dB */ + 0x61800 /* 15dB */ + }; + + unsigned int treble_table[] = + { + 0, + 0x5400, /* 1dB */ + 0xac00, /* 2dB */ + 0x10400, /* 3dB */ + 0x16000, /* 4dB */ + 0x1c000, /* 5dB */ + 0x22400, /* 6dB */ + 0x28400, /* 7dB */ + 0x2ec00, /* 8dB */ + 0x35400, /* 9dB */ + 0x3c000, /* 10dB */ + 0x42c00, /* 11dB */ + 0x49c00, /* 12dB */ + 0x51800, /* 13dB */ + 0x58400, /* 14dB */ + 0x5f800 /* 15dB */ + }; + + unsigned char fliptable[] = + { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff + }; + + extern unsigned int stack[]; + /* Place the MP3 data right after the stack */ + + #define MP3BUF_LEN 0x100000 /* 1 Mbyte */ + + unsigned char *mp3buf = (unsigned char *)stack; + + char *tracks[100]; + int num_tracks; + + int mp3buf_write; + int mp3buf_read; + int last_dma_chunk_size; + + bool dma_on; /* The DMA is active */ + bool playing; /* We are playing an MP3 stream */ + bool filling; /* We are filling the buffer with data from disk */ + + struct event_queue mpeg_queue; + + static void mas_poll_start(unsigned int interval_in_ms); + void mpeg_thread(void); + + void reset_mp3_buffer(void) + { + mp3buf_read = 0; + mp3buf_write = 0; + } + + void setup_sci0(void) + { + /* PB15 is I/O, PB14 is IRQ6, PB12 is SCK0 */ + PBCR1 = (PBCR1 & 0x0cff) | 0x1200; + + /* Set PB12 to output */ + PBIOR |= 0x1000; + + /* Disable serial port */ + SCR0 = 0x00; + + /* Synchronous, no prescale */ + SMR0 = 0x80; + + /* Set baudrate 1Mbit/s */ + BRR0 = 0x03; + + /* use SCK as serial clock output */ + SCR0 = 0x01; + + /* Clear FER and PER */ + SSR0 &= 0xe7; + + /* Set interrupt ITU2 and SCI0 priority to 0 */ + IPRD &= 0x0ff0; + + /* set IRQ6 and IRQ7 to edge detect */ + ICR |= 0x03; + + /* set PB15 and PB14 to inputs */ + PBIOR &= 0x7fff; + PBIOR &= 0xbfff; + + /* set IRQ6 prio 8 and IRQ7 prio 0 */ + IPRB = ( IPRB & 0xff00 ) | 0x0080; + + /* Enable End of DMA interrupt at prio 8 */ + IPRC = (IPRC & 0xf0ff) | 0x0800; + + /* Enable Tx (only!) */ + SCR0 |= 0x20; + } + + + void init_dma(void) + { + SAR3 = (unsigned int) mp3buf + mp3buf_read; + DAR3 = 0x5FFFEC3; + CHCR3 &= ~0x0002; /* Clear interrupt */ + CHCR3 = 0x1504; /* Single address destination, TXI0, IE=1 */ + last_dma_chunk_size = MIN(65536, mp3buf_write - mp3buf_read); + DTCR3 = last_dma_chunk_size & 0xffff; + DMAOR = 0x0001; /* Enable DMA */ + CHCR3 |= 0x0001; /* Enable DMA IRQ */ + } + + void start_dma(void) + { + SCR0 |= 0x80; + dma_on = true; + } + + void stop_dma(void) + { + SCR0 &= 0x7f; + dma_on = false; + } + + void dma_tick(void) + { + /* Start DMA if it isn't running */ + if(playing && !dma_on) + { + if(PBDR & 0x4000) + { + if(!(SCR0 & 0x80)) + start_dma(); + } + } + } + + void bitswap(unsigned char *data, int length) + { + int i; + for(i = 0;i < length;i++) + { + data[i] = fliptable[data[i]]; + } + } + + int main(void) + { + char buf[40]; + char str[32]; + int i=0; + DIR *d; + struct dirent *dent; + char *tmp; + int volume, bass, treble; + unsigned short frame_count; + + /* Clear it all! */ + SSR1 &= ~(SCI_RDRF | SCI_ORER | SCI_PER | SCI_FER); + + /* This enables the serial Rx interrupt, to be able to exit into the + debugger when you hit CTRL-C */ + SCR1 |= 0x40; + SCR1 &= ~0x80; + + IPRE |= 0xf000; /* Highest priority */ + + i2c_init(); + + dma_on = true; + + kernel_init(); + + set_irq_level(0); + + setup_sci0(); + + i=mas_readmem(MAS_BANK_D1,0xff6,(unsigned long*)buf,2); + if (i) { + debugf("Error - mas_readmem() returned %d\n", i); + while(1); + } + + i = buf[0] | buf[1] << 8; + debugf("MAS version: %x\n", i); + i = buf[4] | buf[5] << 8; + debugf("MAS revision: %x\n", i); + + i=mas_readmem(MAS_BANK_D1,0xff9,(unsigned long*)buf,7); + if (i) { + debugf("Error - mas_readmem() returned %d\n", i); + while(1); + } + + for(i = 0;i < 7;i++) + { + str[i*2+1] = buf[i*4]; + str[i*2] = buf[i*4+1]; + } + str[i*2] = 0; + debugf("Description: %s\n", str); + + i=mas_writereg(0x3b, 0x20); + if (i < 0) { + debugf("Error - mas_writereg() returned %d\n", i); + while(1); + } + + i = mas_run(1); + if (i < 0) { + debugf("Error - mas_run() returned %d\n", i); + while(1); + } + + i = ata_init(); + debugf("ata_init() returned %d\n", i); + + i = disk_init(); + debugf("disk_init() returned %d\n", i); + + debugf("part[0] starts at sector %d\n", part[0].start); + - i = fat_mount(part[0].start); + + i = fat_mount(IF_MV2(0,) IF_MV2(0,) part[0].start); + debugf("fat_mount() returned %d\n", i); + + num_tracks = 0; + if((d = opendir("/"))) + { + while((dent = readdir(d))) + { + debugf("%s\n", dent->d_name); + i = strlen(dent->d_name); + tmp = dent->d_name + i - 4; + debugf("%s\n", tmp); + if(!stricmp(tmp, ".mp3")) + { + tmp = malloc(i+1); + if(tmp) + { + debugf("Adding track %s\n", dent->d_name); + snprintf(tmp, i+1, "/%s", dent->d_name); + tracks[num_tracks++] = tmp; + } + else + { + panicf("Out of memory\n"); + } + } + } + closedir(d); + } + + debugf("Number of tracks: %d\n"); + + queue_init(&mpeg_queue); + + create_thread(mpeg_thread, stack - 0x2000, 0x4000); + + mas_poll_start(2); + + debugf("let's play...\n"); + + queue_post(&mpeg_queue, MPEG_PLAY, 0); + + volume = 0x2c; + + if(dac_config(0x04) < 0) + debugf("DAC write failed\n"); + + if(dac_volume(volume) < 0) + debugf("DAC write failed\n"); + + bass = 12; + treble = 8; + + mas_writereg(MAS_REG_KPRESCALE, 0xe9400); + mas_writereg(MAS_REG_KBASS, bass_table[bass]); + mas_writereg(MAS_REG_KTREBLE, treble_table[treble]); + + while(1) + { + sleep(HZ*4); + } + } + + #pragma interrupt + void IRQ6(void) + { + stop_dma(); + } + + #pragma interrupt + void DEI3(void) + { + int unplayed_space_left; + int space_until_end_of_buffer; + + if(playing) + { + mp3buf_read += last_dma_chunk_size; + if(mp3buf_read >= MP3BUF_LEN) + mp3buf_read = 0; + + unplayed_space_left = mp3buf_write - mp3buf_read; + if(unplayed_space_left < 0) + unplayed_space_left = MP3BUF_LEN + unplayed_space_left; + + space_until_end_of_buffer = MP3BUF_LEN - mp3buf_read; + + if(!filling && unplayed_space_left < MP3_LOW_WATER) + { + queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); + } + + if(unplayed_space_left) + { + last_dma_chunk_size = MIN(65536, unplayed_space_left); + last_dma_chunk_size = MIN(last_dma_chunk_size, space_until_end_of_buffer); + DTCR3 = last_dma_chunk_size & 0xffff; + SAR3 = (unsigned int)mp3buf + mp3buf_read; + } + else + { + debugf("No more MP3 data. Stopping.\n"); + CHCR3 = 0; /* Stop DMA interrupt */ + } + } + + CHCR3 &= ~0x0002; /* Clear DMA interrupt */ + } + + static void mas_poll_start(unsigned int interval_in_ms) + { + unsigned int count; + + count = FREQ / 1000 / 8 * interval_in_ms; + + if(count > 0xffff) + { + panicf("Error! The MAS poll interval is too long (%d ms)\n", + interval_in_ms); + return; + } + + /* We are using timer 1 */ + + TSTR &= ~0x02; /* Stop the timer */ + TSNC &= ~0x02; /* No synchronization */ + TMDR &= ~0x02; /* Operate normally */ + + TCNT1 = 0; /* Start counting at 0 */ + GRA1 = count; + TCR1 = 0x23; /* Clear at GRA match, sysclock/8 */ + + /* Enable interrupt on level 2 */ + IPRC = (IPRC & ~0x000f) | 0x0002; + + TSR1 &= ~0x02; + TIER1 = 0xf9; /* Enable GRA match interrupt */ + + TSTR |= 0x02; /* Start timer 2 */ + } + + #pragma interrupt + void IMIA1(void) + { + dma_tick(); + TSR1 &= ~0x01; + } + + int track_index = 0; + char *peek_next_track(int index) + { + if(track_index < num_tracks) + return tracks[track_index+index]; + else + return NULL; + } + + void next_track(void) + { + track_index++; + } + + int mpeg_file = -1; + + int new_file(void) + { + char *trackname; + + trackname = peek_next_track(0); + + debugf("playing %s\n", trackname); + mpeg_file = open(trackname, O_RDONLY); + if(mpeg_file < 0) + { + debugf("Couldn't open file\n"); + return -1; + } + return 0; + } + + void mpeg_thread(void) + { + struct event ev; + int len; + int free_space_left; + int amount_to_read; + bool play_pending; + + play_pending = false; + playing = false; + + while(1) + { + debugf("S\n"); + queue_wait(&mpeg_queue, &ev); + switch(ev.id) + { + case MPEG_PLAY: + /* Stop the current stream */ + play_pending = false; + playing = false; + stop_dma(); + + reset_mp3_buffer(); + + new_file(); + + /* Make it read more data */ + filling = true; + queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); + + /* Tell the file loading code that we want to start playing + as soon as we have some data */ + play_pending = true; + break; + + case MPEG_STOP: + /* Stop the current stream */ + playing = false; + stop_dma(); + break; + + case MPEG_PAUSE: + /* Stop the current stream */ + playing = false; + stop_dma(); + break; + + case MPEG_RESUME: + /* Stop the current stream */ + playing = true; + start_dma(); + break; + + case MPEG_NEED_DATA: + free_space_left = mp3buf_read - mp3buf_write; + + /* We interpret 0 as "empty buffer" */ + if(free_space_left <= 0) + free_space_left = MP3BUF_LEN + free_space_left; + + if(free_space_left <= MP3_CHUNK_SIZE) + { + debugf("0\n"); + ata_spindown(-1); + filling = false; + break;; + } + + amount_to_read = MIN(MP3_CHUNK_SIZE, free_space_left); + amount_to_read = MIN(MP3BUF_LEN - mp3buf_write, amount_to_read); + + /* Read in a few seconds worth of MP3 data. We don't want to + read too large chunks because the bitswapping will take + too much time. We must keep the DMA happy and also give + the other threads a chance to run. */ + debugf("R\n"); + len = read(mpeg_file, mp3buf+mp3buf_write, amount_to_read); + if(len) + { + debugf("B\n"); + bitswap(mp3buf + mp3buf_write, len); + + mp3buf_write += len; + if(mp3buf_write >= MP3BUF_LEN) + { + mp3buf_write = 0; + debugf("W\n"); + } + + /* Tell ourselves that we want more data */ + queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); + + /* And while we're at it, see if we have startet playing + yet. If not, do it. */ + if(play_pending) + { + play_pending = false; + playing = true; + + init_dma(); + start_dma(); + } + } + else + { + close(mpeg_file); + + /* Make sure that the write pointer is at a word + boundary */ + mp3buf_write &= 0xfffffffe; + + next_track(); + if(new_file() < 0) + { + /* No more data to play */ + debugf("Finished playing\n"); + playing = false; + ata_spindown(-1); + filling = false; + } + else + { + /* Tell ourselves that we want more data */ + queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); + } + } + break; + } + } + } + + /* Newlib trap honeypot */ + void __trap34(void) + { + debugf("newlib trap34\n"); + while(1); + } + diff --git a/firmware/usb.c b/firmware/usb.c index e27c003154..ab8e080c45 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -157,17 +157,38 @@ static void usb_slave_mode(bool on) panicf("ata: %d",rc); } - pinfo = disk_init(); + pinfo = disk_init(IF_MV(0)); if (!pinfo) panicf("disk: NULL"); + fat_init(); for ( i=0; i<4; i++ ) { - rc = fat_mount(pinfo[i].start); + rc = fat_mount(IF_MV2(0,) IF_MV2(0,) pinfo[i].start); if (!rc) - break; + break; /* only one partition gets mounted as of now */ } if (i==4) panicf("mount: %d",rc); +#ifdef HAVE_MULTIVOLUME + /* mount partition on the optional volume */ +#ifdef HAVE_MMC + if (mmc_detect()) /* for Ondio, only if card detected */ +#endif + { + pinfo = disk_init(1); + if (pinfo) + { + for ( i=0; i<4; i++ ) { + if (!fat_mount(1, 1, pinfo[i].start)) + break; /* only one partition gets mounted as of now */ + } + + if ( i==4 ) { + rc = fat_mount(1, 1, 0); + } + } + } +#endif /* #ifdef HAVE_MULTIVOLUME */ } } -- cgit v1.2.3