altos/test: Adjust CRC error rate after FEC fix
[fw/altos] / src / drivers / ao_fat.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #ifndef AO_FAT_TEST
20 #include "ao.h"
21 #endif
22
23 #include "ao_fat.h"
24 #include "ao_bufio.h"
25
26 /* Include FAT commands */
27 #ifndef AO_FAT_TEST
28 #define FAT_COMMANDS    1
29 #endif
30
31 /* Spew FAT tracing */
32 #define FAT_TRACE       0
33  
34 #ifdef DBG
35 #undef DBG
36 #endif
37
38 #if FAT_TRACE
39 #define DBG(...) printf(__VA_ARGS__)
40 #else
41 #define DBG(...)
42 #endif
43
44 /*
45  * Basic file system types
46  */
47
48 typedef ao_fat_offset_t         offset_t;
49 typedef ao_fat_sector_t         sector_t;
50 typedef ao_fat_cluster_t        cluster_t;
51 typedef ao_fat_dirent_t         dirent_t;
52 typedef ao_fat_cluster_offset_t cluster_offset_t;
53
54 /* Global FAT lock */
55 static uint8_t ao_fat_mutex;
56
57 /* Partition information, sector numbers */
58
59 static uint8_t  partition_type;
60 static sector_t partition_start, partition_end;
61
62 #define AO_FAT_BAD_CLUSTER              0xffffff7
63 #define AO_FAT_LAST_CLUSTER             0xfffffff
64 #define AO_FAT_IS_LAST_CLUSTER(c)               (((c) & 0xffffff8) == 0xffffff8)
65 #define AO_FAT_IS_LAST_CLUSTER16(c)     (((c) & 0xfff8) == 0xfff8)
66
67
68 #define SECTOR_SIZE     512
69 #define SECTOR_MASK     (SECTOR_SIZE - 1)
70 #define SECTOR_SHIFT    9
71
72 #define DIRENT_SIZE     32
73
74 /* File system parameters */
75 static uint8_t          sectors_per_cluster;
76 static uint32_t         bytes_per_cluster;
77 static sector_t         reserved_sector_count;
78 static uint8_t          number_fat;
79 static dirent_t         root_entries;
80 static sector_t         sectors_per_fat;
81 static cluster_t        number_cluster;
82 static sector_t         fat_start;
83 static sector_t         root_start;
84 static sector_t         data_start;
85 static cluster_t        next_free;
86 static uint8_t          filesystem_full;
87
88 /* FAT32 extra data */
89 static uint8_t          fat32;
90 static uint8_t          fsinfo_dirty;
91 static cluster_t        root_cluster;
92 static sector_t         fsinfo_sector;
93 static cluster_t        free_count;
94
95 /*
96  * Deal with LSB FAT data structures
97  */
98 static uint32_t
99 get_u32(uint8_t *base)
100 {
101         return ((uint32_t) base[0] |
102                 ((uint32_t) base[1] << 8) |
103                 ((uint32_t) base[2] << 16) |
104                 ((uint32_t) base[3] << 24));
105 }
106
107 static void
108 put_u32(uint8_t *base, uint32_t value)
109 {
110         base[0] = value;
111         base[1] = value >> 8;
112         base[2] = value >> 16;
113         base[3] = value >> 24;
114 }
115
116 static uint16_t
117 get_u16(uint8_t *base)
118 {
119         return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
120 }
121
122 static void
123 put_u16(uint8_t *base, uint16_t value)
124 {
125         base[0] = value;
126         base[1] = value >> 8;
127 }
128
129 static uint8_t
130 _ao_fat_cluster_valid(cluster_t cluster)
131 {
132         return (2 <= cluster && cluster < number_cluster);
133 }
134
135 /* Start using a sector */
136 static uint8_t *
137 _ao_fat_sector_get(sector_t sector)
138 {
139         sector += partition_start;
140         if (sector >= partition_end)
141                 return NULL;
142         return ao_bufio_get(sector);
143 }
144
145 /* Finish using a sector, 'w' is 1 if modified */
146 #define _ao_fat_sector_put(b,w) ao_bufio_put(b,w)
147
148 /* Get the next cluster entry in the chain */
149 static cluster_t
150 _ao_fat_entry_read(cluster_t cluster)
151 {
152         sector_t        sector;
153         cluster_t       offset;
154         uint8_t         *buf;
155         cluster_t       ret;
156
157         if (!_ao_fat_cluster_valid(cluster))
158                 return 0xfffffff7;
159
160         if (fat32)
161                 cluster <<= 2;
162         else
163                 cluster <<= 1;
164         sector = cluster >> (SECTOR_SHIFT);
165         offset = cluster & SECTOR_MASK;
166         buf = _ao_fat_sector_get(fat_start + sector);
167         if (!buf)
168                 return 0;
169
170         if (fat32) {
171                 ret = get_u32(buf + offset);
172                 ret &= 0xfffffff;
173         } else {
174                 ret = get_u16(buf + offset);
175                 if (AO_FAT_IS_LAST_CLUSTER16(ret))
176                         ret |= 0xfff0000;
177         }
178         _ao_fat_sector_put(buf, 0);
179         return ret;
180 }
181
182 /* Replace the referenced cluster entry in the chain with
183  * 'new_value'. Return the previous value.
184  */
185 static cluster_t
186 _ao_fat_entry_replace(cluster_t  cluster, cluster_t new_value)
187 {
188         sector_t                sector;
189         cluster_offset_t        offset;
190         uint8_t                 *buf;
191         cluster_t               ret = 0;
192         cluster_t               old_value;
193         uint8_t                 fat;
194
195         if (!_ao_fat_cluster_valid(cluster))
196                 return 0xfffffff7;
197
198         /* Convert from cluster index to byte index */
199         if (fat32)
200                 cluster <<= 2;
201         else
202                 cluster <<= 1;
203         sector = cluster >> SECTOR_SHIFT;
204         offset = cluster & SECTOR_MASK;
205
206         new_value &= 0xfffffff;
207         for (fat = 0; fat < number_fat; fat++) {
208                 buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
209                 if (!buf)
210                         return 0;
211                 if (fat32) {
212                         old_value = get_u32(buf + offset);
213                         put_u32(buf + offset, new_value | (old_value & 0xf0000000));
214                         if (fat == 0) {
215                                 ret = old_value & 0xfffffff;
216
217                                 /* Track the free count if it wasn't marked
218                                  * invalid when we mounted the file system
219                                  */
220                                 if (free_count != 0xffffffff) {
221                                         if (new_value && !ret) {
222                                                 --free_count;
223                                                 fsinfo_dirty = 1;
224                                         } else if (!new_value && ret) {
225                                                 ++free_count;
226                                                 fsinfo_dirty = 1;
227                                         }
228                                 }
229                         }
230                 } else {
231                         if (fat == 0) {
232                                 ret = get_u16(buf + offset);
233                                 if (AO_FAT_IS_LAST_CLUSTER16(ret))
234                                         ret |= 0xfff0000;
235                         }
236                         put_u16(buf + offset, new_value);
237                 }
238                 _ao_fat_sector_put(buf, 1);
239         }
240         return ret;
241         
242 }
243
244 /*
245  * Walk a cluster chain and mark
246  * all of them as free
247  */
248 static void
249 _ao_fat_free_cluster_chain(cluster_t cluster)
250 {
251         while (_ao_fat_cluster_valid(cluster)) {
252                 if (cluster < next_free) {
253                         next_free = cluster;
254                         fsinfo_dirty = 1;
255                 }
256                 cluster = _ao_fat_entry_replace(cluster, 0x00000000);
257         }
258 }
259
260 /*
261  * _ao_fat_cluster_seek
262  * 
263  * The basic file system operation -- map a file cluster index to a
264  * partition cluster number. Done by computing the cluster number and
265  * then walking that many clusters from the first cluster. Returns
266  * 0xffff if we walk off the end of the file or the cluster chain
267  * is damaged somehow
268  */
269 static cluster_t
270 _ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
271 {
272         while (distance) {
273                 cluster = _ao_fat_entry_read(cluster);
274                 if (!_ao_fat_cluster_valid(cluster))
275                         break;
276                 distance--;
277         }
278         return cluster;
279 }
280
281 /*
282  * _ao_fat_cluster_set_size
283  *
284  * Set the number of clusters in the specified chain,
285  * freeing extra ones or alocating new ones as needed
286  *
287  * Returns AO_FAT_BAD_CLUSTER on allocation failure
288  */
289
290 static cluster_t
291 _ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
292 {
293         cluster_t       have;
294         cluster_t       last_cluster;
295         cluster_t       next_cluster;
296
297         /* Walk the cluster chain to the
298          * spot where it needs to change. That
299          * will either be the end of the chain (in case it needs to grow),
300          * or after the desired number of clusters, in which case it needs to shrink
301          */
302         next_cluster = first_cluster;
303         last_cluster = 0;
304         DBG("\tclusters:");
305         for (have = 0; have < size; have++) {
306                 DBG(" %08x", next_cluster);
307                 if (!_ao_fat_cluster_valid(next_cluster))
308                         break;
309                 last_cluster = next_cluster;
310                 next_cluster = _ao_fat_entry_read(next_cluster);
311         }
312         DBG("\n");
313
314         /* At this point, last_cluster points to the last valid
315          * cluster in the file, if any. That's the spot in the FAT
316          * that needs to be rewritten, either to truncate the file by
317          * writing an END marker, or to extend the file by writing
318          * more clusters. next_cluster will contain the value of the
319          * FAT at last_cluster.
320          *
321          * If this is at the head of the cluster chain, then
322          * last_cluster will be zero and next_cluster will
323          * be the first cluster in the chain.
324          */
325         if (have == size) {
326                 /* The file is large enough, truncate as needed */
327                 if (_ao_fat_cluster_valid(next_cluster)) {
328                         DBG("truncate between %08x and %08x\n", last_cluster, next_cluster);
329                         if (last_cluster)
330                                 /*
331                                  * Otherwise, rewrite the last cluster
332                                  * in the chain with a LAST marker
333                                  */
334                                 (void) _ao_fat_entry_replace(last_cluster,
335                                                             AO_FAT_LAST_CLUSTER);
336                         else
337                                 /*
338                                  * If the file is getting erased, then
339                                  * rewrite the directory entry cluster
340                                  * value
341                                  */
342                                 first_cluster = 0;
343
344                         /* Clear the remaining clusters in the chain */
345                         _ao_fat_free_cluster_chain(next_cluster);
346
347                         /* The file system is no longer full (if it was) */
348                         filesystem_full = 0;
349                 } else {
350                         DBG("unchanged FAT chain\n");
351                         /* The chain is already the right length, don't mess with it */
352                         ;
353                 }
354         } else {
355                 cluster_t       need;
356                 cluster_t       free;
357
358                 if (filesystem_full)
359                         return AO_FAT_BAD_CLUSTER;
360
361                 /* Set next_free if it has wrapped or wasn't set before */
362                 if (next_free < 2 || number_cluster <= next_free) {
363                         next_free = 2;
364                         fsinfo_dirty = 1;
365                 }
366
367                 /* See if there are enough free clusters in the file system */
368                 need = size - have;
369
370 #define loop_cluster    for (free = next_free; need > 0;)
371 #define next_cluster                            \
372                 if (++free == number_cluster)   \
373                         free = 2;               \
374                 if (free == next_free)          \
375                         break;                  \
376
377                 loop_cluster {
378                         if (!_ao_fat_entry_read(free))
379                                 need--;
380                         next_cluster;
381                 }
382
383                 /* Still need some, tell the user that we've failed */
384                 if (need) {
385                         filesystem_full = 1;
386                         return AO_FAT_BAD_CLUSTER;
387                 }
388
389                 /* Now go allocate those clusters and
390                  * thread them onto the chain
391                  */
392                 need = size - have;
393                 loop_cluster {
394                         if (_ao_fat_entry_read(free) == 0) {
395                                 next_free = free + 1;
396                                 if (next_free >= number_cluster)
397                                         next_free = 2;
398                                 fsinfo_dirty = 1;
399                                 DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free);
400                                 if (last_cluster)
401                                         _ao_fat_entry_replace(last_cluster, free);
402                                 else
403                                         first_cluster = free;
404                                 last_cluster = free;
405                                 need--;
406                         }
407                         next_cluster;
408                 }
409 #undef loop_cluster
410 #undef next_cluster
411                 DBG("\tlast cluster %08x\n", last_cluster);
412                 /* Mark the new end of the chain */
413                 _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
414         }
415
416         DBG("\tfirst cluster %08x\n", first_cluster);
417         return first_cluster;
418 }
419
420 /* Start using a root directory entry */
421 static uint8_t *
422 _ao_fat_root_get(dirent_t e)
423 {
424         offset_t                byte = e * DIRENT_SIZE;
425         sector_t                sector = byte >> SECTOR_SHIFT;
426         cluster_offset_t        offset = byte & SECTOR_MASK;
427         uint8_t                 *buf;
428
429         if (fat32) {
430                 cluster_t       cluster_distance = sector / sectors_per_cluster;
431                 sector_t        sector_index = sector % sectors_per_cluster;
432                 cluster_t       cluster = _ao_fat_cluster_seek(root_cluster, cluster_distance);
433
434                 if (_ao_fat_cluster_valid(cluster))
435                         sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
436                 else
437                         return NULL;
438         } else {
439                 if (e >= root_entries)
440                         return NULL;
441                 sector = root_start + sector;
442         }
443
444         buf = _ao_fat_sector_get(sector);
445         if (!buf)
446                 return NULL;
447         return buf + offset;
448 }
449
450 /* Finish using a root directory entry, 'w' is 1 if modified */
451 static void
452 _ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
453 {
454         cluster_offset_t        offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
455         uint8_t                 *buf = root - offset;
456
457         _ao_fat_sector_put(buf, write);
458 }
459
460 /*
461  * _ao_fat_root_extend
462  *
463  * On FAT32, make the root directory at least 'ents' entries long
464  */
465 static int8_t
466 _ao_fat_root_extend(dirent_t ents)
467 {
468         offset_t        byte_size;
469         cluster_t       cluster_size;
470         if (!fat32)
471                 return 0;
472         
473         byte_size = (ents + 1) * 0x20;
474         cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster;
475         if (_ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
476                 return 1;
477         return 0;
478 }
479                 
480 /*
481  * _ao_fat_setup_partition
482  * 
483  * Load the boot block and find the first partition
484  */
485 static uint8_t
486 _ao_fat_setup_partition(void)
487 {
488         uint8_t *mbr;
489         uint8_t *partition;
490         uint32_t partition_size;
491
492         mbr = ao_bufio_get(0);
493         if (!mbr)
494                 return AO_FAT_FILESYSTEM_MBR_READ_FAILURE;
495
496         /* Check the signature */
497         if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
498                 DBG ("Invalid MBR signature %02x %02x\n",
499                         mbr[0x1fe], mbr[0x1ff]);
500                 ao_bufio_put(mbr, 0);
501                 return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE;
502         }
503
504         /* Check to see if it's actually a boot block, in which
505          * case it's presumably not a paritioned device
506          */
507
508         if (mbr[0] == 0xeb) {
509                 partition_start = 0;
510                 partition_size = get_u16(mbr + 0x13);
511                 if (partition_size == 0)
512                         partition_size = get_u32(mbr + 0x20);
513         } else {
514                 /* Just use the first partition */
515                 partition = &mbr[0x1be];
516         
517                 partition_type = partition[4];
518                 switch (partition_type) {
519                 case 4:         /* FAT16 up to 32M */
520                 case 6:         /* FAT16 over 32M */
521                         break;
522                 case 0x0b:      /* FAT32 up to 2047GB */
523                 case 0x0c:      /* FAT32 LBA */
524                         break;
525                 default:
526                         DBG ("Invalid partition type %02x\n", partition_type);
527                         ao_bufio_put(mbr, 0);
528                         return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE;
529                 }
530
531                 partition_start = get_u32(partition+8);
532                 partition_size = get_u32(partition+12);
533                 if (partition_size == 0) {
534                         DBG ("Zero-sized partition\n");
535                         ao_bufio_put(mbr, 0);
536                         return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION;
537                 }
538         }
539         partition_end = partition_start + partition_size;
540         ao_bufio_put(mbr, 0);
541         return AO_FAT_FILESYSTEM_SUCCESS;
542 }
543         
544 static uint8_t
545 _ao_fat_setup_fs(void)
546 {
547         uint8_t         *boot = _ao_fat_sector_get(0);
548         uint32_t        data_sectors;
549
550         if (!boot)
551                 return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE;
552
553         /* Check the signature */
554         if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
555                 DBG ("Invalid BOOT signature %02x %02x\n",
556                         boot[0x1fe], boot[0x1ff]);
557                 _ao_fat_sector_put(boot, 0);
558                 return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE;
559         }
560
561         /* Check the sector size */
562         if (get_u16(boot + 0xb) != SECTOR_SIZE) {
563                 DBG ("Invalid sector size %d\n",
564                         get_u16(boot + 0xb));
565                 _ao_fat_sector_put(boot, 0);
566                 return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE;
567         }
568
569         sectors_per_cluster = boot[0xd];
570         bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
571         reserved_sector_count = get_u16(boot+0xe);
572         number_fat = boot[0x10];
573         root_entries = get_u16(boot + 0x11);
574         sectors_per_fat = get_u16(boot+0x16);
575         fat32 = 0;
576         if (sectors_per_fat == 0) {
577                 fat32 = 1;
578                 sectors_per_fat = get_u32(boot+0x24);
579                 root_cluster = get_u32(boot+0x2c);
580                 fsinfo_sector = get_u16(boot + 0x30);
581         }
582         _ao_fat_sector_put(boot, 0);
583
584         free_count = 0xffffffff;
585         next_free = 0;
586         if (fat32 && fsinfo_sector) {
587                 uint8_t *fsinfo = _ao_fat_sector_get(fsinfo_sector);
588
589                 if (fsinfo) {
590                         free_count = get_u32(fsinfo + 0x1e8);
591                         next_free = get_u32(fsinfo + 0x1ec);
592                         _ao_fat_sector_put(fsinfo, 0);
593                 }
594         }
595
596         fat_start = reserved_sector_count;;
597         root_start = fat_start + number_fat * sectors_per_fat;
598         data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
599
600         data_sectors = (partition_end - partition_start) - data_start;
601
602         number_cluster = data_sectors / sectors_per_cluster;
603
604         return AO_FAT_FILESYSTEM_SUCCESS;
605 }
606
607 /*
608  * State for an open file
609  */
610
611 struct ao_file {
612         struct ao_fat_dirent    *dirent;
613         offset_t                offset;
614         offset_t                cluster_offset;
615         cluster_t               cluster;
616         uint8_t                 busy;
617 };
618
619 #define AO_FAT_NFILE    8
620
621 static struct ao_fat_dirent     ao_file_dirent[AO_FAT_NFILE];
622
623 static struct ao_fat_dirent *
624 _ao_fat_file_dirent_alloc(struct ao_fat_dirent *want)
625 {
626         int8_t  d;
627         struct ao_fat_dirent *free = NULL, *dirent;
628
629         for (d = 0; d < AO_FAT_NFILE; d++) {
630
631                 dirent = &ao_file_dirent[d];
632                 /* See if there's another user of this file already */
633                 if (want && dirent->name[0] != 0) {
634                         if (dirent->entry == want->entry)
635                                 return dirent;
636                 } else {
637                         if (!free) {
638                                 free = dirent;
639                                 if (!want)
640                                         break;
641                         }
642                 }
643         }
644         if (free && want)
645                 *free = *want;
646         return free;
647 }
648
649 static struct ao_file           ao_file_table[AO_FAT_NFILE];
650
651 static int8_t
652 _ao_fat_fd_alloc(struct ao_fat_dirent *dirent)
653 {
654         int8_t  fd;
655
656         for (fd = 0; fd < AO_FAT_NFILE; fd++)
657                 if (!ao_file_table[fd].busy) {
658                         ao_file_table[fd].dirent = _ao_fat_file_dirent_alloc(dirent);
659                         ao_file_table[fd].busy = 1;
660                         ao_file_table[fd].offset = 0;
661                         ao_file_table[fd].cluster_offset = 0;
662                         ao_file_table[fd].cluster = ao_file_table[fd].dirent->cluster;
663                                 
664                         return fd;
665                 }
666         return -AO_FAT_EMFILE;
667 }
668
669 static void
670 _ao_fat_fd_free(int8_t fd)
671 {
672         struct ao_file *file = &ao_file_table[fd];
673         struct ao_fat_dirent *dirent = file->dirent;
674         memset(&ao_file_table[fd], '\0', sizeof (struct ao_file));
675
676         /* Check and see if another ao_file references the same dirent */
677         for (fd = 0; fd < AO_FAT_NFILE; fd++)
678                 if (ao_file_table[fd].dirent == dirent)
679                         return;
680         memset(dirent, '\0', sizeof (struct ao_fat_dirent));
681 }
682
683 static struct ao_file *
684 _ao_fat_fd_to_file(int8_t fd)
685 {
686         struct ao_file *file;
687         if (fd < 0 || AO_FAT_NFILE <= fd)
688                 return NULL;
689
690         file = &ao_file_table[fd];
691         if (!file->busy)
692                 return NULL;
693         return file;
694 }
695
696 static uint8_t                  ao_filesystem_setup;
697 static uint8_t                  ao_filesystem_status;
698
699 static uint8_t
700 _ao_fat_setup(void)
701 {
702         if (!ao_filesystem_setup) {
703
704                 ao_filesystem_setup = 1;
705                 ao_bufio_setup();
706         
707                 /* Re-initialize all global state; this will help to allow the
708                  * file system to get swapped someday
709                  */
710                 partition_type = partition_start = partition_end = 0;
711                 sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
712                 number_fat = root_entries = sectors_per_fat = 0;
713                 number_cluster = fat_start = root_start = data_start = 0;
714                 next_free = filesystem_full = 0;
715                 fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
716
717                 /* Reset open file table */
718                 memset(&ao_file_table, '\0', sizeof (ao_file_table));
719
720                 ao_filesystem_status = _ao_fat_setup_partition();
721                 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
722                         return ao_filesystem_status;
723                 ao_filesystem_status = _ao_fat_setup_fs();
724                 if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
725                         return ao_filesystem_status;
726         }
727         return ao_filesystem_status;
728 }
729
730 void
731 ao_fat_unmount(void)
732 {
733         ao_filesystem_setup = 0;
734 }
735
736 /*
737  * Basic file operations
738  */
739
740 static uint32_t
741 _ao_fat_current_sector(struct ao_file *file)
742 {
743         cluster_t       cluster_offset;
744         uint32_t        sector_offset;
745         uint16_t        sector_index;
746         cluster_t       cluster;
747
748         DBG("current sector offset %d size %d\n",
749             file->offset, file->dirent->size);
750
751         if (file->offset > (offset_t) file->dirent->size) {
752                 printf ("file offset %d larger than size %d\n",
753                         file->offset, file->dirent->size);
754                 return 0xffffffff;
755         }
756
757         sector_offset = file->offset >> SECTOR_SHIFT;
758
759         if (!file->cluster || file->offset < file->cluster_offset) {
760                 file->cluster = file->dirent->cluster;
761                 file->cluster_offset = 0;
762                 DBG("\treset to start of file %08x\n", file->cluster);
763         }
764
765         if ((offset_t) (file->cluster_offset + bytes_per_cluster) <= file->offset) {
766                 cluster_t       cluster_distance;
767
768                 cluster_offset = sector_offset / sectors_per_cluster;
769
770                 cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster;
771
772                 DBG("\tseek forward %d clusters\n", cluster_distance);
773                 cluster = _ao_fat_cluster_seek(file->cluster, cluster_distance);
774
775                 if (!_ao_fat_cluster_valid(cluster)) {
776                         printf ("invalid cluster %08x\n", cluster);
777                         return 0xffffffff;
778                 }
779                 file->cluster = cluster;
780                 file->cluster_offset = cluster_offset * bytes_per_cluster;
781         }
782
783         sector_index = sector_offset % sectors_per_cluster;
784         DBG("current cluster %08x sector_index %d sector %d\n",
785             file->cluster, sector_index,
786             data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index);
787         return data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index;
788 }
789
790 /*
791  * _ao_fat_invaldate_cluster_offset
792  *
793  * When the file size gets shrunk, invalidate
794  * any file structures referencing clusters beyond that point
795  */
796
797 static void
798 _ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent)
799 {
800         int8_t          fd;
801         struct ao_file  *file;
802
803         for (fd = 0; fd < AO_FAT_NFILE; fd++) {
804                 file = &ao_file_table[fd];
805                 if (!file->busy)
806                         continue;
807                 if (file->dirent == dirent) {
808                         if (file->cluster_offset >= (offset_t) dirent->size) {
809                                 file->cluster_offset = 0;
810                                 file->cluster = dirent->cluster;
811                         }
812                 }
813         }
814 }
815
816  
817 /*
818  * _ao_fat_set_size
819  *
820  * Set the size of the current file, truncating or extending
821  * the cluster chain as needed
822  */
823 static int8_t
824 _ao_fat_set_size(struct ao_file *file, uint32_t size)
825 {
826         uint8_t         *dent;
827         cluster_t       first_cluster;
828         cluster_t       have_clusters, need_clusters;
829
830         DBG ("Set size %d\n", size);
831         if (size == file->dirent->size) {
832                 DBG("\tsize match\n");
833                 return AO_FAT_SUCCESS;
834         }
835
836         first_cluster = file->dirent->cluster;
837         have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster;
838         need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
839
840         DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
841         if (have_clusters != need_clusters) {
842                 if (file->cluster && (offset_t) size > file->cluster_offset) {
843                         cluster_t       offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster;
844                         cluster_t       extra_clusters = need_clusters - offset_clusters;
845                         cluster_t       next_cluster;
846
847                         DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
848                              offset_clusters, extra_clusters);
849
850                         /* Need one more to account for file->cluster, which we already have */
851                         next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1);
852                         if (next_cluster == AO_FAT_BAD_CLUSTER)
853                                 return -AO_FAT_ENOSPC;
854                 } else {
855                         DBG ("\tset size absolute need_clusters %d\n", need_clusters);
856                         first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters);
857
858                         if (first_cluster == AO_FAT_BAD_CLUSTER)
859                                 return -AO_FAT_ENOSPC;
860                 }
861         }
862
863         DBG ("\tupdate directory size\n");
864         /* Update the directory entry */
865         dent = _ao_fat_root_get(file->dirent->entry);
866         if (!dent) {
867                 printf ("dent update failed\n");
868                 return -AO_FAT_EIO;
869         }
870         put_u32(dent + 0x1c, size);
871         put_u16(dent + 0x1a, first_cluster);
872         if (fat32)
873                 put_u16(dent + 0x14, first_cluster >> 16);
874         _ao_fat_root_put(dent, file->dirent->entry, 1);
875
876         file->dirent->size = size;
877         file->dirent->cluster = first_cluster;
878         if (have_clusters > need_clusters)
879                 _ao_fat_invalidate_cluster_offset(file->dirent);
880         DBG ("set size done\n");
881         return AO_FAT_SUCCESS;
882 }
883
884 /*
885  * _ao_fat_root_init
886  *
887  * Initialize a root directory entry
888  */
889 static void
890 _ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
891 {
892         (void) attr;
893         memset(dent, '\0', 0x20);
894         memmove(dent, name, 11);
895
896         dent[0x0b] = 0x00;
897         dent[0x0c] = 0x00;
898         dent[0x0d] = 0x00;
899
900         /* XXX fix time */
901         put_u16(dent + 0x0e, 0);
902         /* XXX fix date */
903         put_u16(dent + 0x10, 0);
904         /* XXX fix date */
905         put_u16(dent + 0x12, 0);
906
907         /* XXX fix time */
908         put_u16(dent + 0x16, 0);
909         /* XXX fix date */
910         put_u16(dent + 0x18, 0);
911
912         /* cluster number */
913         /* Low cluster bytes */
914         put_u16(dent + 0x1a, 0);
915         /* FAT32 high cluster bytes */
916         put_u16(dent + 0x14, 0);
917
918         /* size */
919         put_u32(dent + 0x1c, 0);
920 }
921
922
923 static void
924 _ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry)
925 {
926         memcpy(dirent->name, dent + 0x00, 11);
927         dirent->attr = dent[0x0b];
928         dirent->size = get_u32(dent+0x1c);
929         dirent->cluster = get_u16(dent+0x1a);
930         if (fat32)
931                 dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
932         dirent->entry = entry;
933 }
934
935 /*
936  * _ao_fat_flush_fsinfo
937  *
938  * Write out any fsinfo changes to disk
939  */
940
941 static void
942 _ao_fat_flush_fsinfo(void)
943 {
944         uint8_t *fsinfo;
945
946         if (!fat32)
947                 return;
948
949         if (!fsinfo_dirty)
950                 return;
951         fsinfo_dirty = 0;
952         if (!fsinfo_sector)
953                 return;
954
955         fsinfo = _ao_fat_sector_get(fsinfo_sector);
956         if (fsinfo) {
957                 put_u32(fsinfo + 0x1e8, free_count);
958                 put_u32(fsinfo + 0x1ec, next_free);
959                 _ao_fat_sector_put(fsinfo, 1);
960         }
961 }
962
963 /*
964  * Public API
965  */
966
967 /*
968  * ao_fat_sync
969  *
970  * Flush any pending I/O to storage
971  */
972
973 static void
974 _ao_fat_sync(void)
975 {
976         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
977                 return;
978         _ao_fat_flush_fsinfo();
979         ao_bufio_flush();
980 }
981
982 void
983 ao_fat_sync(void)
984 {
985         ao_mutex_get(&ao_fat_mutex);
986         _ao_fat_sync();
987         ao_mutex_put(&ao_fat_mutex);
988 }
989
990 /*
991  * ao_fat_full
992  *
993  * Returns true if the filesystem cannot take
994  * more data
995  */
996
997 int8_t
998 ao_fat_full(void)
999 {
1000         ao_mutex_get(&ao_fat_mutex);
1001         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
1002                 ao_mutex_put(&ao_fat_mutex);
1003                 return 1;
1004         }
1005         ao_mutex_put(&ao_fat_mutex);
1006         return filesystem_full;
1007 }
1008
1009 static int8_t
1010 _ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1011 {
1012         uint8_t *dent;
1013
1014         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1015                 return -AO_FAT_EIO;
1016
1017         for (;;) {
1018                 dent = _ao_fat_root_get(*entry);
1019                 if (!dent)
1020                         return -AO_FAT_EDIREOF;
1021
1022                 if (dent[0] == AO_FAT_DENT_END) {
1023                         _ao_fat_root_put(dent, *entry, 0);
1024                         return -AO_FAT_EDIREOF;
1025                 }
1026                 if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
1027                         _ao_fat_dirent_init(dirent, dent, *entry);
1028                         _ao_fat_root_put(dent, *entry, 0);
1029                         (*entry)++;
1030                         return AO_FAT_SUCCESS;
1031                 }
1032                 _ao_fat_root_put(dent, *entry, 0);
1033                 (*entry)++;
1034         }
1035 }
1036
1037 int8_t
1038 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
1039 {
1040         int8_t status;
1041
1042         ao_mutex_get(&ao_fat_mutex);
1043         status = _ao_fat_readdir(entry, dirent);
1044         ao_mutex_put(&ao_fat_mutex);
1045         return status;
1046 }
1047
1048 /*
1049  * ao_fat_open
1050  *
1051  * Open an existing file.
1052  */
1053 static int8_t
1054 _ao_fat_open(char name[11], uint8_t mode)
1055 {
1056         uint16_t                entry = 0;
1057         static struct ao_fat_dirent     dirent;
1058         int8_t                  status;
1059
1060         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1061                 return -AO_FAT_EIO;
1062
1063         for (;;) {
1064                 status = _ao_fat_readdir(&entry, &dirent);
1065                 if (status < 0) {
1066                         if (status == -AO_FAT_EDIREOF)
1067                                 return -AO_FAT_ENOENT;
1068                         return status;
1069                 }
1070                 if (!memcmp(name, dirent.name, 11)) {
1071                         if (AO_FAT_IS_DIR(dirent.attr))
1072                                 return -AO_FAT_EISDIR;
1073                         if (!AO_FAT_IS_FILE(dirent.attr))
1074                                 return -AO_FAT_EPERM;
1075                         if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
1076                                 return -AO_FAT_EACCESS;
1077                         return _ao_fat_fd_alloc(&dirent);
1078                 }
1079         }
1080         return -AO_FAT_ENOENT;
1081 }
1082
1083 int8_t
1084 ao_fat_open(char name[11], uint8_t mode)
1085 {
1086         int8_t status;
1087
1088         ao_mutex_get(&ao_fat_mutex);
1089         status = _ao_fat_open(name, mode);
1090         ao_mutex_put(&ao_fat_mutex);
1091         return status;
1092 }
1093
1094 /*
1095  * ao_fat_close
1096  *
1097  * Close the currently open file
1098  */
1099 static int8_t
1100 _ao_fat_close(int8_t fd)
1101 {
1102         struct ao_file *file;
1103
1104         file = _ao_fat_fd_to_file(fd);
1105         if (!file)
1106                 return -AO_FAT_EBADF;
1107
1108         _ao_fat_fd_free(fd);
1109         _ao_fat_sync();
1110         return AO_FAT_SUCCESS;
1111 }
1112
1113 int8_t
1114 ao_fat_close(int8_t fd)
1115 {
1116         int8_t status;
1117
1118         ao_mutex_get(&ao_fat_mutex);
1119         status = _ao_fat_close(fd);
1120         ao_mutex_put(&ao_fat_mutex);
1121         return status;
1122 }
1123
1124 /*
1125  * ao_fat_creat
1126  *
1127  * Open and truncate an existing file or
1128  * create a new file
1129  */
1130
1131 static int8_t
1132 _ao_fat_creat(char name[11])
1133 {
1134         uint16_t                entry;
1135         int8_t                  fd;
1136         int8_t                  status;
1137         uint8_t                 *dent;
1138         struct ao_file          *file;
1139
1140         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
1141                 return -AO_FAT_EIO;
1142
1143         fd = _ao_fat_open(name, AO_FAT_OPEN_WRITE);
1144         if (fd >= 0) {
1145                 file = &ao_file_table[fd];
1146                 status = _ao_fat_set_size(file, 0);
1147                 if (status < 0) {
1148                         _ao_fat_close(fd);
1149                         fd = status;
1150                 }
1151         } else {
1152                 if (fd == -AO_FAT_ENOENT) {
1153                         entry = 0;
1154                         for (;;) {
1155                                 dent = _ao_fat_root_get(entry);
1156                                 if (!dent) {
1157                                 
1158                                         if (_ao_fat_root_extend(entry))
1159                                                 continue;
1160                                         fd = -AO_FAT_ENOSPC;
1161                                         break;
1162                                 }
1163                                 if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
1164                                         fd = _ao_fat_fd_alloc(NULL);
1165                                         if (fd < 0) {
1166                                                 _ao_fat_root_put(dent, entry, 0);
1167                                                 break;
1168                                         }
1169
1170                                         file = &ao_file_table[fd];
1171                                         /* Initialize the dent */
1172                                         _ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
1173
1174                                         /* Now initialize the dirent from the dent */
1175                                         _ao_fat_dirent_init(file->dirent, dent, entry);
1176
1177                                         /* And write the dent to storage */
1178                                         _ao_fat_root_put(dent, entry, 1);
1179
1180                                         status = -AO_FAT_SUCCESS;
1181                                         break;
1182                                 } else {
1183                                         _ao_fat_root_put(dent, entry, 0);
1184                                 }
1185                                 entry++;
1186                         }
1187                 }
1188         }
1189         return fd;
1190 }
1191
1192 int8_t
1193 ao_fat_creat(char name[11])
1194 {
1195         int8_t  status;
1196
1197         ao_mutex_get(&ao_fat_mutex);
1198         status = _ao_fat_creat(name);
1199         ao_mutex_put(&ao_fat_mutex);
1200         return status;
1201 }
1202
1203 /*
1204  * ao_fat_map_current
1205  *
1206  * Map the sector pointed at by the current file offset
1207  */
1208
1209 static void *
1210 ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, cluster_offset_t *this_time)
1211 {
1212         cluster_offset_t        offset;
1213         sector_t                sector;
1214         void                    *buf;
1215
1216         offset = file->offset & SECTOR_MASK;
1217         sector = _ao_fat_current_sector(file);
1218         if (sector == 0xffffffff) {
1219                 return NULL;
1220         }
1221         buf = _ao_fat_sector_get(sector);
1222         if (!buf)
1223                 return NULL;
1224         if (offset + len < SECTOR_SIZE)
1225                 *this_time = len;
1226         else
1227                 *this_time = SECTOR_SIZE - offset;
1228         *offsetp = offset;
1229         return buf;
1230 }
1231
1232 /*
1233  * ao_fat_read
1234  *
1235  * Read from the file
1236  */
1237 int
1238 ao_fat_read(int8_t fd, void *dst, int len)
1239 {
1240         uint8_t                 *dst_b = dst;
1241         cluster_offset_t        this_time;
1242         cluster_offset_t        offset;
1243         uint8_t                 *buf;
1244         int                     ret = 0;
1245         struct ao_file          *file;
1246
1247         ao_mutex_get(&ao_fat_mutex);
1248         file = _ao_fat_fd_to_file(fd);
1249         if (!file) {
1250                 ret = -AO_FAT_EBADF;
1251                 goto done;
1252         }
1253
1254         if (file->offset + len > (offset_t) file->dirent->size)
1255                 len = file->dirent->size - file->offset;
1256
1257         if (len < 0)
1258                 len = 0;
1259
1260         while (len) {
1261                 buf = ao_fat_map_current(file, len, &offset, &this_time);
1262                 if (!buf) {
1263                         ret = -AO_FAT_EIO;
1264                         break;
1265                 }
1266                 memcpy(dst_b, buf + offset, this_time);
1267                 _ao_fat_sector_put(buf, 0);
1268
1269                 ret += this_time;
1270                 len -= this_time;
1271                 dst_b += this_time;
1272                 file->offset = file->offset + this_time;
1273         }
1274 done:
1275         ao_mutex_put(&ao_fat_mutex);
1276         return ret;
1277 }
1278
1279 /*
1280  * ao_fat_write
1281  *
1282  * Write to the file, extended as necessary
1283  */
1284 int
1285 ao_fat_write(int8_t fd, void *src, int len)
1286 {
1287         uint8_t                 *src_b = src;
1288         cluster_offset_t        this_time;
1289         cluster_offset_t        offset;
1290         uint8_t                 *buf;
1291         int                     ret = 0;
1292         struct ao_file          *file;
1293
1294         ao_mutex_get(&ao_fat_mutex);
1295         file = _ao_fat_fd_to_file(fd);
1296         if (!file) {
1297                 ret = -AO_FAT_EBADF;
1298                 goto done;
1299         }
1300
1301         if (file->offset + len > (offset_t) file->dirent->size) {
1302                 ret = _ao_fat_set_size(file, file->offset + len);
1303                 if (ret < 0)
1304                         goto done;
1305         }
1306
1307         while (len) {
1308                 buf = ao_fat_map_current(file, len, &offset, &this_time);
1309                 if (!buf) {
1310                         ret = -AO_FAT_EIO;
1311                         break;
1312                 }
1313                 memcpy(buf + offset, src_b, this_time);
1314                 _ao_fat_sector_put(buf, 1);
1315
1316                 ret += this_time;
1317                 len -= this_time;
1318                 src_b += this_time;
1319                 file->offset = file->offset + this_time;
1320         }
1321 done:
1322         ao_mutex_put(&ao_fat_mutex);
1323         return ret;
1324 }
1325
1326 /*
1327  * ao_fat_seek
1328  *
1329  * Set the position for the next I/O operation
1330  * Note that this doesn't actually change the size
1331  * of the file if the requested position is beyond
1332  * the current file length, that would take a future
1333  * write
1334  */
1335 int32_t
1336 ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence)
1337 {
1338         offset_t        new_offset;
1339         struct ao_file  *file;
1340         int32_t         ret;
1341
1342         ao_mutex_get(&ao_fat_mutex);
1343         file = _ao_fat_fd_to_file(fd);
1344         if (!file) {
1345                 ret = -AO_FAT_EBADF;
1346                 goto done;
1347         }
1348
1349         new_offset = file->offset;
1350         switch (whence) {
1351         case AO_FAT_SEEK_SET:
1352                 new_offset = pos;
1353                 break;
1354         case AO_FAT_SEEK_CUR:
1355                 new_offset += pos;
1356                 break;
1357         case AO_FAT_SEEK_END:
1358                 new_offset = file->dirent->size + pos;
1359                 break;
1360         }
1361         ret = file->offset = new_offset;
1362 done:
1363         ao_mutex_put(&ao_fat_mutex);
1364         return ret;
1365 }
1366
1367 /*
1368  * ao_fat_unlink
1369  *
1370  * Remove a file from the directory, marking
1371  * all clusters as free
1372  */
1373 int8_t
1374 ao_fat_unlink(char name[11])
1375 {
1376         uint16_t                entry = 0;
1377         static struct ao_fat_dirent     dirent;
1378         int8_t                  ret;
1379
1380         ao_mutex_get(&ao_fat_mutex);
1381         if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
1382                 ret = -AO_FAT_EIO;
1383                 goto done;
1384         }
1385
1386         while (ao_fat_readdir(&entry, &dirent)) {
1387                 if (memcmp(name, dirent.name, 11) == 0) {
1388                         uint8_t *next;
1389                         uint8_t *ent;
1390                         uint8_t delete;
1391
1392                         if (AO_FAT_IS_DIR(dirent.attr)) {
1393                                 ret = -AO_FAT_EISDIR;
1394                                 goto done;
1395                         }
1396                         if (!AO_FAT_IS_FILE(dirent.attr)) {
1397                                 ret = -AO_FAT_EPERM;
1398                                 goto done;
1399                         }
1400
1401                         _ao_fat_free_cluster_chain(dirent.cluster);
1402                         next = _ao_fat_root_get(dirent.entry + 1);
1403                         if (next && next[0] != AO_FAT_DENT_END)
1404                                 delete = AO_FAT_DENT_EMPTY;
1405                         else
1406                                 delete = AO_FAT_DENT_END;
1407                         if (next)
1408                                 _ao_fat_root_put(next, dirent.entry + 1, 0);
1409                         ent = _ao_fat_root_get(dirent.entry);
1410                         if (ent) {
1411                                 memset(ent, '\0', DIRENT_SIZE);
1412                                 *ent = delete;
1413                                 _ao_fat_root_put(ent, dirent.entry, 1);
1414                         }
1415                         ao_bufio_flush();
1416                         ret = AO_FAT_SUCCESS;
1417                         goto done;
1418                 }
1419         }
1420         ret = -AO_FAT_ENOENT;
1421 done:
1422         ao_mutex_put(&ao_fat_mutex);
1423         return ret;
1424 }
1425
1426 int8_t
1427 ao_fat_rename(char old[11], char new[11])
1428 {
1429         (void) old;
1430         (void) new;
1431         return -AO_FAT_EIO;
1432 }
1433
1434 #if FAT_COMMANDS
1435
1436 static const char *filesystem_errors[] = {
1437         [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally",
1438         [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error",
1439         [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid",
1440         [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type",
1441         [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors",
1442         [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error",
1443         [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid",
1444         [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512",
1445 };
1446         
1447 static void
1448 ao_fat_mbr_cmd(void)
1449 {
1450         uint8_t status;
1451
1452         ao_mutex_get(&ao_fat_mutex);
1453         status = _ao_fat_setup();
1454         if (status == AO_FAT_FILESYSTEM_SUCCESS) {
1455                 printf ("partition type: %02x\n", partition_type);
1456                 printf ("partition start: %08x\n", partition_start);
1457
1458                 printf ("partition end:   %08x\n", partition_end);
1459
1460                 printf ("fat32: %d\n", fat32);
1461                 printf ("sectors per cluster %d\n", sectors_per_cluster);
1462                 printf ("reserved sectors %d\n", reserved_sector_count);
1463                 printf ("number of FATs %d\n", number_fat);
1464                 printf ("root entries %d\n", root_entries);
1465                 printf ("sectors per fat %d\n", sectors_per_fat);
1466
1467                 printf ("fat  start %d\n", fat_start);
1468                 printf ("root start %d\n", root_start);
1469                 printf ("data start %d\n", data_start);
1470         } else {
1471                 printf ("FAT filesystem not available: %s\n", filesystem_errors[status]);
1472         }
1473         ao_mutex_put(&ao_fat_mutex);
1474 }
1475
1476 struct ao_fat_attr {
1477         uint8_t bit;
1478         char    label;
1479 };
1480
1481 static const struct ao_fat_attr ao_fat_attr[] = {
1482         { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' },
1483         { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' },
1484         { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' },
1485         { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' },
1486         { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' },
1487         { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' },
1488 };
1489
1490 #define NUM_FAT_ATTR    (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0]))
1491
1492 static void
1493 ao_fat_list_cmd(void)
1494 {
1495         uint16_t                entry = 0;
1496         static struct ao_fat_dirent     dirent;
1497         int                     i;
1498         int8_t                  status;
1499
1500         while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) {
1501                 for (i = 0; i < 8; i++)
1502                         putchar(dirent.name[i]);
1503                 putchar('.');
1504                 for (; i < 11; i++)
1505                         putchar(dirent.name[i]);
1506                 for (i = 0; i < (int) NUM_FAT_ATTR; i++)
1507                         putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' ');
1508                 printf (" @%08x %d\n", dirent.cluster, dirent.size);
1509         }
1510         if (status != -AO_FAT_EDIREOF)
1511                 printf ("readdir failed: %d\n", status);
1512 }
1513
1514 static void
1515 ao_fat_parse_name(char name[11])
1516 {
1517         uint8_t c;
1518
1519         name[0] = '\0';
1520         ao_cmd_white();
1521         c = 0;
1522         while (ao_cmd_lex_c != '\n') {
1523                 if (ao_cmd_lex_c == '.') {
1524                         for (; c < 8; c++)
1525                                 name[c] = ' ';
1526                 } else {
1527                         if (c < 11)
1528                                 name[c++] = ao_cmd_lex_c;
1529                 }
1530                 ao_cmd_lex();
1531         }
1532         while (c < 11)
1533                 name[c++] = ' ';
1534 }
1535
1536 static void
1537 ao_fat_dump_cmd(void)
1538 {
1539         static char     name[11];
1540         int8_t          fd;
1541         int             cnt, i;
1542         static char     buf[32];
1543
1544         ao_fat_parse_name(name);
1545         if (name[0] == '\0') {
1546                 ao_cmd_status = ao_cmd_syntax_error;
1547                 return;
1548         }
1549                 
1550         fd = ao_fat_open(name, AO_FAT_OPEN_READ);
1551         if (fd < 0) {
1552                 printf ("Open failed: %d\n", fd);
1553                 return;
1554         }
1555         while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
1556                 for (i = 0; i < cnt; i++)
1557                         putchar(buf[i]);
1558         }
1559         ao_fat_close(fd);
1560 }
1561
1562 static void
1563 ao_fat_write_cmd(void)
1564 {
1565         static char     name[11];
1566         int8_t          fd;
1567         char            c;
1568         int             status;
1569
1570         ao_fat_parse_name(name);
1571         if (name[0] == '\0') {
1572                 ao_cmd_status = ao_cmd_syntax_error;
1573                 return;
1574         }
1575                 
1576         fd = ao_fat_creat(name);
1577         if (fd < 0) {
1578                 printf ("Open failed: %d\n", fd);
1579                 return;
1580         }
1581         flush();
1582         while ((c = getchar()) != 4) {
1583                 if (c == '\r') c = '\n';
1584                 if (ao_echo()) {
1585                         if (c == '\n') putchar ('\r');
1586                         putchar(c); flush();
1587                 }
1588                 status = ao_fat_write(fd, &c, 1);
1589                 if (status != 1) {
1590                         printf ("Write failure %d\n", status);
1591                         break;
1592                 }
1593         }
1594         ao_fat_close(fd);
1595 }
1596
1597 static void
1598 put32(uint32_t a)
1599 {
1600         ao_cmd_put16(a >> 16);
1601         ao_cmd_put16(a);
1602 }
1603
1604 static void
1605 ao_fat_hexdump_cmd(void)
1606 {
1607         char            name[11];
1608         int8_t          fd;
1609         int             cnt, i;
1610         char            buf[8];
1611         uint32_t        addr;
1612
1613         ao_fat_parse_name(name);
1614         if (name[0] == '\0') {
1615                 ao_cmd_status = ao_cmd_syntax_error;
1616                 return;
1617         }
1618
1619         fd = ao_fat_open(name, AO_FAT_OPEN_READ);
1620         if (fd < 0) {
1621                 printf ("Open failed: %d\n", fd);
1622                 return;
1623         }
1624         addr = 0;
1625         while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
1626                 put32(addr);
1627                 for (i = 0; i < cnt; i++) {
1628                         putchar(' ');
1629                         ao_cmd_put8(buf[i]);
1630                 }
1631                 putchar('\n');
1632                 addr += cnt;
1633         }
1634         ao_fat_close(fd);
1635 }
1636
1637 static const struct ao_cmds ao_fat_cmds[] = {
1638         { ao_fat_mbr_cmd,       "M\0Show FAT MBR and other info" },
1639         { ao_fat_list_cmd,      "F\0List FAT directory" },
1640         { ao_fat_dump_cmd,      "D <name>\0Dump FAT file" },
1641         { ao_fat_write_cmd,     "W <name>\0Write FAT file (end with ^D)" },
1642         { ao_fat_hexdump_cmd,   "H <name>\0HEX dump FAT file" },
1643         { 0, NULL },
1644 };
1645
1646 #endif
1647
1648 void
1649 ao_fat_init(void)
1650 {
1651         ao_bufio_init();
1652 #if FAT_COMMANDS
1653         ao_cmd_register(&ao_fat_cmds[0]);
1654 #endif
1655 }