/* * Copyright © 2013 Keith Packard * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 #include #include #include #include #include #include #include #include #include #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); abort(); } #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; 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) { 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" /* 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) { 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; 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(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); } 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"); } } 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; }