--- /dev/null
+/*
+ * 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]);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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]);
+}
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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));
+}
+
+
--- /dev/null
+/*
+ * 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_ */
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
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
--- /dev/null
+/*
+ * 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;
+}