diff options
| author | Keith Packard <keithp@keithp.com> | 2013-03-27 01:12:33 -0700 | 
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2013-03-27 01:12:33 -0700 | 
| commit | e14834817f78a04b4d9b44a8373119dffd42c966 (patch) | |
| tree | d513cac8f9f111b81917fadfcdf253e7a897e997 | |
| parent | 747114786512339211d4981a7828c8c6f1f46c20 (diff) | |
altos: Add SDCARD and FAT16 filesystem support
This adds a fairly primitive FAT16 file system implementation
along with support for SD cards.
Signed-off-by: Keith Packard <keithp@keithp.com>
| -rw-r--r-- | src/drivers/ao_bufio.c | 298 | ||||
| -rw-r--r-- | src/drivers/ao_bufio.h | 36 | ||||
| -rw-r--r-- | src/drivers/ao_fat.c | 674 | ||||
| -rw-r--r-- | src/drivers/ao_fat.h | 73 | ||||
| -rw-r--r-- | src/drivers/ao_sdcard.c | 398 | ||||
| -rw-r--r-- | src/drivers/ao_sdcard.h | 75 | ||||
| -rw-r--r-- | src/test/Makefile | 5 | ||||
| -rw-r--r-- | src/test/ao_fat_test.c | 118 | 
8 files changed, 1676 insertions, 1 deletions
| diff --git a/src/drivers/ao_bufio.c b/src/drivers/ao_bufio.c new file mode 100644 index 00000000..9a5e801b --- /dev/null +++ b/src/drivers/ao_bufio.c @@ -0,0 +1,298 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef AO_FAT_TEST +#include "ao.h" +#endif + +#include "ao_sdcard.h" +#include "ao_bufio.h" + +#define AO_NUM_BUF		4 +#define AO_BUFSIZ		512 + +struct ao_bufio { +	uint32_t	block; +	int16_t		seqno; +	uint8_t		busy;	 +	uint8_t		dirty; +}; + +static struct ao_bufio	ao_bufio[AO_NUM_BUF]; +static uint8_t		ao_buffer[AO_NUM_BUF][AO_BUFSIZ]; +static int16_t		ao_seqno; +static uint8_t		ao_bufio_mutex; + +#if 0 +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + +static inline void +ao_bufio_lock(void) +{ +	ao_mutex_get(&ao_bufio_mutex); +} + +static inline void +ao_bufio_unlock(void) +{ +	ao_mutex_put(&ao_bufio_mutex); +} + +static inline int16_t +ao_seqno_age(int16_t s) +{ +	return ao_seqno - s; +} + +static inline int16_t +ao_seqno_next(void) +{ +	return ++ao_seqno; +} + +static inline int +ao_seqno_older(int16_t a, int16_t b) +{ +	return ao_seqno_age(a) > ao_seqno_age(b); +} + +static inline void +ao_validate_bufno(int b) +{ +	if (b < 0 || AO_NUM_BUF <= b) +		ao_panic(AO_PANIC_BUFIO); +} + +static inline int +ao_buf_to_num(uint8_t *buf) +{ +	int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ; + +	ao_validate_bufno(b); +	return b; +} + +static inline int +ao_bufio_to_num(struct ao_bufio *bufio) +{ +	int b = (bufio - ao_bufio); + +	ao_validate_bufno(b); +	return b; +} + +static inline struct ao_bufio * +ao_buf_to_bufio(uint8_t *buf) +{ +	int b = ao_buf_to_num(buf); +	struct ao_bufio *bufio; + +	bufio = &ao_bufio[b]; +	DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio); +	return bufio; +} + +static inline uint8_t * +ao_bufio_to_buf(struct ao_bufio *bufio) +{ +	int b = ao_bufio_to_num(bufio); +	uint8_t *buf; + +	buf = &ao_buffer[b][0]; +	DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf); +	return buf; +} + +/* + * Write a buffer to storage if it is dirty + */ +static void +ao_bufio_write(struct ao_bufio *bufio) +{ +	if (bufio->dirty) { +		ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio)); +		bufio->dirty = 0; +	} +} + +/* + * Read a buffer from storage + */ +static uint8_t +ao_bufio_read(struct ao_bufio *bufio) +{ +	uint8_t	*buf = ao_bufio_to_buf(bufio); + +	return ao_sdcard_read_block(bufio->block, buf); +} + +/* + * Find a buffer containing the specified block + */ +static struct ao_bufio * +ao_bufio_find_block(uint32_t block) +{ +	int b; + +	for (b = 0; b < AO_NUM_BUF; b++) { +		struct ao_bufio *bufio = &ao_bufio[b]; +		if (bufio->block == block) { +			DBG ("Found existing buffer %d (seqno %d)\n", +				ao_bufio_to_num(bufio), bufio->seqno); +			return bufio; +		} +	} +	return NULL; +} + +/* + * Find the least recently used idle buffer + */ +static struct ao_bufio * +ao_bufio_find_idle(void) +{ +	int b; +	struct ao_bufio *oldest = NULL; + +	for (b = 0; b < AO_NUM_BUF; b++) { +		struct ao_bufio *bufio = &ao_bufio[b]; +		if (!bufio->busy) +			if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno)) +				oldest = bufio; +	} +	if (oldest) +		DBG ("Using idle buffer %d (seqno %d)\n", +			ao_bufio_to_num(oldest), oldest->seqno); +	return oldest; +} + +/* + * Return a pointer to a buffer containing + * the contents of the specified block + */ +uint8_t * +ao_bufio_get(uint32_t block) +{ +	struct ao_bufio *bufio; +	uint8_t	*buf = NULL; + +	ao_bufio_lock(); +	bufio = ao_bufio_find_block(block); +	if (!bufio) { +		bufio = ao_bufio_find_idle(); +		if (bufio) { +			ao_bufio_write(bufio); +			bufio->block = block; +			DBG ("read buffer\n"); +			if (!ao_bufio_read(bufio)) { +				bufio->block = 0xffffffff; +				bufio = NULL; +			} +		} +	} +	if (bufio) { +		bufio->busy++; +		buf = ao_bufio_to_buf(bufio); +	} +	ao_bufio_unlock(); +	return buf; +} + +/* + * Release a buffer, marking it dirty + * if it has been written to + */ +void +ao_bufio_put(uint8_t *buf, uint8_t write) +{ +	struct ao_bufio *bufio; + +	ao_bufio_lock(); +	bufio = ao_buf_to_bufio(buf); +	 +	DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write); +	bufio->dirty |= write; +	if (!--bufio->busy) { +		bufio->seqno = ao_seqno_next(); +		DBG ("not busy, seqno %d\n", bufio->seqno); +	} +	ao_bufio_unlock(); +} + +/* + * Flush a single buffer immediately. Useful + * if write order is important + */ +void +ao_bufio_flush_one(uint8_t *buf) +{ +	ao_bufio_lock(); +	ao_bufio_write(ao_buf_to_bufio(buf)); +	ao_bufio_unlock(); +} + +/* + * Flush all buffers to storage + */ +void +ao_bufio_flush(void) +{ +	int	b; + +	ao_bufio_lock(); +	for (b = 0; b < AO_NUM_BUF; b++) +		ao_bufio_write(&ao_bufio[b]); +	ao_bufio_unlock(); +} + +static void +ao_bufio_test_read(void) +{ +	uint8_t	*buf; +	ao_cmd_decimal(); +	if (ao_cmd_status != ao_cmd_success) +		return; +	if ((buf = ao_bufio_get(ao_cmd_lex_u32))) { +		int i; +		for (i = 0; i < 512; i++) { +			printf (" %02x", buf[i]); +			if ((i & 0xf) == 0xf) +				printf("\n"); +		} +		ao_bufio_put(buf, 0); +	} +} + +static const struct ao_cmds ao_bufio_cmds[] = { +	{ ao_bufio_test_read,	"q\0Test bufio read" }, +	{ 0, NULL }, +}; + +void +ao_bufio_init(void) +{ +	int b; + +	for (b = 0; b < AO_NUM_BUF; b++) +		ao_bufio[b].block = 0xffffffff; +	ao_sdcard_init(); + +	ao_cmd_register(&ao_bufio_cmds[0]); +} diff --git a/src/drivers/ao_bufio.h b/src/drivers/ao_bufio.h new file mode 100644 index 00000000..c3bee906 --- /dev/null +++ b/src/drivers/ao_bufio.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_BUFIO_H_ +#define _AO_BUFIO_H_ + +uint8_t * +ao_bufio_get(uint32_t block); + +void +ao_bufio_put(uint8_t *buf, uint8_t write); + +void +ao_bufio_flush_one(uint8_t *buf); + +void +ao_bufio_flush(void); + +void +ao_bufio_init(void); + +#endif /* _AO_BUFIO_H_ */ diff --git a/src/drivers/ao_fat.c b/src/drivers/ao_fat.c new file mode 100644 index 00000000..a1476168 --- /dev/null +++ b/src/drivers/ao_fat.c @@ -0,0 +1,674 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef AO_FAT_TEST +#include "ao.h" +#endif + +#include "ao_fat.h" +#include "ao_bufio.h" + +/* Partition information, block numbers */ + +static uint8_t partition_type; +static uint32_t	partition_start, partition_end; + +/* 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 uint32_t	fat_start; +static uint32_t root_start; +static uint32_t data_start; + +static uint32_t +get_u32(uint8_t *base) +{ +	return ((uint32_t) base[0] | +		((uint32_t) base[1] << 8) | +		((uint32_t) base[2] << 16) | +		((uint32_t) base[3] << 24)); +} + +static void +put_u32(uint8_t *base, uint32_t value) +{ +	base[0] = value; +	base[1] = value >> 8; +	base[2] = value >> 16; +	base[3] = value >> 24; +} + +static uint16_t +get_u16(uint8_t *base) +{ +	return ((uint16_t) base[0] | ((uint16_t) base[1] << 8)); +} + +static void +put_u16(uint8_t *base, uint16_t value) +{ +	base[0] = value; +	base[1] = value >> 8; +} + +static uint8_t +ao_fat_cluster_valid(uint16_t cluster) +{ +	return (2 <= cluster && cluster < 0xfff0); +} + +/* Start using a block */ +static uint8_t * +ao_fat_block_get(uint32_t block) +{ +	block += partition_start; +	if (block >= partition_end) +		return NULL; +	return ao_bufio_get(block); +} + +/* Finish using a block, 'w' is 1 if modified */ +#define ao_fat_block_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 * 0x20; +	uint32_t	sector = byte >> 9; +	uint16_t	offset = byte & 0x1ff; +	uint8_t		*buf; + +	buf = ao_fat_block_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 * 0x20) & 0x1ff); +	uint8_t		*buf = root - offset; + +	ao_fat_block_put(buf, write); +} + +/* Get the next cluster entry in the chain */ +static uint16_t +ao_fat_entry_read(uint16_t cluster) +{ +	uint32_t	sector; +	uint16_t	offset; +	uint8_t		*buf; +	uint16_t	ret; + +	if (!ao_fat_cluster_valid(cluster)) +		return 0xfff7; + +	cluster -= 2; +	sector = cluster >> 8; +	offset = (cluster << 1) & 0x1ff; +	buf = ao_fat_block_get(fat_start + sector); +	if (!buf) +		return 0; +	ret = buf[offset] | (buf[offset+1] << 8); +	ao_fat_block_put(buf, 0); +	return ret; +} + +static uint16_t +ao_fat_entry_replace(uint16_t  cluster, uint16_t new_value) +{ +	uint32_t	sector; +	uint16_t	offset; +	uint8_t		*buf; +	uint16_t	ret; +	uint8_t		other_fats; + +	if (!ao_fat_cluster_valid(cluster)) +		return 0; + +	cluster -= 2; +	sector = cluster >> 8; +	offset = (cluster << 1) & 0x1ff; +	buf = ao_fat_block_get(fat_start + sector); +	if (!buf) +		return 0; +	ret = get_u16(buf + offset); +	put_u16(buf + offset, new_value); +	ao_fat_block_put(buf, 1); +	for (other_fats = 1; other_fats < number_fat; other_fats++) { +		buf = ao_fat_block_get(fat_start + other_fats * sectors_per_fat); +		if (buf) { +			put_u16(buf + offset, new_value); +			ao_fat_block_put(buf, 1); +		} +	} +	return ret; +	 +} + +static void +ao_fat_clear_cluster_chain(uint16_t cluster) +{ +	while (ao_fat_cluster_valid(cluster)) +		cluster = ao_fat_entry_replace(cluster, 0x0000); +} + +static uint16_t +ao_fat_cluster_seek(uint16_t cluster, uint16_t distance) +{ +	while (distance) { +		cluster = ao_fat_entry_read(cluster); +		if (!ao_fat_cluster_valid(cluster)) +			break; +		distance--; +	} +	return cluster; +} + +static uint32_t +ao_fat_sector_seek(uint16_t cluster, uint32_t sector) +{ +	cluster = ao_fat_cluster_seek(cluster, sector / sectors_per_cluster); +	if (!ao_fat_cluster_valid(cluster)) +		return 0xffffffff; +	return data_start + (cluster-2) * sectors_per_cluster + sector % sectors_per_cluster; +} + +/* Load the boot block and find the first partition */ +static uint8_t +ao_fat_setup_partition(void) +{ +	uint8_t *mbr; +	uint8_t	*partition; +	uint32_t partition_size; + +	mbr = ao_bufio_get(0); +	if (!mbr) +		return 0; + +	/* Check the signature */ +	if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) { +		printf ("Invalid MBR signature %02x %02x\n", +			mbr[0x1fe], mbr[0x1ff]); +		ao_bufio_put(mbr, 0); +		return 0; +	} + +	/* Just use the first partition */ +	partition = &mbr[0x1be]; +	 +	partition_type = partition[4]; +	switch (partition_type) { +	case 4:		/* FAT16 up to 32M */ +	case 6:		/* FAT16 over 32M */ +		break; +	case 0x0b:	/* FAT32 up to 2047GB */ +	case 0x0c:	/* FAT32 LBA */ +		break; +	default: +		printf ("Invalid partition type %02x\n", partition_type); +		ao_bufio_put(mbr, 0); +		return 0; +	} + +	partition_start = get_u32(partition+8); +	partition_size = get_u32(partition+12); +	if (partition_size == 0) { +		printf ("Zero-sized partition\n"); +		ao_bufio_put(mbr, 0); +		return 0; +	} +	partition_end = partition_start + partition_size; +	printf ("Partition type %02x start %08x end %08x\n", +		partition_type, partition_start, partition_end); +	ao_bufio_put(mbr, 0); +	return 1; +} +	 +static uint8_t +ao_fat_setup_fs(void) +{ +	uint8_t	*boot = ao_fat_block_get(0); + +	if (!boot) +		return 0; + +	/* Check the signature */ +	if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) { +		printf ("Invalid BOOT signature %02x %02x\n", +			boot[0x1fe], boot[0x1ff]); +		ao_bufio_put(boot, 0); +		return 0; +	} + +	/* Check the sector size */ +	if (get_u16(boot + 0xb) != 0x200) { +		printf ("Invalid sector size %d\n", +			get_u16(boot + 0xb)); +		ao_bufio_put(boot, 0); +		return 0; +	} + +	sectors_per_cluster = boot[0xd]; +	bytes_per_cluster = sectors_per_cluster << 9; +	reserved_sector_count = get_u16(boot+0xe); +	number_fat = boot[0x10]; +	root_entries = get_u16(boot + 0x11); +	sectors_per_fat = get_u16(boot+0x16); + +	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); +	printf ("root entries %d\n", root_entries); +	printf ("sectors per fat %d\n", sectors_per_fat); + +	fat_start = reserved_sector_count;; +	root_start = fat_start + number_fat * sectors_per_fat; +	data_start = root_start + ((root_entries * 0x20 + 0x1ff) >> 9); + +	printf ("fat  start %d\n", fat_start); +	printf ("root start %d\n", root_start); +	printf ("data start %d\n", data_start); + +	return 1; +} + +static uint8_t +ao_fat_setup(void) +{ +	if (!ao_fat_setup_partition()) +		return 0; +	if (!ao_fat_setup_fs()) +		return 0; +	return 1; +} + +/* + * Low-level directory operations + */ + +/* + * Basic file operations + */ + +static struct ao_fat_dirent	ao_file_dirent; +static uint32_t 		ao_file_offset; + +static uint32_t +ao_file_offset_to_sector(uint32_t offset) +{ +	if (offset > ao_file_dirent.size) +		return 0xffffffff; +	return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> 9); +} + +uint8_t +ao_fat_open(char name[11]) +{ +	uint16_t		entry = 0; +	struct ao_fat_dirent	dirent; + +	while (ao_fat_readdir(&entry, &dirent)) { +		if (!memcmp(name, dirent.name, 11)) { +			ao_file_dirent = dirent; +			ao_file_offset = 0; +			return 1; +		} +	} +	return 0; +} + + + +static uint8_t +ao_fat_set_size(uint32_t size) +{ +	uint16_t	clear_cluster = 0; +	uint8_t		*dent; +	uint16_t	first_cluster; + +	first_cluster = ao_file_dirent.cluster; +	printf ("set size to %d\n", size); +	if (size == ao_file_dirent.size) +		return 1; +	if (size == 0) { +		printf ("erase file\n"); +		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; + +			printf("Remove %d clusters\n", old_num - new_num); +			/* 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); + +			printf ("Last cluster is now %04x\n", last_cluster); +			/* 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; + +			if (old_num) +				last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1); +			else +				last_cluster = 0; + +			need = new_num - old_num; +			printf ("Need %d clusters\n", need); +			/* See if there are enough free clusters in the file system */ +			for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { +				if (!ao_fat_entry_read(free)) { +					printf ("\tCluster %04x available\n", free); +					need--; +				} +			} +			/* Still need some, tell the user that we've failed */ +			if (need) { +				printf ("File system full\n"); +				return 0; +			} + +			need = new_num - old_num; +			/* Now go allocate those clusters */ +			for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) { +				if (!ao_fat_entry_read(free)) { +					printf ("\tAllocate %04x\n", free); +					if (last_cluster) +						ao_fat_entry_replace(last_cluster, free); +					else +						first_cluster = free; +					last_cluster = free; +					need--; +				} +			} +			/* Mark the new end of the chain */ +			ao_fat_entry_replace(last_cluster, 0xffff); +		} +	} + +	/* Deallocate clusters off the end of the file */ +	if (ao_fat_cluster_valid(clear_cluster)) { +		printf ("Clear clusters starting with %04x\n", clear_cluster); +		ao_fat_clear_cluster_chain(clear_cluster); +	} + +	dent = ao_fat_root_get(ao_file_dirent.entry); +	if (!dent) +		return 0; +	put_u32(dent + 0x1c, size); +	put_u16(dent + 0x1a, first_cluster); +	ao_fat_root_put(dent, ao_file_dirent.entry, 1); +	ao_file_dirent.size = size; +	ao_file_dirent.cluster = first_cluster; +	return 1; +} + +uint8_t +ao_fat_creat(char name[11]) +{ +	uint16_t	entry; + +	if (ao_fat_open(name)) +		return ao_fat_set_size(0); + +	for (entry = 0; entry < root_entries; entry++) { +		uint8_t	*dent = ao_fat_root_get(entry); + +		if (dent[0] == AO_FAT_DENT_EMPTY || +		    dent[0] == AO_FAT_DENT_END) { +			memmove(dent, name, 11); +			dent[0x0b] = 0x00; +			dent[0x0c] = 0x00; +			dent[0x0d] = 0x00; +			/* XXX fix time */ +			put_u16(dent + 0x0e, 0); +			/* XXX fix date */ +			put_u16(dent + 0x10, 0); +			/* XXX fix date */ +			put_u16(dent + 0x12, 0); +			/* XXX FAT32 high cluster bytes */ +			put_u16(dent + 0x14, 0); +			/* XXX fix time */ +			put_u16(dent + 0x16, 0); +			/* XXX fix date */ +			put_u16(dent + 0x18, 0); +			/* cluster number */ +			put_u16(dent + 0x1a, 0); +			/* size */ +			put_u32(dent + 0x1c, 0); +			ao_fat_root_put(dent, entry, 1); +			return ao_fat_open(name); +		} +	} +	return 0; +} + +void +ao_fat_close(void) +{ +	memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent)); +	ao_bufio_flush(); +} + +int +ao_fat_read(uint8_t *dest, int len) +{ +	uint32_t	sector; +	uint16_t	this_time; +	uint16_t	offset; +	uint8_t		*buf; +	int		ret = 0; + +	if (ao_file_offset + len > ao_file_dirent.size) +		len = ao_file_dirent.size - ao_file_offset; + +	while (len) { +		offset = ao_file_offset & 0x1ff; +		if (offset + len < 512) +			this_time = len; +		else +			this_time = 512 - offset; + +		sector = ao_file_offset_to_sector(ao_file_offset); +		if (sector == 0xffffffff) +			break; +		buf = ao_fat_block_get(sector); +		if (!buf) +			break; +		memcpy(dest, buf + offset, this_time); +		ao_fat_block_put(buf, 0); + +		ret += this_time; +		len -= this_time; +		dest += this_time; +		ao_file_offset += this_time; +	} +	return ret; +} + +int +ao_fat_write(uint8_t *src, int len) +{ +	uint32_t	sector; +	uint16_t	this_time; +	uint16_t	offset; +	uint8_t		*buf; +	int		ret = 0; + +	if (ao_file_offset + len > ao_file_dirent.size) { +		if (!ao_fat_set_size(ao_file_offset + len)) +			return 0; +	} + +	while (len) { +		offset = ao_file_offset & 0x1ff; +		if (offset + len < 512) +			this_time = len; +		else +			this_time = 512 - offset; + +		sector = ao_file_offset_to_sector(ao_file_offset); +		if (sector == 0xffffffff) +			break; +		buf = ao_fat_block_get(sector); +		if (!buf) +			break; +		memcpy(buf + offset, src, this_time); +		ao_fat_block_put(buf, 1); + +		ret += this_time; +		len -= this_time; +		src += this_time; +		ao_file_offset += this_time; +	} +	return 0; +} + +uint32_t +ao_fat_seek(int32_t pos, uint8_t whence) +{ +	switch (whence) { +	case AO_FAT_SEEK_SET: +		ao_file_offset = pos; +		break; +	case AO_FAT_SEEK_CUR: +		ao_file_offset += pos; +		break; +	case AO_FAT_SEEK_END: +		ao_file_offset = ao_file_dirent.size + pos; +		break; +	} +	if (ao_file_offset > ao_file_dirent.size) +		ao_fat_set_size(ao_file_offset); +	return ao_file_offset; +} + +uint8_t +ao_fat_unlink(char name[11]) +{ +	uint16_t		entry = 0; +	struct ao_fat_dirent	dirent; + +	while (ao_fat_readdir(&entry, &dirent)) { +		if (memcmp(name, dirent.name, 11) == 0) { +			uint8_t	*next; +			uint8_t	*ent; +			uint8_t	delete; +			ao_fat_clear_cluster_chain(dirent.cluster); +			next = ao_fat_root_get(dirent.entry + 1); +			if (next && next[0] != AO_FAT_DENT_END) +				delete = AO_FAT_DENT_EMPTY; +			else +				delete = AO_FAT_DENT_END; +			if (next) +				ao_fat_root_put(next, dirent.entry + 1, 0); +			ent = ao_fat_root_get(dirent.entry); +			if (ent) { +				memset(ent, '\0', 0x20); +				*ent = delete; +				ao_fat_root_put(ent, dirent.entry, 1); +			} +			ao_bufio_flush(); +			return 1; +		} +	} +	return 0; +} + +uint8_t +ao_fat_rename(char old[11], char new[11]) +{ +	return 0; +} + +uint8_t +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[0] == AO_FAT_DENT_END) { +			ao_fat_root_put(dent, *entry, 0); +			return 0; +		} +		if (dent[0] != AO_FAT_DENT_EMPTY && +		    (dent[0x0b] & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == 0) +			break; +		ao_fat_root_put(dent, *entry, 0); +		(*entry)++; +	} +	memcpy(dirent->name, dent, 11); +	dirent->attr = dent[0xb]; +	dirent->size = get_u32(dent+0x1c); +	dirent->cluster = get_u16(dent+0x1a); +	dirent->entry = *entry; +	ao_fat_root_put(dent, *entry, 0); +	(*entry)++; +	return 1; +} + +static void +ao_fat_list(void) +{ +	uint16_t		entry = 0; +	struct ao_fat_dirent	dirent; + +	while (ao_fat_readdir(&entry, &dirent)) { +		printf ("%-8.8s.%-3.3s %02x %d\n", +			dirent.name, dirent.name + 8, dirent.attr, dirent.size); +	} +} + +static void +ao_fat_test(void) +{ +	ao_fat_setup(); +	ao_fat_list(); +} + +static const struct ao_cmds ao_fat_cmds[] = { +	{ ao_fat_test,	"F\0Test FAT" }, +	{ 0, NULL }, +}; + +void +ao_fat_init(void) +{ +	ao_bufio_init(); +	ao_cmd_register(&ao_fat_cmds[0]); +} + diff --git a/src/drivers/ao_fat.h b/src/drivers/ao_fat.h new file mode 100644 index 00000000..2bf6222e --- /dev/null +++ b/src/drivers/ao_fat.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_FAT_H_ +#define _AO_FAT_H_ + +void +ao_fat_init(void); + +#define AO_FAT_FILE_READ_ONLY		0x01 +#define AO_FAT_FILE_HIDDEN		0x02 +#define AO_FAT_FILE_SYSTEM		0x04 +#define AO_FAT_FILE_VOLUME_LABEL	0x08 +#define AO_FAT_FILE_DIRECTORY		0x10 +#define AO_FAT_FILE_ARCHIVE		0x20 + +#define AO_FAT_DENT_EMPTY		0xe5 +#define AO_FAT_DENT_END			0x00 + +uint8_t +ao_fat_open(char name[11]); + +uint8_t +ao_fat_creat(char name[11]); + +void +ao_fat_close(void); + +int +ao_fat_read(uint8_t *dest, int len); + +int +ao_fat_write(uint8_t *buf, int len); + +#define AO_FAT_SEEK_SET	0 +#define AO_FAT_SEEK_CUR	1 +#define AO_FAT_SEEK_END	2 + +uint32_t +bao_fat_seek(int32_t pos, uint8_t whence); + +uint8_t +ao_fat_unlink(char name[11]); + +uint8_t +ao_fat_rename(char old[11], char new[11]); + +struct ao_fat_dirent { +	char		name[11]; +	uint8_t		attr; +	uint32_t	size; +	uint16_t	cluster; +	uint16_t	entry; +}; + +uint8_t +ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent); + +#endif /* _AO_FAT_H_ */ diff --git a/src/drivers/ao_sdcard.c b/src/drivers/ao_sdcard.c new file mode 100644 index 00000000..2174af1e --- /dev/null +++ b/src/drivers/ao_sdcard.c @@ -0,0 +1,398 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" +#include "ao_sdcard.h" + +#define ao_sdcard_get_slow()		ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz) +#define ao_sdcard_get()			ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST) +#define ao_sdcard_put()			ao_spi_put(AO_SDCARD_SPI_BUS) +#define ao_sdcard_send_fixed(d,l)	ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_send(d,l)		ao_spi_send((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_recv(d,l)		ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS) +#define ao_sdcard_select()		ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0) +#define ao_sdcard_deselect()		ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1) + + +static uint8_t	initialized; +static uint8_t	present; +static uint8_t	mutex; +static enum ao_sdtype sdtype; + +#define ao_sdcard_lock()	ao_mutex_get(&mutex) +#define ao_sdcard_unlock()	ao_mutex_put(&mutex) + +#if 0 +#define DBG(...) printf(__VA_ARGS__) +#else +#define DBG(...) +#endif + +/* + * Send an SD command and await the status reply + */ + +static uint8_t +ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg) +{ +	uint8_t	data[6]; +	uint8_t	reply; +	int i; + +	DBG ("\tsend_cmd %d arg %08x\n", cmd, arg); +	if (cmd != SDCARD_GO_IDLE_STATE) { +		for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { +			ao_sdcard_recv(&reply, 1); +			if (reply == 0xff) +				break; +		} +		if (i == SDCARD_CMD_TIMEOUT) +			return SDCARD_STATUS_TIMEOUT; +	} +	 +	data[0] = cmd & 0x3f | 0x40; +	data[1] = arg >> 24; +	data[2] = arg >> 16; +	data[3] = arg >> 8; +	data[4] = arg; +	if (cmd == SDCARD_GO_IDLE_STATE) +		data[5] = 0x95;	/* Valid for 0 arg */ +	else if (cmd == SDCARD_SEND_IF_COND) +		data[5] = 0x87;	/* Valid for 0x1aa arg */ +	else +		data[5] = 0xff;	/* no CRC */ +	ao_sdcard_send(data, 6); + +	/* The first reply byte will be the status, +	 * which must have the high bit clear +	 */ +	for (i = 0; i < SDCARD_CMD_TIMEOUT; i++) { +		ao_sdcard_recv(&reply, 1); +		DBG ("\t\tgot byte %02x\n", reply); +		if ((reply & 0x80) == 0) +			return reply; +	} +	return SDCARD_STATUS_TIMEOUT; +} + +/* + * Retrieve any reply, discarding the trailing CRC byte + */ +static void +ao_sdcard_recv_reply(uint8_t *reply, int len) +{ +	uint8_t	discard; + +	if (len) +		ao_sdcard_recv(reply, len); +	/* trailing byte */ +	ao_sdcard_recv(&discard, 1); +} + +/* + * Wait while the card is busy. The + * card will return a stream of 0xff + * until it isn't busy anymore + */ +static void +ao_sdcard_wait_busy(void) +{ +	uint8_t	v; + +	do { +		ao_sdcard_recv(&v, 1); +	} while (v != 0xff); +	ao_sdcard_send_fixed(0xff, 1); +} + +static uint8_t +ao_sdcard_go_idle_state(void) +{ +	uint8_t	ret; + +	DBG ("go_idle_state\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_GO_IDLE_STATE, 0); +	ao_sdcard_recv_reply(NULL, 0); +	ao_sdcard_deselect(); +	DBG ("\tgo_idle_state status %02x\n", ret); +	return ret; +} + +static uint8_t +ao_sdcard_send_op_cond(void) +{ +	uint8_t	ret; + +	DBG ("send_op_cond\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_SEND_OP_COND, 0); +	ao_sdcard_recv_reply(NULL, 0); +	ao_sdcard_deselect(); +	DBG ("\tsend_op_cond %02x\n", ret); +	return ret; +} + +static uint8_t +ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4]) +{ +	uint8_t ret; + +	DBG ("send_if_cond\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_SEND_IF_COND, arg); +	if (ret != SDCARD_STATUS_IDLE_STATE) { +		DBG ("\tsend_if_cond failed %02x\n", ret); +		return ret; +	} +	ao_sdcard_recv_reply(send_if_cond_response, 4); +	DBG ("send_if_cond status %02x response %02x %02x %02x %02x\n", +		ret, +		send_if_cond_response[0], +		send_if_cond_response[1], +		send_if_cond_response[2], +		send_if_cond_response[3]); +	ao_sdcard_deselect(); +	return ret; +} + +static uint8_t +ao_sdcard_set_blocklen(uint32_t blocklen) +{ +	uint8_t ret; + +	DBG ("set_blocklen %d\n", blocklen); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_SET_BLOCKLEN, blocklen); +	ao_sdcard_recv_reply(NULL, 0); +	if (ret != SDCARD_STATUS_READY_STATE) +		DBG ("\tsend_if_cond failed %02x\n", ret); +	return ret; +	 +} + +static uint8_t +ao_sdcard_app_cmd(void) +{ +	uint8_t	ret; + +	DBG ("app_cmd\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_APP_CMD, 0); +	ao_sdcard_recv_reply(NULL, 0); +	ao_sdcard_deselect(); +	DBG ("\tapp_cmd status %02x\n"); +	return ret; +} + +static uint8_t +ao_sdcard_app_send_op_cond(uint32_t arg) +{ +	uint8_t	ret; + +	ret = ao_sdcard_app_cmd(); +	if (ret != SDCARD_STATUS_IDLE_STATE) +		return ret; +	DBG("send_op_comd\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_APP_SEND_OP_COMD, arg); +	ao_sdcard_recv_reply(NULL, 0); +	ao_sdcard_deselect(); +	DBG ("\tapp_send_op_cond status %02x\n", ret); +	return ret; +} + +static uint8_t +ao_sdcard_read_ocr(uint8_t read_ocr_response[4]) +{ +	uint8_t	ret; + +	DBG ("read_ocr\n"); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_READ_OCR, 0); +	if (ret != SDCARD_STATUS_READY_STATE) +		DBG ("\tread_ocr failed %02x\n", ret); +	else { +		ao_sdcard_recv_reply(read_ocr_response, 4); +		DBG ("\tread_ocr status %02x response %02x %02x %02x %02x\n", ret, +			read_ocr_response[0], read_ocr_response[1], +			read_ocr_response[2], read_ocr_response[3]); +	} +	ao_sdcard_deselect(); +	return ret; +} + +static void +ao_sdcard_setup(void) +{ +	int	i; +	uint8_t	ret; +	uint8_t	response[10]; + +	DBG ("Testing sdcard\n"); + +	ao_sdcard_get_slow(); +	/* +	 * min 74 clocks with CS high +	 */ +	ao_sdcard_send_fixed(0xff, 10); + +	ao_delay(AO_MS_TO_TICKS(10)); + +	/* Reset the card and get it into SPI mode */ + +	for (i = 0; i < SDCARD_IDLE_WAIT; i++) { +		if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE) +			break; +	} +	if (i == SDCARD_IDLE_WAIT) +		goto bail; + +	/* Figure out what kind of card we have */ + +	sdtype = ao_sdtype_unknown; + +	if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) { +		uint32_t	arg = 0; +		uint8_t		sdver2 = 0; + +		/* Check for SD version 2 */ +		if ((response[2] & 0xf) == 1 && response[3] == 0xaa) { +			arg = 0x40000000; +			sdver2 = 1; +		} + +		for (i = 0; i < SDCARD_IDLE_WAIT; i++) { +			ret = ao_sdcard_app_send_op_cond(arg); +			if (ret != SDCARD_STATUS_IDLE_STATE) +				break; +		} +		if (ret != SDCARD_STATUS_READY_STATE) { +			/* MMC */ +			for (i = 0; i < SDCARD_IDLE_WAIT; i++) { +				ret = ao_sdcard_send_op_cond(); +				if (ret != SDCARD_STATUS_IDLE_STATE) +					break; +			} +			if (ret != SDCARD_STATUS_READY_STATE) +				goto bail; +			sdtype = ao_sdtype_mmc3; +		} else { +			/* SD */ +			if (sdver2 != 0) { +				ret = ao_sdcard_read_ocr(response); +				if (ret != SDCARD_STATUS_READY_STATE) +					goto bail; +				if ((response[0] & 0xc0) == 0xc0) +					sdtype = ao_sdtype_sd2block; +				else +					sdtype = ao_sdtype_sd2byte; +			} else { +				sdtype = ao_sdtype_sd1; +			} +		} + +		/* For everything but SDHC cards, set the block length */ +		if (sdtype != ao_sdtype_sd2block) { +			ret = ao_sdcard_set_blocklen(512); +			if (ret != SDCARD_STATUS_READY_STATE) +				DBG ("set_blocklen failed, ignoring\n"); +		} +	} + +	DBG ("SD card detected, type %d\n", sdtype); +bail: +	ao_sdcard_put(); +} + +static uint8_t +ao_sdcard_wait_block_start(void) +{ +	int	i; +	uint8_t	v; + +	DBG ("\twait_block_start\n"); +	for (i = 0; i < SDCARD_BLOCK_TIMEOUT; i++) { +		ao_sdcard_recv(&v, 1); +		DBG("\t\trecv %02x\n", v); +		if (v != 0xff) +			break; +	} +	return v; +} + +/* + * Read a block of 512 bytes from the card + */ +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data) +{ +	uint8_t	ret; +	uint8_t crc[2]; + +	ao_sdcard_lock(); +	if (!initialized) { +		ao_sdcard_setup(); +		initialized = 1; +		if (sdtype != ao_sdtype_unknown) +			present = 1; +	} +	if (!present) { +		ao_sdcard_unlock(); +		return 0; +	} +	if (sdtype != ao_sdtype_sd2block) +		block <<= 9; +	ao_sdcard_get(); +	ao_sdcard_select(); +	ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block); +	ao_sdcard_recv_reply(NULL, 0); +	if (ret != SDCARD_STATUS_READY_STATE) +		goto bail; + +	if (ao_sdcard_wait_block_start() != 0xfe) { +		ret = 0x3f; +		goto bail; +	} + +	ao_sdcard_recv(data, 512); +	ao_sdcard_recv(crc, 2); +bail: +	ao_sdcard_deselect(); +	ao_sdcard_put(); +	ao_sdcard_unlock(); +	return ret == SDCARD_STATUS_READY_STATE; +} + +/* + * Write a block of 512 bytes to the card + */ +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data) +{ +	/* Not doing anything until the file system code seems reasonable +	 */ +	return 1; +} + +void +ao_sdcard_init(void) +{ +	ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN)); +} + + diff --git a/src/drivers/ao_sdcard.h b/src/drivers/ao_sdcard.h new file mode 100644 index 00000000..b9f737c5 --- /dev/null +++ b/src/drivers/ao_sdcard.h @@ -0,0 +1,75 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_SDCARD_H_ +#define _AO_SDCARD_H_ + +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data); + +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data); + +void +ao_sdcard_init(void); + +/* Commands */ +#define SDCARD_GO_IDLE_STATE		0 +#define SDCARD_SEND_OP_COND		1 +#define SDCARD_SEND_IF_COND		8 +#define SDCARD_SEND_CSD			9 +#define SDCARD_SEND_CID			10 +#define SDCARD_SEND_STATUS		13 +#define SDCARD_SET_BLOCKLEN		16 +#define SDCARD_READ_BLOCK		17 +#define SDCARD_WRITE_BLOCK		24 +#define SDCARD_WRITE_MULTIPLE_BLOCK	25 +#define SDCARD_ERASE_WR_BLK_START	32 +#define SDCARD_ERASE_WR_BLK_END		33 +#define SDCARD_ERASE			38 +#define SDCARD_APP_CMD			55 +#define SDCARD_READ_OCR			58 + +/* App commands */ +#define SDCARD_APP_SET_WR_BLK_ERASE_COUNT	23 +#define SDCARD_APP_SEND_OP_COMD			41 + +/* Status */ +#define SDCARD_STATUS_READY_STATE	0 +#define SDCARD_STATUS_IDLE_STATE	1 +#define SDCARD_STATUS_ILLEGAL_COMMAND	4 +#define SDCARD_STATUS_TIMEOUT		0xff + +#define SDCARD_DATA_START_BLOCK		0xfe +#define SDCARD_STOP_TRAN_TOKEN		0xfd +#define SDCARD_WRITE_MULTIPLE_TOKEN	0xfc +#define SDCARD_DATA_RES_MASK		0x1f +#define SDCARD_DATA_RES_ACCEPTED	0x05 + +#define SDCARD_CMD_TIMEOUT		100 +#define SDCARD_IDLE_WAIT		100 +#define SDCARD_BLOCK_TIMEOUT		100 + +enum ao_sdtype { +	ao_sdtype_unknown, +	ao_sdtype_mmc3, +	ao_sdtype_sd1, +	ao_sdtype_sd2byte, +	ao_sdtype_sd2block, +}; + +#endif /* _AO_SDCARD_H_ */ diff --git a/src/test/Makefile b/src/test/Makefile index fccc7937..96170cc1 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -2,7 +2,7 @@ vpath % ..:../core:../drivers:../util:../micropeak  PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \  	ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \ -	ao_aprs_test ao_micropeak_test +	ao_aprs_test ao_micropeak_test ao_fat_test  INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h @@ -64,3 +64,6 @@ check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests  ao_micropeak_test: ao_micropeak_test.c ao_microflight.c ao_kalman.h  	cc $(CFLAGS) -o $@ ao_micropeak_test.c -lm + +ao_fat_test: ao_fat_test.c ao_fat.c ao_bufio.c +	cc $(CFLAGS) -o $@ ao_fat_test.c diff --git a/src/test/ao_fat_test.c b/src/test/ao_fat_test.c new file mode 100644 index 00000000..22ac03ad --- /dev/null +++ b/src/test/ao_fat_test.c @@ -0,0 +1,118 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> + +#define AO_FAT_TEST + +void +ao_mutex_get(uint8_t *mutex) +{ +} + +void +ao_mutex_put(uint8_t *mutex) +{ +} + +void +ao_panic(uint8_t panic) +{ +	printf ("panic %d\n", panic); +	exit(1); +} + +#define AO_PANIC_BUFIO	15 + +#define ao_cmd_success	0 + +uint8_t ao_cmd_status; +uint32_t ao_cmd_lex_u32; + +void +ao_cmd_decimal() +{ +} + +#define ao_cmd_register(x) + +struct ao_cmds { +	void		(*func)(void); +	const char	*help; +}; + +int fs_fd; + +uint8_t +ao_sdcard_read_block(uint32_t block, uint8_t *data) +{ +	lseek(fs_fd, block * 512, 0); +	return read(fs_fd, data, 512) == 512; +} + +uint8_t +ao_sdcard_write_block(uint32_t block, uint8_t *data) +{ +	lseek(fs_fd, block * 512, 0); +	return write(fs_fd, data, 512) == 512; +} + +void +ao_sdcard_init(void) +{ +	fs_fd = open("fat.fs", 2); +} + +#include "ao_bufio.c" +#include "ao_fat.c" + +int +main(int argc, char **argv) +{ +	uint8_t	data[15]; +	int	len; +	ao_fat_init(); +	ao_fat_test(); +	if (ao_fat_open("DATALOG TXT")) { +		printf ("DATALOG.TXT\n"); +		while ((len = ao_fat_read(data, sizeof (data))) > 0) { +			write(1, data, len); +		} +		ao_fat_close(); +//		ao_fat_unlink("DATALOG TXT"); +	} +	if (ao_fat_open("NEWFILE TXT")) { +		printf ("NEWFILE.TXT\n"); +		while ((len = ao_fat_read(data, sizeof (data))) > 0) { +			write(1, data, len); +		} +		ao_fat_close(); +	} +	if (ao_fat_creat ("NEWFILE TXT")) { +		for (len = 0; len < 4095; len++) +			ao_fat_write((uint8_t *) "hello, world!\n", 14); +		ao_fat_close(); +	} +	return 0; +} | 
