altos: Add SDCARD and FAT16 filesystem support
[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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #ifndef AO_FAT_TEST
19 #include "ao.h"
20 #endif
21
22 #include "ao_fat.h"
23 #include "ao_bufio.h"
24
25 /* Partition information, block numbers */
26
27 static uint8_t partition_type;
28 static uint32_t partition_start, partition_end;
29
30 /* File system parameters */
31 static uint8_t  sectors_per_cluster;
32 static uint32_t bytes_per_cluster;
33 static uint16_t reserved_sector_count;
34 static uint8_t  number_fat;
35 static uint16_t root_entries;
36 static uint16_t sectors_per_fat;
37 static uint32_t fat_start;
38 static uint32_t root_start;
39 static uint32_t data_start;
40
41 static uint32_t
42 get_u32(uint8_t *base)
43 {
44         return ((uint32_t) base[0] |
45                 ((uint32_t) base[1] << 8) |
46                 ((uint32_t) base[2] << 16) |
47                 ((uint32_t) base[3] << 24));
48 }
49
50 static void
51 put_u32(uint8_t *base, uint32_t value)
52 {
53         base[0] = value;
54         base[1] = value >> 8;
55         base[2] = value >> 16;
56         base[3] = value >> 24;
57 }
58
59 static uint16_t
60 get_u16(uint8_t *base)
61 {
62         return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
63 }
64
65 static void
66 put_u16(uint8_t *base, uint16_t value)
67 {
68         base[0] = value;
69         base[1] = value >> 8;
70 }
71
72 static uint8_t
73 ao_fat_cluster_valid(uint16_t cluster)
74 {
75         return (2 <= cluster && cluster < 0xfff0);
76 }
77
78 /* Start using a block */
79 static uint8_t *
80 ao_fat_block_get(uint32_t block)
81 {
82         block += partition_start;
83         if (block >= partition_end)
84                 return NULL;
85         return ao_bufio_get(block);
86 }
87
88 /* Finish using a block, 'w' is 1 if modified */
89 #define ao_fat_block_put(b,w) ao_bufio_put(b,w)
90
91 /* Start using a root directory entry */
92 static uint8_t *
93 ao_fat_root_get(uint16_t e)
94 {
95         uint32_t        byte = e * 0x20;
96         uint32_t        sector = byte >> 9;
97         uint16_t        offset = byte & 0x1ff;
98         uint8_t         *buf;
99
100         buf = ao_fat_block_get(root_start + sector);
101         if (!buf)
102                 return NULL;
103         return buf + offset;
104 }
105
106 /* Finish using a root directory entry, 'w' is 1 if modified */
107 static void
108 ao_fat_root_put(uint8_t *root, uint16_t e, uint8_t write)
109 {
110         uint16_t        offset = ((e * 0x20) & 0x1ff);
111         uint8_t         *buf = root - offset;
112
113         ao_fat_block_put(buf, write);
114 }
115
116 /* Get the next cluster entry in the chain */
117 static uint16_t
118 ao_fat_entry_read(uint16_t cluster)
119 {
120         uint32_t        sector;
121         uint16_t        offset;
122         uint8_t         *buf;
123         uint16_t        ret;
124
125         if (!ao_fat_cluster_valid(cluster))
126                 return 0xfff7;
127
128         cluster -= 2;
129         sector = cluster >> 8;
130         offset = (cluster << 1) & 0x1ff;
131         buf = ao_fat_block_get(fat_start + sector);
132         if (!buf)
133                 return 0;
134         ret = buf[offset] | (buf[offset+1] << 8);
135         ao_fat_block_put(buf, 0);
136         return ret;
137 }
138
139 static uint16_t
140 ao_fat_entry_replace(uint16_t  cluster, uint16_t new_value)
141 {
142         uint32_t        sector;
143         uint16_t        offset;
144         uint8_t         *buf;
145         uint16_t        ret;
146         uint8_t         other_fats;
147
148         if (!ao_fat_cluster_valid(cluster))
149                 return 0;
150
151         cluster -= 2;
152         sector = cluster >> 8;
153         offset = (cluster << 1) & 0x1ff;
154         buf = ao_fat_block_get(fat_start + sector);
155         if (!buf)
156                 return 0;
157         ret = get_u16(buf + offset);
158         put_u16(buf + offset, new_value);
159         ao_fat_block_put(buf, 1);
160         for (other_fats = 1; other_fats < number_fat; other_fats++) {
161                 buf = ao_fat_block_get(fat_start + other_fats * sectors_per_fat);
162                 if (buf) {
163                         put_u16(buf + offset, new_value);
164                         ao_fat_block_put(buf, 1);
165                 }
166         }
167         return ret;
168         
169 }
170
171 static void
172 ao_fat_clear_cluster_chain(uint16_t cluster)
173 {
174         while (ao_fat_cluster_valid(cluster))
175                 cluster = ao_fat_entry_replace(cluster, 0x0000);
176 }
177
178 static uint16_t
179 ao_fat_cluster_seek(uint16_t cluster, uint16_t distance)
180 {
181         while (distance) {
182                 cluster = ao_fat_entry_read(cluster);
183                 if (!ao_fat_cluster_valid(cluster))
184                         break;
185                 distance--;
186         }
187         return cluster;
188 }
189
190 static uint32_t
191 ao_fat_sector_seek(uint16_t cluster, uint32_t sector)
192 {
193         cluster = ao_fat_cluster_seek(cluster, sector / sectors_per_cluster);
194         if (!ao_fat_cluster_valid(cluster))
195                 return 0xffffffff;
196         return data_start + (cluster-2) * sectors_per_cluster + sector % sectors_per_cluster;
197 }
198
199 /* Load the boot block and find the first partition */
200 static uint8_t
201 ao_fat_setup_partition(void)
202 {
203         uint8_t *mbr;
204         uint8_t *partition;
205         uint32_t partition_size;
206
207         mbr = ao_bufio_get(0);
208         if (!mbr)
209                 return 0;
210
211         /* Check the signature */
212         if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
213                 printf ("Invalid MBR signature %02x %02x\n",
214                         mbr[0x1fe], mbr[0x1ff]);
215                 ao_bufio_put(mbr, 0);
216                 return 0;
217         }
218
219         /* Just use the first partition */
220         partition = &mbr[0x1be];
221         
222         partition_type = partition[4];
223         switch (partition_type) {
224         case 4:         /* FAT16 up to 32M */
225         case 6:         /* FAT16 over 32M */
226                 break;
227         case 0x0b:      /* FAT32 up to 2047GB */
228         case 0x0c:      /* FAT32 LBA */
229                 break;
230         default:
231                 printf ("Invalid partition type %02x\n", partition_type);
232                 ao_bufio_put(mbr, 0);
233                 return 0;
234         }
235
236         partition_start = get_u32(partition+8);
237         partition_size = get_u32(partition+12);
238         if (partition_size == 0) {
239                 printf ("Zero-sized partition\n");
240                 ao_bufio_put(mbr, 0);
241                 return 0;
242         }
243         partition_end = partition_start + partition_size;
244         printf ("Partition type %02x start %08x end %08x\n",
245                 partition_type, partition_start, partition_end);
246         ao_bufio_put(mbr, 0);
247         return 1;
248 }
249         
250 static uint8_t
251 ao_fat_setup_fs(void)
252 {
253         uint8_t *boot = ao_fat_block_get(0);
254
255         if (!boot)
256                 return 0;
257
258         /* Check the signature */
259         if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
260                 printf ("Invalid BOOT signature %02x %02x\n",
261                         boot[0x1fe], boot[0x1ff]);
262                 ao_bufio_put(boot, 0);
263                 return 0;
264         }
265
266         /* Check the sector size */
267         if (get_u16(boot + 0xb) != 0x200) {
268                 printf ("Invalid sector size %d\n",
269                         get_u16(boot + 0xb));
270                 ao_bufio_put(boot, 0);
271                 return 0;
272         }
273
274         sectors_per_cluster = boot[0xd];
275         bytes_per_cluster = sectors_per_cluster << 9;
276         reserved_sector_count = get_u16(boot+0xe);
277         number_fat = boot[0x10];
278         root_entries = get_u16(boot + 0x11);
279         sectors_per_fat = get_u16(boot+0x16);
280
281         printf ("sectors per cluster %d\n", sectors_per_cluster);
282         printf ("reserved sectors %d\n", reserved_sector_count);
283         printf ("number of FATs %d\n", number_fat);
284         printf ("root entries %d\n", root_entries);
285         printf ("sectors per fat %d\n", sectors_per_fat);
286
287         fat_start = reserved_sector_count;;
288         root_start = fat_start + number_fat * sectors_per_fat;
289         data_start = root_start + ((root_entries * 0x20 + 0x1ff) >> 9);
290
291         printf ("fat  start %d\n", fat_start);
292         printf ("root start %d\n", root_start);
293         printf ("data start %d\n", data_start);
294
295         return 1;
296 }
297
298 static uint8_t
299 ao_fat_setup(void)
300 {
301         if (!ao_fat_setup_partition())
302                 return 0;
303         if (!ao_fat_setup_fs())
304                 return 0;
305         return 1;
306 }
307
308 /*
309  * Low-level directory operations
310  */
311
312 /*
313  * Basic file operations
314  */
315
316 static struct ao_fat_dirent     ao_file_dirent;
317 static uint32_t                 ao_file_offset;
318
319 static uint32_t
320 ao_file_offset_to_sector(uint32_t offset)
321 {
322         if (offset > ao_file_dirent.size)
323                 return 0xffffffff;
324         return ao_fat_sector_seek(ao_file_dirent.cluster, offset >> 9);
325 }
326
327 uint8_t
328 ao_fat_open(char name[11])
329 {
330         uint16_t                entry = 0;
331         struct ao_fat_dirent    dirent;
332
333         while (ao_fat_readdir(&entry, &dirent)) {
334                 if (!memcmp(name, dirent.name, 11)) {
335                         ao_file_dirent = dirent;
336                         ao_file_offset = 0;
337                         return 1;
338                 }
339         }
340         return 0;
341 }
342
343
344
345 static uint8_t
346 ao_fat_set_size(uint32_t size)
347 {
348         uint16_t        clear_cluster = 0;
349         uint8_t         *dent;
350         uint16_t        first_cluster;
351
352         first_cluster = ao_file_dirent.cluster;
353         printf ("set size to %d\n", size);
354         if (size == ao_file_dirent.size)
355                 return 1;
356         if (size == 0) {
357                 printf ("erase file\n");
358                 clear_cluster = ao_file_dirent.cluster;
359                 first_cluster = 0;
360         } else {
361                 uint16_t        new_num;
362                 uint16_t        old_num;
363
364                 new_num = (size + bytes_per_cluster - 1) / bytes_per_cluster;
365                 old_num = (ao_file_dirent.size + bytes_per_cluster - 1) / bytes_per_cluster;
366                 if (new_num < old_num) {
367                         uint16_t last_cluster;
368
369                         printf("Remove %d clusters\n", old_num - new_num);
370                         /* Go find the last cluster we want to preserve in the file */
371                         last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, new_num - 1);
372
373                         printf ("Last cluster is now %04x\n", last_cluster);
374                         /* Rewrite that cluster entry with 0xffff to mark the end of the chain */
375                         clear_cluster = ao_fat_entry_replace(last_cluster, 0xffff);
376                 } else if (new_num > old_num) {
377                         uint16_t        need;
378                         uint16_t        free;
379                         uint16_t        last_cluster;
380
381                         if (old_num)
382                                 last_cluster = ao_fat_cluster_seek(ao_file_dirent.cluster, old_num - 1);
383                         else
384                                 last_cluster = 0;
385
386                         need = new_num - old_num;
387                         printf ("Need %d clusters\n", need);
388                         /* See if there are enough free clusters in the file system */
389                         for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) {
390                                 if (!ao_fat_entry_read(free)) {
391                                         printf ("\tCluster %04x available\n", free);
392                                         need--;
393                                 }
394                         }
395                         /* Still need some, tell the user that we've failed */
396                         if (need) {
397                                 printf ("File system full\n");
398                                 return 0;
399                         }
400
401                         need = new_num - old_num;
402                         /* Now go allocate those clusters */
403                         for (free = 2; need > 0 && (free - 2) < sectors_per_fat * 256; free++) {
404                                 if (!ao_fat_entry_read(free)) {
405                                         printf ("\tAllocate %04x\n", free);
406                                         if (last_cluster)
407                                                 ao_fat_entry_replace(last_cluster, free);
408                                         else
409                                                 first_cluster = free;
410                                         last_cluster = free;
411                                         need--;
412                                 }
413                         }
414                         /* Mark the new end of the chain */
415                         ao_fat_entry_replace(last_cluster, 0xffff);
416                 }
417         }
418
419         /* Deallocate clusters off the end of the file */
420         if (ao_fat_cluster_valid(clear_cluster)) {
421                 printf ("Clear clusters starting with %04x\n", clear_cluster);
422                 ao_fat_clear_cluster_chain(clear_cluster);
423         }
424
425         dent = ao_fat_root_get(ao_file_dirent.entry);
426         if (!dent)
427                 return 0;
428         put_u32(dent + 0x1c, size);
429         put_u16(dent + 0x1a, first_cluster);
430         ao_fat_root_put(dent, ao_file_dirent.entry, 1);
431         ao_file_dirent.size = size;
432         ao_file_dirent.cluster = first_cluster;
433         return 1;
434 }
435
436 uint8_t
437 ao_fat_creat(char name[11])
438 {
439         uint16_t        entry;
440
441         if (ao_fat_open(name))
442                 return ao_fat_set_size(0);
443
444         for (entry = 0; entry < root_entries; entry++) {
445                 uint8_t *dent = ao_fat_root_get(entry);
446
447                 if (dent[0] == AO_FAT_DENT_EMPTY ||
448                     dent[0] == AO_FAT_DENT_END) {
449                         memmove(dent, name, 11);
450                         dent[0x0b] = 0x00;
451                         dent[0x0c] = 0x00;
452                         dent[0x0d] = 0x00;
453                         /* XXX fix time */
454                         put_u16(dent + 0x0e, 0);
455                         /* XXX fix date */
456                         put_u16(dent + 0x10, 0);
457                         /* XXX fix date */
458                         put_u16(dent + 0x12, 0);
459                         /* XXX FAT32 high cluster bytes */
460                         put_u16(dent + 0x14, 0);
461                         /* XXX fix time */
462                         put_u16(dent + 0x16, 0);
463                         /* XXX fix date */
464                         put_u16(dent + 0x18, 0);
465                         /* cluster number */
466                         put_u16(dent + 0x1a, 0);
467                         /* size */
468                         put_u32(dent + 0x1c, 0);
469                         ao_fat_root_put(dent, entry, 1);
470                         return ao_fat_open(name);
471                 }
472         }
473         return 0;
474 }
475
476 void
477 ao_fat_close(void)
478 {
479         memset(&ao_file_dirent, '\0', sizeof (struct ao_fat_dirent));
480         ao_bufio_flush();
481 }
482
483 int
484 ao_fat_read(uint8_t *dest, int len)
485 {
486         uint32_t        sector;
487         uint16_t        this_time;
488         uint16_t        offset;
489         uint8_t         *buf;
490         int             ret = 0;
491
492         if (ao_file_offset + len > ao_file_dirent.size)
493                 len = ao_file_dirent.size - ao_file_offset;
494
495         while (len) {
496                 offset = ao_file_offset & 0x1ff;
497                 if (offset + len < 512)
498                         this_time = len;
499                 else
500                         this_time = 512 - offset;
501
502                 sector = ao_file_offset_to_sector(ao_file_offset);
503                 if (sector == 0xffffffff)
504                         break;
505                 buf = ao_fat_block_get(sector);
506                 if (!buf)
507                         break;
508                 memcpy(dest, buf + offset, this_time);
509                 ao_fat_block_put(buf, 0);
510
511                 ret += this_time;
512                 len -= this_time;
513                 dest += this_time;
514                 ao_file_offset += this_time;
515         }
516         return ret;
517 }
518
519 int
520 ao_fat_write(uint8_t *src, int len)
521 {
522         uint32_t        sector;
523         uint16_t        this_time;
524         uint16_t        offset;
525         uint8_t         *buf;
526         int             ret = 0;
527
528         if (ao_file_offset + len > ao_file_dirent.size) {
529                 if (!ao_fat_set_size(ao_file_offset + len))
530                         return 0;
531         }
532
533         while (len) {
534                 offset = ao_file_offset & 0x1ff;
535                 if (offset + len < 512)
536                         this_time = len;
537                 else
538                         this_time = 512 - offset;
539
540                 sector = ao_file_offset_to_sector(ao_file_offset);
541                 if (sector == 0xffffffff)
542                         break;
543                 buf = ao_fat_block_get(sector);
544                 if (!buf)
545                         break;
546                 memcpy(buf + offset, src, this_time);
547                 ao_fat_block_put(buf, 1);
548
549                 ret += this_time;
550                 len -= this_time;
551                 src += this_time;
552                 ao_file_offset += this_time;
553         }
554         return 0;
555 }
556
557 uint32_t
558 ao_fat_seek(int32_t pos, uint8_t whence)
559 {
560         switch (whence) {
561         case AO_FAT_SEEK_SET:
562                 ao_file_offset = pos;
563                 break;
564         case AO_FAT_SEEK_CUR:
565                 ao_file_offset += pos;
566                 break;
567         case AO_FAT_SEEK_END:
568                 ao_file_offset = ao_file_dirent.size + pos;
569                 break;
570         }
571         if (ao_file_offset > ao_file_dirent.size)
572                 ao_fat_set_size(ao_file_offset);
573         return ao_file_offset;
574 }
575
576 uint8_t
577 ao_fat_unlink(char name[11])
578 {
579         uint16_t                entry = 0;
580         struct ao_fat_dirent    dirent;
581
582         while (ao_fat_readdir(&entry, &dirent)) {
583                 if (memcmp(name, dirent.name, 11) == 0) {
584                         uint8_t *next;
585                         uint8_t *ent;
586                         uint8_t delete;
587                         ao_fat_clear_cluster_chain(dirent.cluster);
588                         next = ao_fat_root_get(dirent.entry + 1);
589                         if (next && next[0] != AO_FAT_DENT_END)
590                                 delete = AO_FAT_DENT_EMPTY;
591                         else
592                                 delete = AO_FAT_DENT_END;
593                         if (next)
594                                 ao_fat_root_put(next, dirent.entry + 1, 0);
595                         ent = ao_fat_root_get(dirent.entry);
596                         if (ent) {
597                                 memset(ent, '\0', 0x20);
598                                 *ent = delete;
599                                 ao_fat_root_put(ent, dirent.entry, 1);
600                         }
601                         ao_bufio_flush();
602                         return 1;
603                 }
604         }
605         return 0;
606 }
607
608 uint8_t
609 ao_fat_rename(char old[11], char new[11])
610 {
611         return 0;
612 }
613
614 uint8_t
615 ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
616 {
617         uint8_t *dent;
618
619         if (*entry >= root_entries)
620                 return 0;
621         for (;;) {
622                 dent = ao_fat_root_get(*entry);
623
624                 if (dent[0] == AO_FAT_DENT_END) {
625                         ao_fat_root_put(dent, *entry, 0);
626                         return 0;
627                 }
628                 if (dent[0] != AO_FAT_DENT_EMPTY &&
629                     (dent[0x0b] & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == 0)
630                         break;
631                 ao_fat_root_put(dent, *entry, 0);
632                 (*entry)++;
633         }
634         memcpy(dirent->name, dent, 11);
635         dirent->attr = dent[0xb];
636         dirent->size = get_u32(dent+0x1c);
637         dirent->cluster = get_u16(dent+0x1a);
638         dirent->entry = *entry;
639         ao_fat_root_put(dent, *entry, 0);
640         (*entry)++;
641         return 1;
642 }
643
644 static void
645 ao_fat_list(void)
646 {
647         uint16_t                entry = 0;
648         struct ao_fat_dirent    dirent;
649
650         while (ao_fat_readdir(&entry, &dirent)) {
651                 printf ("%-8.8s.%-3.3s %02x %d\n",
652                         dirent.name, dirent.name + 8, dirent.attr, dirent.size);
653         }
654 }
655
656 static void
657 ao_fat_test(void)
658 {
659         ao_fat_setup();
660         ao_fat_list();
661 }
662
663 static const struct ao_cmds ao_fat_cmds[] = {
664         { ao_fat_test,  "F\0Test FAT" },
665         { 0, NULL },
666 };
667
668 void
669 ao_fat_init(void)
670 {
671         ao_bufio_init();
672         ao_cmd_register(&ao_fat_cmds[0]);
673 }
674