#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <string.h>
#include <getopt.h>
#include <math.h>
#include <unistd.h>
#include <fcntl.h>
+#include <openssl/md5.h>
#define AO_FAT_TEST
ao_panic(uint8_t panic)
{
printf ("panic %d\n", panic);
- exit(1);
+ abort();
}
#define AO_PANIC_BUFIO 15
int fs_fd;
+uint64_t total_reads, total_writes;
+
uint8_t
ao_sdcard_read_block(uint32_t block, uint8_t *data)
{
+ ++total_reads;
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)
{
+ ++total_writes;
lseek(fs_fd, block * 512, 0);
return write(fs_fd, data, 512) == 512;
}
+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)
{
- fs_fd = open("fat.fs", 2);
+ char cmd[1024];
+
+ 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);
+ }
+ fs_fd = open(fs, 2);
+ if (fs_fd < 0) {
+ perror (fs);
+ exit(1);
+ }
}
#include "ao_bufio.c"
+void
+check_bufio(char *where)
+{
+ int b;
+
+ for (b = 0; b < AO_NUM_BUF; b++) {
+ if (ao_bufio[b].busy) {
+ printf ("%s: buffer %d busy. block %d seqno %u\n",
+ where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff);
+ abort();
+ }
+ }
+}
+
+
+void
+check_fat(void);
+
#include "ao_fat.c"
-int
-main(int argc, char **argv)
+/* Get the next cluster entry in the chain */
+static cluster_t
+ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat)
+{
+ 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;
+ if (fat32)
+ ret = get_u32(buf + offset);
+ else
+ ret = get_u16(buf + offset);
+ _ao_fat_sector_put(buf, 0);
+ return ret;
+}
+
+void
+dump_fat(void)
+{
+ int e;
+
+ printf ("\n **** FAT ****\n\n");
+ for (e = 0; e < number_cluster; e++) {
+ if ((e & 0xf) == 0x0)
+ printf ("%04x: ", e);
+ 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)
+{
+ dirent_t entry = 0;
+ struct ao_fat_dirent dirent;
+
+ printf (" **** Root directory ****\n");
+ while (ao_fat_readdir(&entry, &dirent)) {
+ printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
+ entry,
+ dirent.name,
+ dirent.name + 8,
+ dirent.attr,
+ dirent.cluster,
+ dirent.size);
+ }
+
+ printf (" **** End of root directory ****\n");
+}
+
+void
+fatal(char *msg, ...)
+{
+// dump_fat();
+// fat_list();
+
+ va_list l;
+ va_start(l, msg);
+ vfprintf(stderr, msg, l);
+ va_end(l);
+
+ abort();
+}
+
+void
+check_fat(void)
{
- uint8_t data[15];
+ cluster_t e;
+ int f;
+
+ for (e = 0; e < number_cluster; e++) {
+ cluster_t v = ao_fat_entry_raw_read(e, 0);
+ for (f = 1; f < number_fat; f++) {
+ cluster_t o = ao_fat_entry_raw_read(e, f);
+ if (o != v)
+ fatal ("fats differ at %08x (0 %08x %d %08x)\n", e, v, f, o);
+ }
+ }
+}
+
+cluster_t
+check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used)
+{
+ cluster_t clusters = 0;
+ cluster_t cluster;
+
+ if (!first_cluster)
+ return 0;
+
+ for (cluster = first_cluster;
+ 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 %08x\n", dent, cluster);
+ if (used[cluster])
+ fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1);
+ used[cluster] = dent;
+ clusters++;
+ }
+ return clusters;
+}
+
+void
+check_fs(void)
+{
+ dirent_t r;
+ cluster_t cluster, chain;
+ dirent_t *used;
+ uint8_t *dent;
+
+ check_fat();
+
+ used = calloc(sizeof (dirent_t), number_cluster);
+
+ 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);
+
+ if (name[0] == AO_FAT_DENT_END) {
+ break;
+ }
+
+ clusters = check_file(r + 1, first_cluster, used);
+ if (size == 0) {
+ if (clusters != 0)
+ fatal("file %d: zero sized, but %d clusters\n", clusters);
+ } else {
+ if (size > clusters * bytes_per_cluster)
+ fatal("file %d: size %u beyond clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ if (size <= (clusters - 1) * bytes_per_cluster)
+ fatal("file %d: size %u too small clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ }
+ }
+ 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 (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);
+
+ if (chain != 0) {
+ if (used[cluster] == 0)
+ fatal("cluster %d: marked busy, but not in any file\n", cluster);
+ } else {
+ if (used[cluster] != 0)
+ fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1);
+ }
+ }
+}
+
+#define NUM_FILES 100
+#define LINES_FILE 500000
+
+uint32_t sizes[NUM_FILES];
+
+unsigned char md5[NUM_FILES][MD5_DIGEST_LENGTH];
+
+void
+micro_test_fs(void)
+{
+ int8_t fd;
+ char name[] = "FOO ";
+ char buf[512];
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);
+
+ printf ("write once\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ ao_fat_write(fd, "hello world\n", 12);
+ ao_fat_close(fd);
+ }
+
+ printf ("read first\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ len = ao_fat_read(fd, buf, sizeof(buf));
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ printf ("write again\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ ao_fat_write(fd, "hi\n", 3);
+ ao_fat_close(fd);
+ }
+
+ printf ("read again\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ len = ao_fat_read(fd, buf, sizeof(buf));
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ printf ("write 3\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ int l;
+ char c;
+
+ for (l = 0; l < 10; l++) {
+ for (c = ' '; c < '~'; c++)
+ ao_fat_write(fd, &c, 1);
+ c = '\n';
+ ao_fat_write(fd, &c, 1);
}
- ao_fat_close();
-// ao_fat_unlink("DATALOG TXT");
+ ao_fat_close(fd);
+ }
+
+ printf ("read 3\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0)
+ write (1, buf, len);
+ ao_fat_close(fd);
}
- if (ao_fat_open("NEWFILE TXT")) {
- printf ("NEWFILE.TXT\n");
- while ((len = ao_fat_read(data, sizeof (data))) > 0) {
- write(1, data, len);
+
+ check_fs();
+ printf ("all done\n");
+}
+
+
+void
+short_test_fs(void)
+{
+ int len;
+ int8_t fd;
+ char buf[345];
+
+ if ((fd = ao_fat_open("HELLO TXT",AO_FAT_OPEN_READ)) >= 0) {
+ printf ("File contents for HELLO.TXT\n");
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))))
+ write(1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ if ((fd = ao_fat_creat("NEWFILE TXT")) >= 0) {
+ printf ("Create new file\n");
+ for (len = 0; len < 2; len++)
+ ao_fat_write(fd, "hello, world!\n", 14);
+ ao_fat_seek(fd, 0, AO_FAT_SEEK_SET);
+ printf ("read new file\n");
+ while ((len = ao_fat_read(fd, buf, sizeof (buf))))
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ 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;
+ int8_t fd;
+ uint64_t total_file_size = 0;
+
+ total_reads = total_writes = 0;
+
+ 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+49)/50)) == 0) {
+ printf ("."); fflush(stdout);
+ }
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ int j;
+ char line[64];
+ check_bufio("file created");
+ MD5_Init(&ctx);
+ for (j = 0; j < LINES_FILE; j++) {
+ int len, ret;
+ sprintf (line, "Hello, world %d %d\r\n", id, j);
+ len = strlen(line);
+ ret = ao_fat_write(fd, line, len);
+ if (ret <= 0)
+ break;
+ total_file_size += ret;
+ MD5_Update(&ctx, line, ret);
+ sizes[id] += ret;
+ if (ret != len)
+ printf ("write failed %d\n", ret);
+ }
+ ao_fat_close(fd);
+ MD5_Final(&md5[id][0], &ctx);
+ check_bufio("file written");
}
- 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();
+
+ 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++) {
+ uint32_t size;
+ sprintf(name, "D%07dTXT", id);
+ size = 0;
+ if ((id % ((NUM_FILES+49)/50)) == 0) {
+ printf ("."); fflush(stdout);
+ }
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ MD5_Init(&ctx);
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+ MD5_Update(&ctx, buf, len);
+ size += len;
+ }
+ ao_fat_close(fd);
+ MD5_Final(md5_check, &ctx);
+ if (size != sizes[id])
+ fatal("file %d: size differs %d written %d read\n",
+ id, sizes[id], size);
+ if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0)
+ fatal ("file %d: checksum failed\n", id);
+ 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
+};
+
+void
+do_test(void (*test)(void))
+{
+ ao_fat_init();
+
+ check_bufio("top");
+ _ao_fat_setup();
+
+ check_fs();
+ check_bufio("after setup");
+ (*test)();
+ ao_fat_unmount();
+}
+
+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];
+
+ do_test(micro_test_fs);
+ do_test(short_test_fs);
+ do_test(long_test_fs);
}
+ unlink (fs);
+
return 0;
}