ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat.c
Go to the documentation of this file.
1
28
29#include <stddef.h>
30#include <stdint.h>
31
32#include "ra8_attributes.h"
33#include "ra8_fs.h"
34#include "ra8_fs_fat_internal.h"
35
36/* =============================================================================
37 * Little-endian helpers
38 * =============================================================================
39 */
40
41/* `priv_rd16()`: see header for the documented contract. */
42uint16_t priv_rd16(const uint8_t* p)
43{
44 return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << k_shift_byte));
45}
46
47/* `priv_rd32()`: see header for the documented contract. */
48uint32_t priv_rd32(const uint8_t* p)
49{
50 return (uint32_t)p[0] | ((uint32_t)p[1] << k_shift_byte) | ((uint32_t)p[2] << k_shift_two_bytes) |
51 ((uint32_t)p[3] << k_shift_three_bytes);
52}
53
54/* `priv_rd64()`: see header for the documented contract. */
55uint64_t priv_rd64(const uint8_t* p)
56{
57 return (uint64_t)priv_rd32(p) | ((uint64_t)priv_rd32(&p[sizeof(uint32_t)]) << k_shift_word32);
58}
59
60/* `priv_wr16()`: see header for the documented contract. */
61void priv_wr16(uint8_t* p, uint16_t v)
62{
63 p[0] = (uint8_t)(v & k_byte_mask);
64 p[1] = (uint8_t)((v >> k_shift_byte) & k_byte_mask);
65}
66
67/* `priv_wr32()`: see header for the documented contract. */
68void priv_wr32(uint8_t* p, uint32_t v)
69{
70 p[0] = (uint8_t)(v & k_byte_mask);
71 p[1] = (uint8_t)((v >> k_shift_byte) & k_byte_mask);
72 p[2] = (uint8_t)((v >> k_shift_two_bytes) & k_byte_mask);
73 p[3] = (uint8_t)((v >> k_shift_three_bytes) & k_byte_mask);
74}
75
76/* `priv_wr64()`: see header for the documented contract. */
77void priv_wr64(uint8_t* p, uint64_t v)
78{
79 priv_wr32(p, (uint32_t)v);
80 priv_wr32(&p[sizeof(uint32_t)], (uint32_t)(v >> k_shift_word32));
81}
82
83/* `priv_bps()`: see header for the documented contract. */
84uint32_t priv_bps(const ra8_fs_mount_t* m)
85{
86 return m->bytes_per_sector;
87}
88
89/* `priv_cluster_bytes()`: see header for the documented contract. */
91{
93}
94
95/* `priv_dir_eps()`: see header for the documented contract. */
96uint32_t priv_dir_eps(const ra8_fs_mount_t* m)
97{
98 return m->bytes_per_sector / (uint32_t)k_ra8_fs_dir_entry_bytes;
99}
100
101/* `priv_byte_fill()`: see header for the documented contract. */
102void priv_byte_fill(uint8_t* dst, uint8_t value, uint32_t n)
103{
104 for (uint32_t i = 0U; i < n; i++) {
105 dst[i] = value;
106 }
107}
108
109/* `priv_byte_copy()`: see header for the documented contract. */
110void priv_byte_copy(uint8_t* dst, const uint8_t* src, uint32_t n)
111{
112 for (uint32_t i = 0U; i < n; i++) {
113 dst[i] = src[i];
114 }
115}
116
117/* `priv_byte_equal()`: see header for the documented contract. */
118uint8_t priv_byte_equal(const uint8_t* a, const uint8_t* b, uint32_t n)
119{
120 for (uint32_t i = 0U; i < n; i++) {
121 if (a[i] != b[i]) {
122 return 0U;
123 }
124 }
125 return 1U;
126}
127
128/* =============================================================================
129 * Backend wrappers
130 * =============================================================================
131 */
132
133/* `priv_read_sector()`: see header for the documented contract. */
134ra8_err_t priv_read_sector(const ra8_fs_mount_t* m, uint64_t lba, uint8_t* buf)
135{
136 return m->backend.read_block(m->backend.ctx, lba + m->partition_base_lba, 1, buf);
137}
138
139/* `priv_write_sector()`: see header for the documented contract. */
140ra8_err_t priv_write_sector(const ra8_fs_mount_t* m, uint64_t lba, const uint8_t* buf)
141{
142 return m->backend.write_block(m->backend.ctx, lba + m->partition_base_lba, 1, buf);
143}
144
145/* =============================================================================
146 * FAT entry get/set -- dispatches across FAT12/16/32 layouts
147 * =============================================================================
148 */
149
172static uint64_t internal_fat_entry_byte_offset(const ra8_fs_mount_t* m, uint32_t cluster)
173{
174 if (m->type == k_ra8_fs_type_fat12) {
175 return (uint64_t)cluster + ((uint64_t)cluster / 2U);
176 }
177 if (m->type == k_ra8_fs_type_fat16) {
178 return (uint64_t)cluster * 2U;
179 }
180 /* 8 bytes on neither format: FAT32 and exFAT entries are 4 bytes, and on an
181 * exFAT volume near the 2^32-cluster ceiling the product exceeds 32 bits --
182 * which is exactly why this returns 64 bits. */
183 return (uint64_t)cluster * 4U;
184}
185
186/* `priv_fat_get()`: see header for the documented contract. */
187ra8_err_t priv_fat_get(const ra8_fs_mount_t* m, uint32_t cluster, uint32_t* out_value)
188{
189 const uint64_t fat_offset = internal_fat_entry_byte_offset(m, cluster);
190 const uint64_t sec_num = m->first_fat_lba + (fat_offset / priv_bps(m));
191 const uint32_t sec_off = (uint32_t)(fat_offset % priv_bps(m));
192
193 uint8_t* const buf = priv_sec_fat();
194 ra8_err_t err = priv_fat_sector_read(m, sec_num, buf);
195 if (err != k_ra8_ok) {
196 return err;
197 }
198
199 uint32_t v = 0;
200 if (m->type == k_ra8_fs_type_fat12) {
201 /* MS FAT spec sec 4.1: read 16 bits straddling the byte and shift. */
202 uint8_t b0 = buf[sec_off];
203 uint8_t b1 = 0;
204 if (sec_off + 1U < priv_bps(m)) {
205 b1 = buf[sec_off + 1U];
206 } else {
207 uint8_t* const buf2 = priv_sec_fat2();
208 err = priv_fat_sector_read(m, sec_num + 1U, buf2);
209 if (err != k_ra8_ok) {
210 return err;
211 }
212 b1 = buf2[0];
213 }
214 uint16_t raw = (uint16_t)((uint16_t)b0 | ((uint16_t)b1 << k_shift_byte));
215 if ((cluster & 1U) != 0U) {
216 v = (uint32_t)(raw >> k_shift_nibble);
217 } else {
218 v = (uint32_t)(raw & k_fat12_value_mask);
219 }
220 } else if (m->type == k_ra8_fs_type_fat16) {
221 v = priv_rd16(&buf[sec_off]);
222 } else if (m->type == k_ra8_fs_type_exfat) {
223 /* exFAT spec sec 4.1: a FAT entry is a full 32-bit value. FAT32's top four
224 * bits are reserved and masked off above; masking here would fold the
225 * end-of-chain marker 0xFFFFFFFF down to 0x0FFFFFFF and, on a volume with
226 * more than 2^28 clusters, truncate an ordinary cluster number. */
227 v = priv_rd32(&buf[sec_off]);
228 } else {
229 v = priv_rd32(&buf[sec_off]) & k_cluster_mask_fat32;
230 }
231
232 *out_value = v;
233 return k_ra8_ok;
234}
235
265 const uint8_t* buf,
266 const uint8_t* buf2,
267 uint64_t sec_num,
268 uint8_t straddle)
269{
270 ra8_err_t err = priv_write_sector(m, sec_num, buf);
271 if (err != k_ra8_ok) {
272 return err;
273 }
274 priv_fat_sector_wrote(m, buf, sec_num);
275 if (straddle == 0U) {
276 return k_ra8_ok;
277 }
278 err = priv_write_sector(m, sec_num + 1U, buf2);
279 if (err != k_ra8_ok) {
280 return err;
281 }
282 priv_fat_sector_wrote(m, buf2, sec_num + 1U);
283 return k_ra8_ok;
284}
285
312 uint64_t sec_num,
313 uint32_t sec_off,
314 uint32_t cluster,
315 uint32_t value)
316{
317 uint8_t* const buf = priv_sec_fat();
318 uint8_t* const buf2 = priv_sec_fat2();
319 uint8_t straddle = 0U;
320 ra8_err_t err = priv_fat_sector_read(m, sec_num, buf);
321 if (err != k_ra8_ok) {
322 return err;
323 }
324 uint8_t b0 = buf[sec_off];
325 uint8_t b1 = 0;
326 if (sec_off + 1U < priv_bps(m)) {
327 b1 = buf[sec_off + 1U];
328 } else {
329 err = priv_fat_sector_read(m, sec_num + 1U, buf2);
330 if (err != k_ra8_ok) {
331 return err;
332 }
333 b1 = buf2[0];
334 straddle = 1U;
335 }
336 uint16_t raw = (uint16_t)((uint16_t)b0 | ((uint16_t)b1 << k_shift_byte));
337 if ((cluster & 1U) != 0U) {
338 raw = (uint16_t)((raw & k_fat12_low_nibble_mask) |
339 (uint16_t)((value & k_fat12_value_mask) << k_shift_nibble));
340 } else {
341 raw = (uint16_t)((raw & k_fat12_high_nibble_mask) | (uint16_t)(value & k_fat12_value_mask));
342 }
343 buf[sec_off] = (uint8_t)(raw & k_byte_mask);
344 if (straddle == 0U) {
345 buf[sec_off + 1U] = (uint8_t)((raw >> k_shift_byte) & k_byte_mask);
346 } else {
347 buf2[0] = (uint8_t)((raw >> k_shift_byte) & k_byte_mask);
348 }
349 return internal_fat12_store(m, buf, buf2, sec_num, straddle);
350}
351
377static ra8_err_t
378internal_fat16_set_one(const ra8_fs_mount_t* m, uint64_t sec_num, uint32_t sec_off, uint32_t value)
379{
380 uint8_t* const buf = priv_sec_fat();
381 ra8_err_t err = priv_fat_sector_read(m, sec_num, buf);
382 if (err != k_ra8_ok) {
383 return err;
384 }
385 priv_wr16(&buf[sec_off], (uint16_t)(value & k_word_mask));
386 err = priv_write_sector(m, sec_num, buf);
387 if (err != k_ra8_ok) {
388 return err;
389 }
390 priv_fat_sector_wrote(m, buf, sec_num);
391 return k_ra8_ok;
392}
393
419static ra8_err_t
420internal_fat32_set_one(const ra8_fs_mount_t* m, uint64_t sec_num, uint32_t sec_off, uint32_t value)
421{
422 uint8_t* const buf = priv_sec_fat();
423 ra8_err_t err = priv_fat_sector_read(m, sec_num, buf);
424 if (err != k_ra8_ok) {
425 return err;
426 }
427 uint32_t prev = priv_rd32(&buf[sec_off]) & ~k_cluster_mask_fat32;
428 priv_wr32(&buf[sec_off], (value & k_cluster_mask_fat32) | prev);
429 err = priv_write_sector(m, sec_num, buf);
430 if (err != k_ra8_ok) {
431 return err;
432 }
433 priv_fat_sector_wrote(m, buf, sec_num);
434 return k_ra8_ok;
435}
436
467 uint64_t sec_num,
468 uint32_t sec_off,
469 uint32_t value)
470{
471 uint8_t* const buf = priv_sec_fat();
472 ra8_err_t err = priv_fat_sector_read(m, sec_num, buf);
473 if (err != k_ra8_ok) {
474 return err;
475 }
476 priv_wr32(&buf[sec_off], value);
477 err = priv_write_sector(m, sec_num, buf);
478 if (err != k_ra8_ok) {
479 return err;
480 }
481 priv_fat_sector_wrote(m, buf, sec_num);
482 return k_ra8_ok;
483}
484
485/* `priv_fat_set()`: see header for the documented contract. */
486ra8_err_t priv_fat_set(const ra8_fs_mount_t* m, uint32_t cluster, uint32_t value)
487{
488 const uint64_t fat_offset = internal_fat_entry_byte_offset(m, cluster);
489 for (uint32_t i = 0; i < m->num_fats; i++) {
490 const uint64_t fat_base = m->first_fat_lba + ((uint64_t)i * m->fat_size_sectors);
491 const uint64_t sec_num = fat_base + (fat_offset / priv_bps(m));
492 const uint32_t sec_off = (uint32_t)(fat_offset % priv_bps(m));
493 ra8_err_t err = k_ra8_ok;
494 if (m->type == k_ra8_fs_type_fat12) {
495 err = internal_fat12_set_one(m, sec_num, sec_off, cluster, value);
496 } else if (m->type == k_ra8_fs_type_fat16) {
497 err = internal_fat16_set_one(m, sec_num, sec_off, value);
498 } else if (m->type == k_ra8_fs_type_exfat) {
499 err = internal_exfat_fat_set_one(m, sec_num, sec_off, value);
500 } else {
501 err = internal_fat32_set_one(m, sec_num, sec_off, value);
502 }
503 if (err != k_ra8_ok) {
504 return err;
505 }
506 }
507 return k_ra8_ok;
508}
509
510/* `priv_is_eoc()`: see header for the documented contract. */
511uint8_t priv_is_eoc(const ra8_fs_mount_t* m, uint32_t value)
512{
513 if (m->type == k_ra8_fs_type_fat12) {
514 return (uint8_t)((value >= k_cluster_eoc_min_fat12) ? 1U : 0U);
515 }
516 if (m->type == k_ra8_fs_type_fat16) {
517 return (uint8_t)((value >= k_cluster_eoc_min_fat16) ? 1U : 0U);
518 }
519 if (m->type == k_ra8_fs_type_exfat) {
520 return (uint8_t)((value >= k_cluster_eoc_min_exfat) ? 1U : 0U);
521 }
522 return (uint8_t)((value >= k_cluster_eoc_min_fat32) ? 1U : 0U);
523}
524
525/* `priv_eoc_write()`: see header for the documented contract. */
527{
528 if (m->type == k_ra8_fs_type_fat12) {
530 }
531 if (m->type == k_ra8_fs_type_fat16) {
533 }
534 if (m->type == k_ra8_fs_type_exfat) {
536 }
538}
539
540/* `priv_cluster_to_lba()`: see header for the documented contract. */
541uint64_t priv_cluster_to_lba(const ra8_fs_mount_t* m, uint32_t cluster)
542{
543 return m->first_data_lba +
544 (((uint64_t)cluster - (uint64_t)k_cluster_first_data) * m->sectors_per_cluster);
545}
546
582static uint32_t internal_alloc_start(const ra8_fs_mount_t* m, uint32_t hint)
583{
584 if ((hint - (uint32_t)k_cluster_first_data) >= m->count_of_clusters) {
585 return (uint32_t)k_cluster_first_data;
586 }
587 return hint;
588}
589
590/* `priv_alloc_cluster()`: see header for the documented contract. */
591ra8_err_t priv_alloc_cluster(const ra8_fs_mount_t* m, uint32_t* out_cluster)
592{
593 /* Start where the last allocation stopped and wrap exactly once, instead of
594 * restarting at cluster 2 every time (#607). Combined with the FAT sector
595 * cache behind priv_fat_get(), appending to a file costs a bounded number of
596 * block reads per cluster rather than one read per cluster EXAMINED -- which
597 * is what made writing a K-cluster file O(K * N) real device round trips. */
598 const uint32_t past_end = (uint32_t)k_cluster_first_data + m->count_of_clusters;
599 uint32_t c = internal_alloc_start(m, priv_alloc_hint_get(m));
600 for (uint32_t seen = 0U; seen < m->count_of_clusters; seen++) {
601 uint32_t v = 0;
602 ra8_err_t err = priv_fat_get(m, c, &v);
603 if (err != k_ra8_ok) {
604 return err;
605 }
606 if (v == k_cluster_free) {
607 *out_cluster = c;
608 priv_alloc_hint_set(m, c + 1U);
610 return k_ra8_ok;
611 }
612 c++;
613 if (c >= past_end) {
614 c = (uint32_t)k_cluster_first_data;
615 }
616 }
617 return k_ra8_err_no_mem;
618}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Minimal FAT12/FAT16/FAT32 filesystem adapter (read + write).
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
ra8_err_t priv_fat_get(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t *out_value)
Fetch the FAT entry for cluster, returning the next-cluster value.
Definition ra8_fs_fat.c:187
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
void priv_wr64(uint8_t *p, uint64_t v)
Encode a uint64_t into a byte buffer, little-endian.
Definition ra8_fs_fat.c:77
static ra8_err_t internal_fat16_set_one(const ra8_fs_mount_t *m, uint64_t sec_num, uint32_t sec_off, uint32_t value)
Write a FAT16 entry into one sector.
Definition ra8_fs_fat.c:378
ra8_err_t priv_fat_set(const ra8_fs_mount_t *m, uint32_t cluster, uint32_t value)
Write value into the FAT entry for cluster across every FAT copy.
Definition ra8_fs_fat.c:486
void priv_byte_fill(uint8_t *dst, uint8_t value, uint32_t n)
Fill n bytes of dst with value.
Definition ra8_fs_fat.c:102
uint64_t priv_cluster_to_lba(const ra8_fs_mount_t *m, uint32_t cluster)
Convert a cluster number into its first data-region LBA.
Definition ra8_fs_fat.c:541
uint32_t priv_dir_eps(const ra8_fs_mount_t *m)
Directory entries per sector on one mounted volume.
Definition ra8_fs_fat.c:96
static ra8_err_t internal_fat32_set_one(const ra8_fs_mount_t *m, uint64_t sec_num, uint32_t sec_off, uint32_t value)
Write a FAT32 entry into one sector (preserves top 4 reserved bits).
Definition ra8_fs_fat.c:420
uint8_t priv_is_eoc(const ra8_fs_mount_t *m, uint32_t value)
Test whether value is an end-of-chain marker for this FAT type.
Definition ra8_fs_fat.c:511
static uint64_t internal_fat_entry_byte_offset(const ra8_fs_mount_t *m, uint32_t cluster)
Compute the byte offset of cluster's FAT entry for this FAT type.
Definition ra8_fs_fat.c:172
ra8_err_t priv_read_sector(const ra8_fs_mount_t *m, uint64_t lba, uint8_t *buf)
Read a single sector into the module scratch buffer.
Definition ra8_fs_fat.c:134
uint32_t priv_bps(const ra8_fs_mount_t *m)
One mounted volume's sector size in bytes.
Definition ra8_fs_fat.c:84
static ra8_err_t internal_fat12_set_one(const ra8_fs_mount_t *m, uint64_t sec_num, uint32_t sec_off, uint32_t cluster, uint32_t value)
Write a FAT12 entry, handling sector-straddling 12-bit packing.
Definition ra8_fs_fat.c:311
uint8_t priv_byte_equal(const uint8_t *a, const uint8_t *b, uint32_t n)
Compare two byte buffers for equality (length n).
Definition ra8_fs_fat.c:118
uint32_t priv_eoc_write(const ra8_fs_mount_t *m)
End-of-chain value to write for this FAT type.
Definition ra8_fs_fat.c:526
void priv_wr32(uint8_t *p, uint32_t v)
Encode a little-endian uint32_t into a byte buffer.
Definition ra8_fs_fat.c:68
ra8_err_t priv_alloc_cluster(const ra8_fs_mount_t *m, uint32_t *out_cluster)
Free-cluster scan from the next-free hint, wrapping exactly once.
Definition ra8_fs_fat.c:591
static ra8_err_t internal_exfat_fat_set_one(const ra8_fs_mount_t *m, uint64_t sec_num, uint32_t sec_off, uint32_t value)
Write an exFAT FAT entry – all 32 bits, nothing reserved.
Definition ra8_fs_fat.c:466
static uint32_t internal_alloc_start(const ra8_fs_mount_t *m, uint32_t hint)
Normalise a next-free hint into a real cluster number.
Definition ra8_fs_fat.c:582
void priv_byte_copy(uint8_t *dst, const uint8_t *src, uint32_t n)
Length-checked byte copy used in place of memcpy().
Definition ra8_fs_fat.c:110
static ra8_err_t internal_fat12_store(const ra8_fs_mount_t *m, const uint8_t *buf, const uint8_t *buf2, uint64_t sec_num, uint8_t straddle)
Commit one (or, when straddling, two) FAT12 sectors and refresh the cache.
Definition ra8_fs_fat.c:264
uint64_t priv_rd64(const uint8_t *p)
Decode a little-endian uint64_t from a byte buffer.
Definition ra8_fs_fat.c:55
uint32_t priv_cluster_bytes(const ra8_fs_mount_t *m)
One mounted volume's cluster size in bytes.
Definition ra8_fs_fat.c:90
ra8_err_t priv_write_sector(const ra8_fs_mount_t *m, uint64_t lba, const uint8_t *buf)
Write a single sector from a caller-provided buffer.
Definition ra8_fs_fat.c:140
void priv_wr16(uint8_t *p, uint16_t v)
Encode a little-endian uint16_t into a byte buffer.
Definition ra8_fs_fat.c:61
void priv_free_count_took(const ra8_fs_mount_t *m, uint32_t n)
Account n clusters as taken out of the volume's free space.
void priv_fat_sector_wrote(const ra8_fs_mount_t *m, const uint8_t *buf, uint64_t lba)
Tell the cache that lba has just been written with buf.
void priv_alloc_hint_set(const ra8_fs_mount_t *m, uint32_t cluster)
Move the next-free hint forward to cluster.
ra8_err_t priv_fat_sector_read(const ra8_fs_mount_t *m, uint64_t lba, uint8_t *buf)
Read one FAT sector, through the shared one-sector cache.
uint32_t priv_alloc_hint_get(const ra8_fs_mount_t *m)
Report the cluster a free-space scan should start from.
uint8_t * priv_sec_fat2(void)
The FAT2-role sector buffer (the FAT12 straddle's second sector).
uint8_t * priv_sec_fat(void)
The FAT-role sector buffer (priv_fat_get and the FAT setters).
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
@ k_fat12_high_nibble_mask
Untouched nibble (odd cluster).
@ k_cluster_eoc_write_fat32
Cluster eoc write fat32.
@ k_cluster_eoc_min_fat32
MS FAT spec sec 4.2 EOC threshold.
@ k_cluster_eoc_min_fat12
MS FAT spec sec 4.1 EOC threshold.
@ k_cluster_free
Cluster free.
@ k_cluster_eoc_min_fat16
MS FAT spec sec 4.1 EOC threshold.
@ k_fat12_low_nibble_mask
Untouched nibble (even cluster).
@ k_cluster_first_data
Cluster numbers start at 2.
@ k_cluster_eoc_write_exfat
exFAT spec sec 4.1 EOC marker.
@ k_cluster_eoc_write_fat16
Cluster eoc write fat16.
@ k_cluster_mask_fat32
Top 4 bits reserved on FAT32.
@ k_fat12_value_mask
12-bit FAT12 entry mask.
@ k_cluster_eoc_min_exfat
exFAT spec sec 4.1 EOC threshold.
@ k_cluster_eoc_write_fat12
Value we write to terminate.
@ k_shift_byte
Shift byte.
@ k_shift_nibble
Shift nibble.
@ k_byte_mask
Byte mask.
@ k_shift_two_bytes
Shift two bytes.
@ k_word_mask
Word mask.
@ k_shift_word32
Shift a whole 32-bit word (u64 halves).
@ k_shift_three_bytes
Shift three bytes.
@ k_ra8_fs_type_fat12
count_of_clusters < 4085.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_type_fat16
4085 <= count_of_clusters < 65525.
@ k_ra8_fs_dir_entry_bytes
MS FAT spec sec 6 "Directory Entry".
ra8_err_t(* read_block)(void *ctx, uint64_t lba, uint32_t count, uint8_t *buf)
Read count consecutive blocks starting at lba.
ra8_err_t(* write_block)(void *ctx, uint64_t lba, uint32_t count, const uint8_t *buf)
Write count consecutive blocks starting at lba.
void * ctx
Caller-owned context passed back into the function pointers.
Cached parse of one mounted FAT volume.
ra8_fs_type_t type
FAT12 / FAT16 / FAT32.
uint32_t bytes_per_sector
Sector size (BPB / VBR == backend).
uint32_t sectors_per_cluster
BPB BPB_SecPerClus.
uint32_t num_fats
BPB BPB_NumFATs.
ra8_fs_backend_t backend
Block-device backend.
uint32_t fat_size_sectors
BPB BPB_FATSz16 / BPB_FATSz32.
uint64_t partition_base_lba
MBR/GPT partition start (0 = superfloppy).
uint32_t count_of_clusters
Per MS spec: data_sectors / SPC.
uint64_t first_data_lba
First sector of the data region.
uint64_t first_fat_lba
Computed: first FAT sector.