diff options
| author | Keith Packard <keithp@keithp.com> | 2013-03-29 00:32:23 -0700 | 
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2013-03-29 00:32:23 -0700 | 
| commit | 86e1039e14304ac13db540f2ee3afd4ff170b8b4 (patch) | |
| tree | e80fbbcaf528cf657cbab0a50a7b298cdfabfac0 /src | |
| parent | 44e418bbecd3a3deae942803141cf115d92f29d2 (diff) | |
altos: Add FAT32 support. And lots more testing.
Generalizes the FAT code to deal with either 16-bit or 32-bit
versions. The testing code now runs over a variety of disk images to
check for compatibility on all of them.
Signed-off-by: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/drivers/ao_bufio.c | 16 | ||||
| -rw-r--r-- | src/drivers/ao_bufio.h | 3 | ||||
| -rw-r--r-- | src/drivers/ao_fat.c | 569 | ||||
| -rw-r--r-- | src/drivers/ao_fat.h | 39 | ||||
| -rw-r--r-- | src/test/ao_fat_test.c | 236 | 
5 files changed, 612 insertions, 251 deletions
diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c index 10b32ceb..87de457f 100644 --- a/src/drivers/ao_bufio.c +++ b/src/drivers/ao_bufio.c @@ -22,7 +22,7 @@  #include "ao_sdcard.h"  #include "ao_bufio.h" -#define AO_NUM_BUF		4 +#define AO_NUM_BUF		16  #define AO_BUFSIZ		512  struct ao_bufio { @@ -292,13 +292,21 @@ static const struct ao_cmds ao_bufio_cmds[] = {  };  void -ao_bufio_init(void) +ao_bufio_setup(void)  {  	int b; -	for (b = 0; b < AO_NUM_BUF; b++) +	for (b = 0; b < AO_NUM_BUF; b++) { +		ao_bufio[b].dirty = 0; +		ao_bufio[b].busy = 0;  		ao_bufio[b].block = 0xffffffff; -	ao_sdcard_init(); +	} +} +void +ao_bufio_init(void) +{ +	ao_bufio_setup(); +	ao_sdcard_init();  	ao_cmd_register(&ao_bufio_cmds[0]);  } diff --git a/src/drivers/ao_bufio.h b/src/drivers/ao_bufio.h index c3bee906..6629f143 100644 --- a/src/drivers/ao_bufio.h +++ b/src/drivers/ao_bufio.h @@ -31,6 +31,9 @@ void  ao_bufio_flush(void);  void +ao_bufio_setup(void); + +void  ao_bufio_init(void);  #endif /* _AO_BUFIO_H_ */ diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c index 98f57d67..a19eff70 100644 --- a/src/drivers/ao_fat.c +++ b/src/drivers/ao_fat.c @@ -22,10 +22,26 @@  #include "ao_fat.h"  #include "ao_bufio.h" +/* + * Basic file system types + */ + +typedef ao_fat_offset_t		offset_t; +typedef ao_fat_sector_t		sector_t; +typedef ao_fat_cluster_t	cluster_t; +typedef ao_fat_dirent_t		dirent_t; +typedef ao_fat_cluster_offset_t	cluster_offset_t; +  /* Partition information, sector numbers */ -static uint8_t partition_type; -static uint32_t	partition_start, partition_end; +static uint8_t	partition_type; +static sector_t	partition_start, partition_end; + +#define AO_FAT_BAD_CLUSTER		0xffffff7 +#define AO_FAT_LAST_CLUSTER		0xfffffff +#define AO_FAT_IS_LAST_CLUSTER(c)		(((c) & 0xffffff8) == 0xffffff8) +#define AO_FAT_IS_LAST_CLUSTER16(c)	(((c) & 0xfff8) == 0xfff8) +  #define SECTOR_SIZE	512  #define SECTOR_MASK	(SECTOR_SIZE - 1) @@ -34,17 +50,25 @@ static uint32_t	partition_start, partition_end;  #define DIRENT_SIZE	32  /* File system parameters */ -static uint8_t	sectors_per_cluster; -static uint32_t	bytes_per_cluster; -static uint16_t	reserved_sector_count; -static uint8_t	number_fat; -static uint16_t	root_entries; -static uint16_t sectors_per_fat; -static uint16_t	number_cluster; -static uint32_t	fat_start; -static uint32_t root_start; -static uint32_t data_start; -static uint16_t	first_free_cluster; +static uint8_t		sectors_per_cluster; +static uint32_t		bytes_per_cluster; +static sector_t		reserved_sector_count; +static uint8_t		number_fat; +static dirent_t		root_entries; +static sector_t 	sectors_per_fat; +static cluster_t	number_cluster; +static sector_t		fat_start; +static sector_t 	root_start; +static sector_t 	data_start; +static cluster_t	next_free; +static uint8_t		filesystem_full; + +/* FAT32 extra data */ +static uint8_t		fat32; +static uint8_t		fsinfo_dirty; +static cluster_t	root_cluster; +static sector_t		fsinfo_sector; +static cluster_t	free_count;  /*   * Deal with LSB FAT data structures @@ -81,14 +105,14 @@ put_u16(uint8_t *base, uint16_t value)  }  static uint8_t -ao_fat_cluster_valid(uint16_t cluster) +ao_fat_cluster_valid(cluster_t cluster)  {  	return (2 <= cluster && cluster < number_cluster);  }  /* Start using a sector */  static uint8_t * -ao_fat_sector_get(uint32_t sector) +ao_fat_sector_get(sector_t sector)  {  	sector += partition_start;  	if (sector >= partition_end) @@ -99,49 +123,36 @@ ao_fat_sector_get(uint32_t sector)  /* Finish using a sector, 'w' is 1 if modified */  #define ao_fat_sector_put(b,w) ao_bufio_put(b,w) -/* Start using a root directory entry */ -static uint8_t * -ao_fat_root_get(uint16_t e) -{ -	uint32_t	byte = e * DIRENT_SIZE; -	uint32_t	sector = byte >> SECTOR_SHIFT; -	uint16_t	offset = byte & SECTOR_MASK; -	uint8_t		*buf; - -	buf = ao_fat_sector_get(root_start + sector); -	if (!buf) -		return NULL; -	return buf + offset; -} - -/* Finish using a root directory entry, 'w' is 1 if modified */ -static void -ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write) -{ -	uint16_t	offset = ((e * DIRENT_SIZE) & SECTOR_MASK); -	uint8_t		*buf = root - offset; - -	ao_fat_sector_put(buf, write); -} -  /* Get the next cluster entry in the chain */ -static uint16_t -ao_fat_entry_read(uint16_t cluster) +static cluster_t +ao_fat_entry_read(cluster_t cluster)  { -	uint32_t	sector; -	uint16_t	offset; +	sector_t	sector; +	cluster_t	offset;  	uint8_t		*buf; -	uint16_t	ret; +	cluster_t	ret;  	if (!ao_fat_cluster_valid(cluster)) -		return 0xfff7; - -	sector = cluster >> (SECTOR_SHIFT - 1); -	offset = (cluster << 1) & SECTOR_MASK; +		return 0xfffffff7; + +	if (fat32) +		cluster <<= 2; +	else +		cluster <<= 1; +	sector = cluster >> (SECTOR_SHIFT); +	offset = cluster & SECTOR_MASK;  	buf = ao_fat_sector_get(fat_start + sector);  	if (!buf)  		return 0; -	ret = get_u16(buf + offset); + +	if (fat32) { +		ret = get_u32(buf + offset); +		ret &= 0xfffffff; +	} else { +		ret = get_u16(buf + offset); +		if (AO_FAT_IS_LAST_CLUSTER16(ret)) +			ret |= 0xfff0000; +	}  	ao_fat_sector_put(buf, 0);  	return ret;  } @@ -149,36 +160,60 @@ ao_fat_entry_read(uint16_t cluster)  /* Replace the referenced cluster entry in the chain with   * 'new_value'. Return the previous value.   */ -static uint16_t -ao_fat_entry_replace(uint16_t  cluster, uint16_t new_value) +static cluster_t +ao_fat_entry_replace(cluster_t  cluster, cluster_t new_value)  { -	uint32_t	sector; -	uint16_t	offset; -	uint8_t		*buf; -	uint16_t	ret; -	uint8_t		other_fats; +	sector_t		sector; +	cluster_offset_t	offset; +	uint8_t			*buf; +	cluster_t		ret; +	cluster_t		old_value; +	uint8_t			fat;  	if (!ao_fat_cluster_valid(cluster)) -		return 0; - -	sector = cluster >> (SECTOR_SHIFT - 1); -	offset = (cluster << 1) & SECTOR_MASK; -	buf = ao_fat_sector_get(fat_start + sector); -	if (!buf) -		return 0; -	ret = get_u16(buf + offset); -	put_u16(buf + offset, new_value); -	ao_fat_sector_put(buf, 1); - -	/* -	 * Keep the other FATs in sync -	 */ -	for (other_fats = 1; other_fats < number_fat; other_fats++) { -		buf = ao_fat_sector_get(fat_start + other_fats * sectors_per_fat + sector); -		if (buf) { +		return 0xfffffff7; + +	/* Convert from cluster index to byte index */ +	if (fat32) +		cluster <<= 2; +	else +		cluster <<= 1; +	sector = cluster >> SECTOR_SHIFT; +	offset = cluster & SECTOR_MASK; + +	new_value &= 0xfffffff; +	for (fat = 0; fat < number_fat; fat++) { +		buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector); +		if (!buf) +			return 0; +		if (fat32) { +			old_value = get_u32(buf + offset); +			put_u32(buf + offset, new_value | (old_value & 0xf0000000)); +			if (fat == 0) { +				ret = old_value & 0xfffffff; + +				/* Track the free count if it wasn't marked +				 * invalid when we mounted the file system +				 */ +				if (free_count != 0xffffffff) { +					if (new_value && !ret) { +						--free_count; +						fsinfo_dirty = 1; +					} else if (!new_value && ret) { +						++free_count; +						fsinfo_dirty = 1; +					} +				} +			} +		} else { +			if (fat == 0) { +				ret = get_u16(buf + offset); +				if (AO_FAT_IS_LAST_CLUSTER16(ret)) +					ret |= 0xfff0000; +			}  			put_u16(buf + offset, new_value); -			ao_fat_sector_put(buf, 1);  		} +		ao_fat_sector_put(buf, 1);  	}  	return ret; @@ -189,12 +224,14 @@ ao_fat_entry_replace(uint16_t  cluster, uint16_t new_value)   * all of them as free   */  static void -ao_fat_free_cluster_chain(uint16_t cluster) +ao_fat_free_cluster_chain(cluster_t cluster)  {  	while (ao_fat_cluster_valid(cluster)) { -		if (cluster < first_free_cluster) -			first_free_cluster = cluster; -		cluster = ao_fat_entry_replace(cluster, 0x0000); +		if (cluster < next_free) { +			next_free = cluster; +			fsinfo_dirty = 1; +		} +		cluster = ao_fat_entry_replace(cluster, 0x00000000);  	}  } @@ -207,8 +244,8 @@ ao_fat_free_cluster_chain(uint16_t cluster)   * 0xffff if we walk off the end of the file or the cluster chain   * is damaged somehow   */ -static uint16_t -ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) +static cluster_t +ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)  {  	while (distance) {  		cluster = ao_fat_entry_read(cluster); @@ -220,6 +257,176 @@ ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)  }  /* + * ao_fat_cluster_set_size + * + * Set the number of clusters in the specified chain, + * freeing extra ones or alocating new ones as needed + * + * Returns AO_FAT_BAD_CLUSTER on allocation failure + */ + +static cluster_t +ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size) +{ +	cluster_t	clear_cluster = 0; + +	if (size == 0) { +		clear_cluster = first_cluster; +		first_cluster = 0; +	} else { +		cluster_t	have; +		cluster_t	last_cluster = 0; +		cluster_t	next_cluster; + +		/* Walk the cluster chain to the +		 * spot where it needs to change. That +		 * will either be the end of the chain (in case it needs to grow), +		 * or after the desired number of clusters, in which case it needs to shrink +		 */ +		next_cluster = first_cluster; +		for (have = 0; have < size; have++) { +			last_cluster = next_cluster; +			next_cluster = ao_fat_entry_read(last_cluster); +			if (!ao_fat_cluster_valid(next_cluster)) +				break; +		} + +		if (have == size) { +			/* The file is large enough, truncate as needed */ +			if (ao_fat_cluster_valid(next_cluster)) { +				/* Rewrite that cluster entry with 0xffff to mark the end of the chain */ +				clear_cluster = ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); +				filesystem_full = 0; +			} else { +				/* The chain is already the right length, don't mess with it */ +				; +			} +		} else { +			cluster_t	need; +			cluster_t	free; + +			if (filesystem_full) +				return AO_FAT_BAD_CLUSTER; + +			if (next_free < 2 || number_cluster <= next_free) { +				next_free = 2; +				fsinfo_dirty = 1; +			} + +			/* See if there are enough free clusters in the file system */ +			need = size - have; + +#define loop_cluster	for (free = next_free; need > 0;) +#define next_cluster					\ +			if (++free == number_cluster)	\ +				free = 2;		\ +			if (free == next_free) \ +				break;			\ + +			loop_cluster { +				if (!ao_fat_entry_read(free)) +					need--; +				next_cluster; +			} +			/* Still need some, tell the user that we've failed */ +			if (need) { +				filesystem_full = 1; +				return AO_FAT_BAD_CLUSTER; +			} + +			/* Now go allocate those clusters and +			 * thread them onto the chain +			 */ +			need = size - have; +			loop_cluster { +				if (!ao_fat_entry_read(free)) { +					next_free = free + 1; +					if (next_free >= number_cluster) +						next_free = 2; +					fsinfo_dirty = 1; +					if (last_cluster) +						ao_fat_entry_replace(last_cluster, free); +					else +						first_cluster = free; +					last_cluster = free; +					need--; +				} +				next_cluster; +			} +#undef loop_cluster +#undef next_cluster +			/* Mark the new end of the chain */ +			ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER); +		} +	} + +	/* Deallocate clusters off the end of the file */ +	if (ao_fat_cluster_valid(clear_cluster)) +		ao_fat_free_cluster_chain(clear_cluster); +	return first_cluster; +} + +/* Start using a root directory entry */ +static uint8_t * +ao_fat_root_get(dirent_t e) +{ +	offset_t		byte = e * DIRENT_SIZE; +	sector_t		sector = byte >> SECTOR_SHIFT; +	cluster_offset_t	offset = byte & SECTOR_MASK; +	uint8_t			*buf; + +	if (fat32) { +		cluster_t	cluster_distance = sector / sectors_per_cluster; +		sector_t	sector_index = sector % sectors_per_cluster; +		cluster_t	cluster = ao_fat_cluster_seek(root_cluster, cluster_distance); + +		if (ao_fat_cluster_valid(cluster)) +			sector = data_start + (cluster-2) * sectors_per_cluster + sector_index; +		else +			return NULL; +	} else { +		if (e >= root_entries) +			return NULL; +		sector = root_start + sector; +	} + +	buf = ao_fat_sector_get(sector); +	if (!buf) +		return NULL; +	return buf + offset; +} + +/* Finish using a root directory entry, 'w' is 1 if modified */ +static void +ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write) +{ +	cluster_offset_t	offset = ((e * DIRENT_SIZE) & SECTOR_MASK); +	uint8_t			*buf = root - offset; + +	ao_fat_sector_put(buf, write); +} + +/* + * ao_fat_root_extend + * + * On FAT32, make the  + */ +static int8_t +ao_fat_root_extend(dirent_t ents) +{ +	offset_t	byte_size; +	cluster_t	cluster_size; +	if (!fat32) +		return 0; +	 +	byte_size = ents * 0x20; +	cluster_size = byte_size / bytes_per_cluster; +	if (ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER) +		return 1; +	return 0; +} +		 +/*   * ao_fat_setup_partition   *    * Load the boot block and find the first partition @@ -316,6 +523,26 @@ ao_fat_setup_fs(void)  	number_fat = boot[0x10];  	root_entries = get_u16(boot + 0x11);  	sectors_per_fat = get_u16(boot+0x16); +	fat32 = 0; +	if (sectors_per_fat == 0) { +		fat32 = 1; +		sectors_per_fat = get_u32(boot+0x24); +		root_cluster = get_u32(boot+0x2c); +		fsinfo_sector = get_u16(boot + 0x30); +	} +	ao_fat_sector_put(boot, 0); + +	free_count = 0xffffffff; +	next_free = 0; +	if (fat32 && fsinfo_sector) { +		uint8_t	*fsinfo = ao_fat_sector_get(fsinfo_sector); + +		if (fsinfo) { +			free_count = get_u32(fsinfo + 0x1e8); +			next_free = get_u32(fsinfo + 0x1ec); +			ao_fat_sector_put(fsinfo, 0); +		} +	}  	fat_start = reserved_sector_count;;  	root_start = fat_start + number_fat * sectors_per_fat; @@ -325,6 +552,7 @@ ao_fat_setup_fs(void)  	number_cluster = data_sectors / sectors_per_cluster; +	printf ("fat32: %d\n", fat32);  	printf ("sectors per cluster %d\n", sectors_per_cluster);  	printf ("reserved sectors %d\n", reserved_sector_count);  	printf ("number of FATs %d\n", number_fat); @@ -335,20 +563,35 @@ ao_fat_setup_fs(void)  	printf ("root start %d\n", root_start);  	printf ("data start %d\n", data_start); -	ao_fat_sector_put(boot, 0); -  	return 1;  } +/* + * State for the current opened file + */ +static struct ao_fat_dirent	ao_file_dirent; +static uint32_t 		ao_file_offset; +static uint32_t			ao_file_cluster_offset; +static cluster_t		ao_file_cluster; +static uint8_t			ao_file_opened; +  static uint8_t  ao_fat_setup(void)  { +	ao_bufio_setup(); +	 +	partition_type = partition_start = partition_end = 0; +	sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0; +	number_fat = root_entries = sectors_per_fat = 0; +	number_cluster = fat_start = root_start = data_start = 0; +	next_free = filesystem_full = 0; +	fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0; +	memset(&ao_file_dirent, '\0', sizeof (ao_file_dirent)); +	ao_file_offset = ao_file_cluster_offset = ao_file_cluster = ao_file_opened = 0;  	if (!ao_fat_setup_partition())  		return 0; -	check_bufio("partition setup");  	if (!ao_fat_setup_fs())  		return 0; -	check_bufio("fs setup");  	return 1;  } @@ -356,19 +599,13 @@ ao_fat_setup(void)   * Basic file operations   */ -static struct ao_fat_dirent	ao_file_dirent; -static uint32_t 		ao_file_offset; -static uint32_t			ao_file_cluster_offset; -static uint16_t			ao_file_cluster; -static uint8_t			ao_file_opened; -  static uint32_t  ao_fat_current_sector(void)  { -	uint16_t	cluster_offset; +	cluster_t	cluster_offset;  	uint32_t	sector_offset;  	uint16_t	sector_index; -	uint16_t	cluster; +	cluster_t	cluster;  	if (ao_file_offset > ao_file_dirent.size)  		return 0xffffffff; @@ -381,7 +618,7 @@ ao_fat_current_sector(void)  	}  	if (ao_file_cluster_offset + bytes_per_cluster <= ao_file_offset) { -		uint16_t	cluster_distance; +		cluster_t	cluster_distance;  		cluster_offset = sector_offset / sectors_per_cluster; @@ -414,98 +651,44 @@ ao_fat_set_offset(uint32_t offset)  static int8_t  ao_fat_set_size(uint32_t size)  { -	uint16_t	clear_cluster = 0;  	uint8_t		*dent; -	uint16_t	first_cluster; +	cluster_t	first_cluster; +	cluster_t	have_clusters, need_clusters; -	first_cluster = ao_file_dirent.cluster;  	if (size == ao_file_dirent.size)  		return AO_FAT_SUCCESS; -	if (size == 0) { -		clear_cluster = ao_file_dirent.cluster; -		first_cluster = 0; -	} else { -		uint16_t	new_num; -		uint16_t	old_num; - -		new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster; -		old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; -		if (new_num < old_num) { -			uint16_t last_cluster; - -			/* Go find the last cluster we want to preserve in the file */ -			last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1); - -			/* Rewrite that cluster entry with 0xffff to mark the end of the chain */ -			clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff); -		} else if (new_num > old_num) { -			uint16_t	need; -			uint16_t	free; -			uint16_t	last_cluster; -			uint16_t	highest_allocated = 0; - -			if (old_num) -				last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1); -			else -				last_cluster = 0; -			if (first_free_cluster < 2 || number_cluster <= first_free_cluster) -				first_free_cluster = 2; +	first_cluster = ao_file_dirent.cluster; +	have_clusters = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster; +	need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster; -			/* See if there are enough free clusters in the file system */ -			need = new_num - old_num; +	if (have_clusters != need_clusters) { +		if (ao_file_cluster && size >= ao_file_cluster_offset) { +			cluster_t	offset_clusters = (ao_file_cluster_offset + bytes_per_cluster) / bytes_per_cluster; +			cluster_t	extra_clusters = need_clusters - offset_clusters; +			cluster_t	next_cluster; -#define loop_cluster	for (free = first_free_cluster; need > 0;) -#define next_cluster					\ -			if (++free == number_cluster)	\ -				free = 2;		\ -			if (free == first_free_cluster) \ -				break;			\ - -			loop_cluster { -				if (!ao_fat_entry_read(free)) -					need--; -				next_cluster; -			} -			/* Still need some, tell the user that we've failed */ -			if (need) +			next_cluster = ao_fat_cluster_set_size(ao_file_cluster, extra_clusters); +			if (next_cluster == AO_FAT_BAD_CLUSTER)  				return -AO_FAT_ENOSPC; +		} else { +			first_cluster = ao_fat_cluster_set_size(first_cluster, need_clusters); -			/* Now go allocate those clusters */ -			need = new_num - old_num; -			loop_cluster { -				if (!ao_fat_entry_read(free)) { -					if (free > highest_allocated) -						highest_allocated = free; -					if (last_cluster) -						ao_fat_entry_replace(last_cluster, free); -					else -						first_cluster = free; -					last_cluster = free; -					need--; -				} -				next_cluster; -			} -			first_free_cluster = highest_allocated + 1; -			if (first_free_cluster >= number_cluster) -				first_free_cluster = 2; - -			/* Mark the new end of the chain */ -			ao_fat_entry_replace(last_cluster, 0xffff); +			if (first_cluster == AO_FAT_BAD_CLUSTER) +				return -AO_FAT_ENOSPC;  		}  	} -	/* Deallocate clusters off the end of the file */ -	if (ao_fat_cluster_valid(clear_cluster)) -		ao_fat_free_cluster_chain(clear_cluster); -  	/* Update the directory entry */  	dent = ao_fat_root_get(ao_file_dirent.entry);  	if (!dent)  		return -AO_FAT_EIO;  	put_u32(dent + 0x1c, size);  	put_u16(dent + 0x1a, first_cluster); +	if (fat32) +		put_u16(dent + 0x14, first_cluster >> 16);  	ao_fat_root_put(dent, ao_file_dirent.entry, 1); +  	ao_file_dirent.size = size;  	ao_file_dirent.cluster = first_cluster;  	return AO_FAT_SUCCESS; @@ -556,10 +739,40 @@ ao_fat_dirent_init(uint8_t *dent, uint16_t entry, struct ao_fat_dirent *dirent)  	dirent->attr = dent[0x0b];  	dirent->size = get_u32(dent+0x1c);  	dirent->cluster = get_u16(dent+0x1a); +	if (fat32) +		dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;  	dirent->entry = entry;  }  /* + * ao_fat_flush_fsinfo + * + * Write out any fsinfo changes to disk + */ + +void +ao_fat_flush_fsinfo(void) +{ +	uint8_t	*fsinfo; + +	if (!fat32) +		return; + +	if (!fsinfo_dirty) +		return; +	fsinfo_dirty = 0; +	if (!fsinfo_sector) +		return; + +	fsinfo = ao_fat_sector_get(fsinfo_sector); +	if (fsinfo) { +		put_u32(fsinfo + 0x1e8, free_count); +		put_u32(fsinfo + 0x1ec, next_free); +		ao_fat_sector_put(fsinfo, 1); +	} +} + +/*   * Public API   */ @@ -605,6 +818,7 @@ ao_fat_creat(char name[11])  {  	uint16_t	entry;  	int8_t		status; +	uint8_t		*dent;  	if (ao_file_opened)  		return -AO_FAT_EMFILE; @@ -616,12 +830,14 @@ ao_fat_creat(char name[11])  		status = ao_fat_set_size(0);  		break;  	case -AO_FAT_ENOENT: -		for (entry = 0; entry < root_entries; entry++) { -			uint8_t	*dent = ao_fat_root_get(entry); - +		entry = 0; +		for (;;) { +			dent = ao_fat_root_get(entry);  			if (!dent) { -				status = -AO_FAT_EIO; -				ao_fat_root_put(dent, entry, 0); +				 +				if (ao_fat_root_extend(entry)) +					continue; +				status = -AO_FAT_ENOSPC;  				break;  			} @@ -636,9 +852,8 @@ ao_fat_creat(char name[11])  			} else {  				ao_fat_root_put(dent, entry, 0);  			} +			entry++;  		} -		if (entry == root_entries) -			status = -AO_FAT_ENOSPC;  	}  	return status;  } @@ -658,6 +873,8 @@ ao_fat_close(void)  	ao_file_offset = 0;  	ao_file_cluster = 0;  	ao_file_opened = 0; + +	ao_fat_flush_fsinfo();  	ao_bufio_flush();  	return AO_FAT_SUCCESS;  } @@ -849,10 +1066,10 @@ ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)  {  	uint8_t	*dent; -	if (*entry >= root_entries) -		return 0;  	for (;;) {  		dent = ao_fat_root_get(*entry); +		if (!dent) +			return 0;  		if (dent[0] == AO_FAT_DENT_END) {  			ao_fat_root_put(dent, *entry, 0); diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h index 5b9b300f..cfe98a76 100644 --- a/src/drivers/ao_fat.h +++ b/src/drivers/ao_fat.h @@ -32,8 +32,8 @@ ao_fat_init(void);  #define AO_FAT_DENT_EMPTY		0xe5  #define AO_FAT_DENT_END			0x00 -#define AO_FAT_IS_FILE(attr)	(((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_ARCHIVE)) == 0) -#define AO_FAT_IS_DIR(attr)	(((attr) & (AO_FAT_FILE_DIRECTORY)) == AO_FAT_FILE_DIRECTORY) +#define AO_FAT_IS_FILE(attr)	(((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY)) == 0) +#define AO_FAT_IS_DIR(attr)	(((attr) & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == AO_FAT_FILE_DIRECTORY)  #define AO_FAT_SUCCESS			0  #define AO_FAT_EPERM			1 @@ -80,12 +80,37 @@ ao_fat_unlink(char name[11]);  int8_t  ao_fat_rename(char old[11], char new[11]); +/* + * Byte offset within a file. Supports files up to 2GB in size + */ +typedef int32_t		ao_fat_offset_t; + +/* + * Cluster index in partition data space + */ +typedef uint32_t	ao_fat_cluster_t; + +/* + * Sector offset within partition + */ +typedef uint32_t	ao_fat_sector_t; + +/* + * Index within the root directory + */ +typedef uint16_t	ao_fat_dirent_t; + +/* + * Offset within a cluster (or sector) + */ +typedef uint16_t	ao_fat_cluster_offset_t; +  struct ao_fat_dirent { -	char		name[11]; -	uint8_t		attr; -	uint32_t	size; -	uint16_t	cluster; -	uint16_t	entry; +	char			name[11]; +	uint8_t			attr; +	uint32_t		size; +	ao_fat_cluster_t	cluster; +	uint16_t		entry;  };  int8_t diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c index fffd5af4..48d5d8a4 100644 --- a/src/test/ao_fat_test.c +++ b/src/test/ao_fat_test.c @@ -84,14 +84,33 @@ ao_sdcard_write_block(uint32_t block, uint8_t *data)  	return write(fs_fd, data, 512) == 512;  } -char	*fs = "fs.fat"; +struct fs_param { +	int	fat; +	int	blocks; +} fs_params[] = { +	{ .fat = 16, .blocks = 16384 }, +	{ .fat = 32, .blocks = 16384 }, +	{ .fat = 16, .blocks = 65536 }, +	{ .fat = 32, .blocks = 65536 }, +	{ .fat = 16, .blocks = 1048576 }, +	{ .fat = 32, .blocks = 1048576 }, +	{ .fat = 0, .blocks = 0 }, +}; + +char		*fs = "fs.fat"; +struct fs_param	*param;  void  ao_sdcard_init(void)  {  	char	cmd[1024]; -	snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -C %s 16384", fs, fs); +	if (fs_fd) { +		close(fs_fd); +		fs_fd = 0; +	} +	snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -F %d -C %s %d", +		 fs, param->fat, fs, param->blocks);  	if (system (cmd) != 0) {  		fprintf(stderr, "'%s' failed\n", cmd);  		exit(1); @@ -125,21 +144,27 @@ check_fat(void);  #include "ao_fat.c"  /* Get the next cluster entry in the chain */ -static uint16_t -ao_fat_entry_raw_read(uint16_t cluster, uint8_t fat) +static cluster_t +ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat)  { -	uint32_t	sector; -	uint16_t	offset; -	uint8_t		*buf; -	uint16_t	ret; - -//	cluster -= 2; -	sector = cluster >> (SECTOR_SHIFT - 1); -	offset = (cluster << 1) & SECTOR_MASK; +	sector_t		sector; +	cluster_offset_t	offset; +	uint8_t			*buf; +	cluster_t		ret; + +	if (fat32) +		cluster <<= 2; +	else +		cluster <<= 1; +	sector = cluster >> SECTOR_SHIFT; +	offset = cluster & SECTOR_MASK;  	buf = ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);  	if (!buf)  		return 0; -	ret = get_u16(buf + offset); +	if (fat32) +		ret = get_u32(buf + offset); +	else +		ret = get_u16(buf + offset);  	ao_fat_sector_put(buf, 0);  	return ret;  } @@ -153,16 +178,21 @@ dump_fat(void)  	for (e = 0; e < number_cluster; e++) {  		if ((e & 0xf) == 0x0)  			printf ("%04x: ", e); -		printf (" %04x", ao_fat_entry_raw_read(e, 0)); +		if (fat32) +			printf (" %08x", ao_fat_entry_raw_read(e, 0)); +		else +			printf (" %04x", ao_fat_entry_raw_read(e, 0));  		if ((e & 0xf) == 0xf)  			putchar ('\n');  	} +	if (e & 0xf) +		putchar('\n');  }  void  fat_list(void)  { -	uint16_t		entry = 0; +	dirent_t		entry = 0;  	struct ao_fat_dirent	dirent;  	printf ("  **** Root directory ****\n"); @@ -182,8 +212,8 @@ fat_list(void)  void  fatal(char *msg, ...)  { -	dump_fat(); -	fat_list(); +//	dump_fat(); +//	fat_list();  	va_list	l;  	va_start(l, msg); @@ -200,7 +230,7 @@ check_fat(void)  	int	f;  	for (e = 0; e < number_cluster; e++) { -		uint16_t	v = ao_fat_entry_raw_read(e, 0); +		cluster_t	v = ao_fat_entry_raw_read(e, 0);  		for (f = 1; f < number_fat; f++) {  			if (ao_fat_entry_raw_read(e, f) != v)  				fatal ("fats differ at %d\n", e); @@ -208,24 +238,24 @@ check_fat(void)  	}  } -uint16_t -check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used) +cluster_t +check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used)  { -	uint16_t	clusters = 0; -	uint16_t	cluster; +	cluster_t	clusters = 0; +	cluster_t	cluster;  	if (!first_cluster)  		return 0;  	for (cluster = first_cluster; -	     (cluster & 0xfff8) != 0xfff8; +	     fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster);  	     cluster = ao_fat_entry_raw_read(cluster, 0))  	{  		if (!ao_fat_cluster_valid(cluster)) -			fatal("file %d: invalid cluster %04x\n", dent, cluster); +			fatal("file %d: invalid cluster %08x\n", dent, cluster);  		if (used[cluster]) -			fatal("file %d: duplicate cluster %04x\n", dent, cluster); -		used[cluster] = 1; +			fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1); +		used[cluster] = dent;  		clusters++;  	}  	return clusters; @@ -234,25 +264,27 @@ check_file(uint16_t dent, uint16_t first_cluster, uint8_t *used)  void  check_fs(void)  { -	uint16_t	r; -	uint16_t	cluster, chain; -	uint8_t		*used; +	dirent_t	r; +	cluster_t	cluster, chain; +	dirent_t	*used; +	uint8_t		*dent;  	check_fat(); -	used = calloc(1, number_cluster); +	used = calloc(sizeof (dirent_t), number_cluster); -	for (r = 0; r < root_entries; r++) { -		uint8_t		*dent = ao_fat_root_get(r); -		uint16_t	clusters; -		uint32_t	size; -		uint16_t	first_cluster; -		uint8_t		name[11]; +	for (r = 0; (dent = ao_fat_root_get(r)); r++) { +		cluster_t	clusters; +		offset_t	size; +		cluster_t	first_cluster; +		char		name[11];  		if (!dent)  			fatal("cannot map dent %d\n", r);  		memcpy(name, dent+0, 11);  		first_cluster = get_u16(dent + 0x1a); +		if (fat32) +			first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16;  		size = get_u32(dent + 0x1c);  		ao_fat_root_put(dent, r, 0); @@ -260,7 +292,7 @@ check_fs(void)  			break;  		} -		clusters = check_file(r, first_cluster, used); +		clusters = check_file(r + 1, first_cluster, used);  		if (size == 0) {  			if (clusters != 0)  				fatal("file %d: zero sized, but %d clusters\n", clusters); @@ -273,20 +305,29 @@ check_fs(void)  				      r, size, clusters, clusters * bytes_per_cluster);  		}  	} -	for (; r < root_entries; r++) { -		uint8_t	*dent = ao_fat_root_get(r); -		if (!dent) -			fatal("cannot map dent %d\n", r); -		if (dent[0] != AO_FAT_DENT_END) -			fatal("found non-zero dent past end %d\n", r); -		ao_fat_root_put(dent, r, 0); +	if (!fat32) { +		for (; r < root_entries; r++) { +			uint8_t	*dent = ao_fat_root_get(r); +			if (!dent) +				fatal("cannot map dent %d\n", r); +			if (dent[0] != AO_FAT_DENT_END) +				fatal("found non-zero dent past end %d\n", r); +			ao_fat_root_put(dent, r, 0); +		} +	} else { +		check_file((dirent_t) -1, root_cluster, used);  	}  	for (cluster = 0; cluster < 2; cluster++) {  		chain = ao_fat_entry_raw_read(cluster, 0); -		if ((chain & 0xfff8) != 0xfff8) -			fatal("cluster %d: not marked busy\n", cluster); +		if (fat32) { +			if ((chain & 0xffffff8) != 0xffffff8) +				fatal("cluster %d: not marked busy\n", cluster); +		} else { +			if ((chain & 0xfff8) != 0xfff8) +				fatal("cluster %d: not marked busy\n", cluster); +		}  	}  	for (; cluster < number_cluster; cluster++) {  		chain = ao_fat_entry_raw_read(cluster, 0); @@ -296,40 +337,66 @@ check_fs(void)  				fatal("cluster %d: marked busy, but not in any file\n", cluster);  		} else {  			if (used[cluster] != 0) -				fatal("cluster %d: marked free, but foudn in file\n", cluster); +				fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1);  		}  	}  } -#define NUM_FILES	10 -#define LINES_FILE	80000 +#define NUM_FILES	100 +#define LINES_FILE	500000  uint32_t		sizes[NUM_FILES];  unsigned char		md5[NUM_FILES][MD5_DIGEST_LENGTH]; -int -main(int argc, char **argv) +void +short_test_fs(void) +{ +	int	len; +	char	buf[345]; + +	if (ao_fat_open("HELLO   TXT",AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { +		printf ("File contents for HELLO.TXT\n"); +		while ((len = ao_fat_read(buf, sizeof(buf)))) +			write(1, buf, len); +		ao_fat_close(); +	} +	 +	if (ao_fat_creat("NEWFILE TXT") == AO_FAT_SUCCESS) { +		printf ("Create new file\n"); +		for (len = 0; len < 2; len++) +			ao_fat_write("hello, world!\n", 14); +		ao_fat_seek(0, AO_FAT_SEEK_SET); +		printf ("read new file\n"); +		while ((len = ao_fat_read(buf, sizeof (buf)))) +			write (1, buf, len); +		ao_fat_close(); +	} + +	check_fs(); +} + +void +long_test_fs(void)  {  	char	name[12];  	int	id;  	MD5_CTX	ctx;  	unsigned char	md5_check[MD5_DIGEST_LENGTH]; +	char buf[337]; +	int	len; +	uint64_t	total_file_size = 0; -	if (argv[1]) -		fs = argv[1]; - -	ao_fat_init(); - -	check_bufio("top"); -	ao_fat_setup(); +	total_reads = total_writes = 0; -	check_fs(); -	check_bufio("after setup");  	printf ("   **** Creating %d files\n", NUM_FILES); +	memset(sizes, '\0', sizeof (sizes));  	for (id = 0; id < NUM_FILES; id++) {  		sprintf(name, "D%07dTXT", id); +		if ((id % (NUM_FILES/50)) == 0) { +			printf ("."); fflush(stdout); +		}  		if (ao_fat_creat(name) == AO_FAT_SUCCESS) {  			int j;  			char	line[64]; @@ -342,6 +409,7 @@ main(int argc, char **argv)  				ret = ao_fat_write((uint8_t *) line, len);  				if (ret <= 0)  					break; +				total_file_size += ret;  				MD5_Update(&ctx, line, ret);  				sizes[id] += ret;  				if (ret != len) @@ -353,20 +421,24 @@ main(int argc, char **argv)  		}  	} +	printf ("\n   **** Write IO: read %llu write %llu data sectors %llu\n", total_reads, total_writes, (total_file_size + 511) / 512); +  	check_bufio("all files created");  	printf ("   **** All done creating files\n");  	check_fs(); +	total_reads = total_writes = 0; +  	printf ("   **** Comparing %d files\n", NUM_FILES);  	for (id = 0; id < NUM_FILES; id++) { -		char buf[337];  		uint32_t size;  		sprintf(name, "D%07dTXT", id);  		size = 0; +		if ((id % (NUM_FILES/50)) == 0) { +			printf ("."); fflush(stdout); +		}  		if (ao_fat_open(name, AO_FAT_OPEN_READ) == AO_FAT_SUCCESS) { -			int	len; -  			MD5_Init(&ctx);  			while ((len = ao_fat_read((uint8_t *) buf, sizeof(buf))) > 0) {  				MD5_Update(&ctx, buf, len); @@ -382,7 +454,43 @@ main(int argc, char **argv)  			check_bufio("file shown");  		}  	} +	printf ("\n  **** Read IO: read %llu write %llu\n", total_reads, total_writes); +} + +char *params[] = { +	"-F 16 -C %s 16384", +	"-F 32 -C %s 16384", +	"-F 16 -C %s 65536", +	"-F 32 -C %s 65536", +	"-F 16 -C %s 1048576", +	"-F 32 -C %s 1048576", +	NULL +}; + +int +main(int argc, char **argv) +{ +	int	p; + +	if (argv[1]) +		fs = argv[1]; + +	for (p = 0; fs_params[p].fat; p++) { +		param = &fs_params[p]; +		ao_fat_init(); + +		check_bufio("top"); +		ao_fat_setup(); + +		check_fs(); +		check_bufio("after setup"); + +#ifdef SIMPLE_TEST +		short_test_fs(); +#else +		long_test_fs(); +#endif +	} -	printf ("\n    **** Total IO: read %llu write %llu\n", total_reads, total_writes);  	return 0;  }  | 
