ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_mount.c
Go to the documentation of this file.
1
15
16#include <stddef.h>
17#include <stdint.h>
18
19#include "ra8_attributes.h"
20#include "ra8_fs.h"
21#include "ra8_fs_fat_internal.h"
22
23/* =============================================================================
24 * Module state
25 * =============================================================================
26 */
27
30
33
41
55
58
59/* `priv_sec_walk()`: see header for the documented contract. */
60uint8_t* priv_sec_walk(void)
61{
63}
64
65/* `priv_sec_fat()`: see header for the documented contract. */
66uint8_t* priv_sec_fat(void)
67{
69}
70
71/* `priv_sec_fat2()`: see header for the documented contract. */
72uint8_t* priv_sec_fat2(void)
73{
75}
76
77/* `priv_sec_io()`: see header for the documented contract. */
78uint8_t* priv_sec_io(void)
79{
81}
82
83/* =============================================================================
84 * Slot allocation
85 * =============================================================================
86 */
87
108{
109 for (uint32_t i = 0; i < k_ra8_fs_max_mounts; i++) {
110 if (s_mounts[i].in_use == 0U) {
111 return &s_mounts[i];
112 }
113 }
114 return nullptr;
115}
116
117/* `priv_alloc_file_slot()`: see header for the documented contract. */
119{
120 for (uint32_t i = 0; i < k_ra8_fs_max_files; i++) {
121 if (s_files[i].in_use == 0U) {
122 return &s_files[i];
123 }
124 }
125 return nullptr;
126}
127
128/* =============================================================================
129 * Public API: mount / unmount
130 * =============================================================================
131 */
132
161static uint8_t internal_bps_valid(uint32_t bs)
162{
163 if ((bs < (uint32_t)k_ra8_fs_sector_min) || (bs > (uint32_t)k_ra8_fs_sector_max) ||
164 ((bs & (bs - 1U)) != 0U)) {
165 return 0U;
166 }
167 return 1U;
168}
169
170/* `priv_parse_bpb_into_mount()`: see header for the documented contract. */
172{
176 }
177 const uint32_t bpb_bps = priv_rd16(&g_fs_scratch[k_bpb_off_bytes_per_sec]);
182 /* The BPB must agree with the DEVICE: `m->bytes_per_sector` was seeded from
183 * the backend's reported block size before this parse ran, and a volume
184 * formatted for a different sector size than the medium presents (a 512e
185 * image on a 4Kn device, or vice versa) is unmountable, not reinterpretable
186 * (#683). Every supported size passes ::priv_bps_valid at mount, so the
187 * comparison also enforces 512..4096.
188
189 MC/DC: the three-condition guard's vectors live with the tests that drive
190 it, cited as `libs/ra8_fs/src/ra8_fs_fat_mount.c@priv_parse_bpb_into_mount`. */
191 if (bpb_bps != m->bytes_per_sector || m->sectors_per_cluster == 0U || m->num_fats == 0U) {
193 }
194 const uint32_t fat_sz_16 = priv_rd16(&g_fs_scratch[k_bpb_off_fat_sz_16]);
195 const uint32_t fat_sz_32 = priv_rd32(&g_fs_scratch[k_bpb_off_fat_sz_32]);
196 const uint32_t tot_sec_16 = priv_rd16(&g_fs_scratch[k_bpb_off_tot_sec_16]);
197 const uint32_t tot_sec_32 = priv_rd32(&g_fs_scratch[k_bpb_off_tot_sec_32]);
198 m->fat_size_sectors = (fat_sz_16 != 0U) ? fat_sz_16 : fat_sz_32;
199 /* Chosen in 32 bits first: assigning the composite pick straight into the
200 * 64-bit field would be a MISRA 10.6 composite-widening. */
201 const uint32_t tot_sec = (tot_sec_16 != 0U) ? tot_sec_16 : tot_sec_32;
202 m->total_sectors = tot_sec;
204 return k_ra8_ok;
205}
206
230{
232 const uint32_t root_dir_sectors =
233 ((m->root_entries * k_ra8_fs_dir_entry_bytes) + (priv_bps(m) - 1U)) / priv_bps(m);
234 m->first_root_lba = m->first_fat_lba + ((uint64_t)m->num_fats * m->fat_size_sectors);
235 m->first_data_lba = m->first_root_lba + root_dir_sectors;
236 if (m->total_sectors < m->first_data_lba) {
238 }
239 const uint64_t data_sectors = m->total_sectors - m->first_data_lba;
240 m->count_of_clusters = (uint32_t)(data_sectors / m->sectors_per_cluster);
245 } else {
247 }
248 return k_ra8_ok;
249}
250
271static uint32_t internal_mbr_part0_lba(const uint8_t* buf)
272{
273 if (buf[k_bpb_off_signature_lo] != (uint8_t)k_bpb_sig_lo) {
274 return 0U;
275 }
276 if (buf[k_bpb_off_signature_hi] != (uint8_t)k_bpb_sig_hi) {
277 return 0U;
278 }
279 if (buf[k_mbr_off_part0_type] == 0U) {
280 return 0U;
281 }
282 return priv_rd32(&buf[k_mbr_off_part0_lba]);
283}
284
310static ra8_err_t internal_mbr_select_entry(const uint8_t* buf, uint8_t index, uint64_t* out_base)
311{
312 if ((uint32_t)index >= (uint32_t)k_mbr_part_entry_count) {
314 }
315 const uint32_t slot = (uint32_t)index * (uint32_t)k_mbr_part_entry_stride;
316 const uint32_t type_off = (uint32_t)k_mbr_off_part0_type + slot;
317 const uint32_t lba_off = (uint32_t)k_mbr_off_part0_lba + slot;
318 if (buf[type_off] == 0U) {
319 return k_ra8_err_not_found;
320 }
321 const uint32_t base = priv_rd32(&buf[lba_off]);
322 if (base == 0U) {
324 }
325 *out_base = base;
326 return k_ra8_ok;
327}
328
359static ra8_err_t internal_locate_indexed(const ra8_fs_mount_t* m, uint8_t index, uint64_t* out_base)
360{
362 return k_ra8_err_not_found;
363 }
365 return k_ra8_err_not_found;
366 }
368 return priv_gpt_locate_partition(m, index, out_base);
369 }
370 return internal_mbr_select_entry(g_fs_scratch, index, out_base);
371}
372
400RA8_EXPECTS_LOCK("ra8_fs_lock")
402 const ra8_fs_format_opts_t* opts)
403{
404 if (backend == nullptr || opts == nullptr) {
405 return k_ra8_err_null_ptr;
406 }
407 if (backend->write_block == nullptr || backend->get_capacity == nullptr) {
408 return k_ra8_err_invalid_arg;
409 }
410 if (opts->type != k_ra8_fs_type_fat12 && opts->type != k_ra8_fs_type_fat16 &&
411 opts->type != k_ra8_fs_type_fat32 && opts->type != k_ra8_fs_type_exfat) {
412 return k_ra8_err_not_supported;
413 }
414 if (!priv_fmt_spc_valid(opts->sectors_per_cluster)) {
415 return k_ra8_err_invalid_arg;
416 }
417 uint64_t block_count = 0U;
418 uint32_t block_size = 0U;
419 ra8_err_t err = backend->get_capacity(backend->ctx, &block_count, &block_size);
420 if (err != k_ra8_ok) {
421 return err;
422 }
423 if ((internal_bps_valid(block_size) == 0U) || (block_count == 0U)) {
425 }
426 if (opts->type == k_ra8_fs_type_exfat) {
427 return priv_exfat_format(backend, block_count, block_size, opts->label);
428 }
429 ra8_fs_fmt_geom_t geom = {};
430 geom.type = opts->type;
431 geom.total_sectors = block_count;
432 geom.bytes_per_sector = block_size;
433 geom.reserved_sectors = priv_fmt_reserved_for(opts->type);
434 geom.root_entries = (opts->type == k_ra8_fs_type_fat32) ? 0U : (uint32_t)k_fmt_root_ents_f16;
435 geom.root_sectors =
436 ((geom.root_entries * (uint32_t)k_ra8_fs_dir_entry_bytes) + (block_size - 1U)) / block_size;
437 err = priv_fmt_choose_geometry(&geom, opts->sectors_per_cluster);
438 if (err != k_ra8_ok) {
439 return err;
440 }
441 return priv_fmt_emit_volume(backend, &geom, opts->label);
442}
443
471{
473 if (err != k_ra8_ok) {
474 return err;
475 }
476 uint64_t base = 0U;
477 if (index != (uint8_t)k_ra8_fs_partition_auto) {
478 const ra8_err_t lerr = internal_locate_indexed(m, index, &base);
479 if (lerr != k_ra8_ok) {
480 return lerr;
481 }
482 } else {
483 err = priv_parse_volume(m);
484 if (err == k_ra8_ok) {
485 return k_ra8_ok;
486 }
488 if (base == 0U) {
489 return err;
490 }
492 const ra8_err_t gpt_err = priv_gpt_locate_volume(m, &base);
493 if (gpt_err != k_ra8_ok) {
494 return gpt_err;
495 }
496 }
497 }
498 m->partition_base_lba = base;
499 err = priv_read_sector(m, 0, g_fs_scratch);
500 if (err != k_ra8_ok) {
501 return err;
502 }
503 return priv_parse_volume(m);
504}
505
534{
535 uint64_t dev_blocks = 0U;
536 uint32_t dev_bps = 0U;
537 const ra8_err_t cerr = backend->get_capacity(backend->ctx, &dev_blocks, &dev_bps);
538 if (cerr != k_ra8_ok) {
539 return cerr;
540 }
541 if (internal_bps_valid(dev_bps) == 0U) {
543 }
544 m->bytes_per_sector = dev_bps;
545 return k_ra8_ok;
546}
547
559RA8_EXPECTS_LOCK("ra8_fs_lock")
561{
562 if (backend == nullptr) {
563 return k_ra8_err_null_ptr;
564 }
565 if (out_type == nullptr) {
566 return k_ra8_err_null_ptr;
567 }
568 if (backend->read_block == nullptr) {
569 return k_ra8_err_invalid_arg;
570 }
571 if (backend->get_capacity == nullptr) {
572 return k_ra8_err_invalid_arg;
573 }
574 ra8_fs_mount_t probe = {};
575 probe.backend = *backend;
576 ra8_err_t err = internal_mount_probe_bps(backend, &probe);
577 if (err != k_ra8_ok) {
578 return err;
579 }
581 if (err != k_ra8_ok) {
582 return err;
583 }
584 if (probe.type != k_ra8_fs_type_exfat) {
585 err = internal_compute_geometry(&probe);
586 if (err != k_ra8_ok) {
587 return err;
588 }
589 }
590 *out_type = probe.type;
591 return k_ra8_ok;
592}
593
627RA8_EXPECTS_LOCK("ra8_fs_lock")
628static ra8_err_t
629internal_mount_locked(const ra8_fs_backend_t* backend, uint8_t index, ra8_fs_mount_t** out_handle)
630{
631 if (backend == nullptr || out_handle == nullptr) {
632 return k_ra8_err_null_ptr;
633 }
634 if (backend->read_block == nullptr || backend->write_block == nullptr ||
635 backend->get_capacity == nullptr) {
636 return k_ra8_err_invalid_arg;
637 }
639 if (m == nullptr) {
640 return k_ra8_err_no_mem;
641 }
642 /* Zero the whole slot on claim (#684). The mount table is a fixed array, so
643 * this ra8_fs_mount_t address has very likely served another volume already,
644 * and the FAT and exFAT parse paths each populate a DIFFERENT subset of the
645 * struct. Clearing every field here makes "repopulate everything" a structural
646 * invariant instead of something each future field author must remember: no
647 * value can survive a card-type change into a field the new parse never
648 * touches. `in_use` is set last, on success. */
649 *m = (ra8_fs_mount_t){};
650 m->backend = *backend;
651 m->partition_base_lba = 0U;
652 const ra8_err_t cerr = internal_mount_probe_bps(backend, m);
653 if (cerr != k_ra8_ok) {
654 return cerr;
655 }
656 /* The mount table is a fixed array, so this is very likely the slot some
657 * earlier volume used. Both fields below are reset for the same reason the
658 * allocator's own state is (see ra8_fs_fat_alloc_internal.h): a slot handed
659 * out again carrying the last volume's answers hands them to this one.
660 * `exfat_upcase_ok` says "this build's up-case table is the one this VOLUME
661 * carries", and a fresh mount vouches for nothing until it has looked (#606). */
662 m->exfat_upcase_ok = 0U;
663 ra8_err_t err = internal_read_boot_sector(m, index);
664 if (err != k_ra8_ok) {
665 return err;
666 }
667 /* exFAT geometry is parsed directly in priv_exfat_parse; the FAT-BPB
668 * geometry computation applies only to FAT12/16/32. */
669 if (m->type != k_ra8_fs_type_exfat) {
671 if (err != k_ra8_ok) {
672 return err;
673 }
674 }
675 /* Claim a fresh allocator slot BEFORE seeding it: the mount table is a fixed
676 * array, so this ra8_fs_mount_t address has very likely served another volume
677 * already, and its cached FAT sector and next-free hint describe that disk. */
679 err = priv_fsinfo_seed(m);
680 if (err != k_ra8_ok) {
682 return err;
683 }
684 m->in_use = 1;
685 *out_handle = m;
686 return k_ra8_ok;
687}
688
717RA8_EXPECTS_LOCK("ra8_fs_lock")
719{
720 if (handle == nullptr) {
721 return k_ra8_err_null_ptr;
722 }
723 if (handle->in_use == 0U) {
724 return k_ra8_err_invalid_state;
725 }
726 /* Last chance to make the on-disk free count true (#607). Reported, not
727 * swallowed: a card whose FSInfo could not be updated is a card a desktop
728 * will report the wrong free space for, and the caller is the only one who
729 * can decide whether that matters. The slot is released either way, so a
730 * failed flush cannot strand the mount. */
731 const ra8_err_t ferr = priv_fsinfo_flush(handle);
733 handle->in_use = 0;
734 handle->type = k_ra8_fs_type_unknown;
735 return ferr;
736}
737
738/* =============================================================================
739 * Public entry points -- the lock brackets
740 * =============================================================================
741 */
742
743RA8_OWNS_RESOURCE("ra8_fs_lock")
745{
747 const ra8_err_t err = internal_format_locked(backend, opts);
749 return err;
750}
751
752RA8_OWNS_RESOURCE("ra8_fs_lock")
754{
756 const ra8_err_t err = internal_probe_locked(backend, out_type);
758 return err;
759}
760
761RA8_OWNS_RESOURCE("ra8_fs_lock")
763{
765 const ra8_err_t err =
766 internal_mount_locked(backend, (uint8_t)k_ra8_fs_partition_auto, out_handle);
768 return err;
769}
770
771RA8_OWNS_RESOURCE("ra8_fs_lock")
773ra8_fs_mount_partition(const ra8_fs_backend_t* backend, uint8_t index, ra8_fs_mount_t** out_handle)
774{
776 const ra8_err_t err = internal_mount_locked(backend, index, out_handle);
778 return err;
779}
780
781RA8_OWNS_RESOURCE("ra8_fs_lock")
783{
785 const ra8_err_t err = internal_unmount_locked(handle);
787 return err;
788}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_OWNS_RESOURCE(kind)
RAII-style resource ownership contract.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_EXPECTS_LOCK(name)
The function expects the named thread/IRQ lock to be held on entry.
#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_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
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).
ra8_err_t ra8_fs_format(const ra8_fs_backend_t *backend, const ra8_fs_format_opts_t *opts)
Format a block device as a fresh, empty FAT12/FAT16/FAT32/exFAT volume.
ra8_err_t ra8_fs_mount(const ra8_fs_backend_t *backend, ra8_fs_mount_t **out_handle)
Mount a FAT volume from a block-device backend, auto-selecting the first partition.
ra8_err_t ra8_fs_mount_partition(const ra8_fs_backend_t *backend, uint8_t index, ra8_fs_mount_t **out_handle)
Mount a FAT/exFAT volume from a specific partition of a block device.
ra8_err_t ra8_fs_unmount(ra8_fs_mount_t *handle)
Unmount a previously mounted volume and release its slot.
ra8_err_t ra8_fs_probe(const ra8_fs_backend_t *backend, ra8_fs_type_t *out_type)
Identify a FAT/exFAT volume without claiming a mount slot.
uint32_t priv_rd32(const uint8_t *p)
Decode a little-endian uint32_t from a byte buffer.
Definition ra8_fs_fat.c:48
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
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
ra8_err_t priv_fsinfo_seed(const ra8_fs_mount_t *m)
Validate FAT32's FSInfo sector and seed the hint and free count.
void priv_alloc_state_bind(const ra8_fs_mount_t *m)
Claim and reset the allocator state slot for a freshly mounted volume.
ra8_err_t priv_fsinfo_flush(const ra8_fs_mount_t *m)
Write the tracked free count and next-free hint back into FSInfo.
void priv_alloc_state_release(const ra8_fs_mount_t *m)
Release the allocator state slot held by m.
const uint8_t k_zero_sector[k_ra8_fs_sector_max]
One whole sector of zero bytes, in read-only storage.
ra8_err_t priv_parse_volume(ra8_fs_mount_t *m)
Parse the volume at the current base: exFAT first, then FAT BPB.
ra8_err_t priv_fmt_emit_volume(const ra8_fs_backend_t *backend, const ra8_fs_fmt_geom_t *g, const char *label)
Lay down the boot sector, FAT seeds, FSInfo, and the empty root.
ra8_err_t priv_fmt_choose_geometry(ra8_fs_fmt_geom_t *g, uint32_t spc_hint)
Pick the cluster size that lands the FAT cluster count in the right band.
bool priv_fmt_spc_valid(uint8_t spc)
Validate a caller-pinned sectors-per-cluster value.
uint32_t priv_fmt_reserved_for(ra8_fs_type_t type)
Map a requested FAT type to its reserved-sector count.
ra8_err_t priv_gpt_locate_volume(const ra8_fs_mount_t *m, uint64_t *out_base)
Locate the first mountable partition on a GPT disk (auto-select).
ra8_err_t priv_gpt_locate_partition(const ra8_fs_mount_t *m, uint8_t index, uint64_t *out_base)
Locate a GPT partition chosen by entry-array index.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
void priv_lock_release(void)
Drop the library lock taken by priv_lock_acquire.
void priv_lock_acquire(void)
Take the library lock, if the caller installed one.
static ra8_err_t internal_mount_locked(const ra8_fs_backend_t *backend, uint8_t index, ra8_fs_mount_t **out_handle)
Mount a volume – the guarded body of ra8_fs_mount() and ra8_fs_mount_partition().
static ra8_err_t internal_format_locked(const ra8_fs_backend_t *backend, const ra8_fs_format_opts_t *opts)
Lay down a fresh volume – the guarded body of ra8_fs_format().
static ra8_fs_mount_t * internal_alloc_mount_slot(void)
Allocate a free entry from the mount table; returns NULL if full.
static ra8_err_t internal_read_boot_sector(ra8_fs_mount_t *m, uint8_t index)
Read + parse the boot sector, transparently following MBR or GPT.
ra8_fs_file_t * priv_alloc_file_slot(void)
Allocate a free entry from the file table; returns NULL if full.
static ra8_err_t internal_mbr_select_entry(const uint8_t *buf, uint8_t index, uint64_t *out_base)
Return the first LBA of MBR primary entry index.
static ra8_err_t internal_unmount_locked(ra8_fs_mount_t *handle)
Release a mount slot – the guarded body of ra8_fs_unmount().
static ra8_err_t internal_compute_geometry(ra8_fs_mount_t *m)
Compute first_fat_lba, first_root_lba, first_data_lba, count_of_clusters.
static ra8_err_t internal_locate_indexed(const ra8_fs_mount_t *m, uint8_t index, uint64_t *out_base)
Resolve an explicit partition index to its first LBA.
static uint32_t internal_mbr_part0_lba(const uint8_t *buf)
Return partition 0's first LBA from an MBR in buf, or 0.
uint8_t g_fs_scratch[k_ra8_fs_sector_max]
Single max-sector scratch buffer reused across all I/O.
static uint8_t internal_bps_valid(uint32_t bs)
Test whether a backend block size is one this adapter supports.
static uint8_t s_sec_arena[k_fs_sec_roles][k_ra8_fs_sector_max]
The four fixed-role sector bounce buffers (#683).
uint8_t * priv_sec_fat2(void)
The FAT2-role sector buffer (the FAT12 straddle's second sector).
static ra8_fs_mount_t s_mounts[k_ra8_fs_max_mounts]
Mount table – max k_ra8_fs_max_mounts simultaneous volumes.
uint8_t * priv_sec_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
static ra8_err_t internal_probe_locked(const ra8_fs_backend_t *backend, ra8_fs_type_t *out_type)
Parse a backend into temporary state and return only its format type.
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
static ra8_err_t internal_mount_probe_bps(const ra8_fs_backend_t *backend, ra8_fs_mount_t *m)
Probe the backend's block size into a claimed mount slot.
uint8_t * priv_sec_fat(void)
The FAT-role sector buffer (priv_fat_get and the FAT setters).
static ra8_fs_file_t s_files[k_ra8_fs_max_files]
File handle table – max k_ra8_fs_max_files open at once.
ra8_err_t priv_parse_bpb_into_mount(ra8_fs_mount_t *m)
Parse the BPB layout fields out of g_fs_scratch into m.
@ k_mbr_part_entry_count
Primary partition entries in an MBR.
@ k_mbr_off_part0_lba
Partition 0 first-LBA (4 bytes, LE).
@ k_mbr_part_entry_stride
Bytes between primary partition entries.
@ k_mbr_off_part0_type
Partition 0 type byte (0 = unused).
@ k_bpb_sig_hi
Bpb sig hi.
@ k_bpb_sig_lo
Bpb sig lo.
@ k_gpt_part_type_protective
MBR type byte for a GPT disk.
@ k_fs_sec_role_io
Leaf data / bitmap sector transfers.
@ k_fs_sec_roles
Arena row count.
@ k_fs_sec_role_walk
Directory scans and entry read-modify-write.
@ k_fs_sec_role_fat
The sector holding a FAT entry.
@ k_fs_sec_role_fat2
FAT12 straddle: the following sector.
@ k_fmt_root_ents_f16
FAT12/16 root-directory entries.
@ k_cluster_count_fat12_max
MS FAT spec sec 3.5 boundary.
@ k_cluster_count_fat16_max
MS FAT spec sec 3.5 boundary.
@ k_bpb_off_tot_sec_32
MS FAT spec sec 3.1 "BPB_TotSec32".
@ k_bpb_off_root_ent_cnt
MS FAT spec sec 3.1 "BPB_RootEntCnt".
@ k_bpb_off_tot_sec_16
MS FAT spec sec 3.1 "BPB_TotSec16".
@ k_bpb_off_bytes_per_sec
MS FAT spec sec 3.1 "BPB_BytsPerSec".
@ k_bpb_off_signature_lo
Boot sig byte 1 (0x55).
@ k_bpb_off_num_fats
MS FAT spec sec 3.1 "BPB_NumFATs".
@ k_bpb_off_root_clus
MS FAT spec sec 3.5 "BPB_RootClus".
@ k_bpb_off_rsvd_sec_cnt
MS FAT spec sec 3.1 "BPB_RsvdSecCnt".
@ k_bpb_off_fat_sz_32
MS FAT spec sec 3.5 "BPB_FATSz32".
@ k_bpb_off_sec_per_clus
MS FAT spec sec 3.1 "BPB_SecPerClus".
@ k_bpb_off_fat_sz_16
MS FAT spec sec 3.1 "BPB_FATSz16".
@ k_bpb_off_signature_hi
Boot sig byte 2 (0xAA).
@ k_ra8_fs_partition_auto
Auto-select the first mountable partition.
ra8_fs_type_t
FAT variant detected from the BPB cluster-count rule.
@ k_ra8_fs_type_fat12
count_of_clusters < 4085.
@ k_ra8_fs_type_fat32
count_of_clusters >= 65525.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_type_fat16
4085 <= count_of_clusters < 65525.
@ k_ra8_fs_type_unknown
Not yet detected / mount failed.
@ k_ra8_fs_max_mounts
Max concurrent mount points.
@ k_ra8_fs_max_files
Max concurrent open file handles.
@ k_ra8_fs_dir_entry_bytes
MS FAT spec sec 6 "Directory Entry".
@ k_ra8_fs_sector_max
Largest supported sector size (4Kn).
@ k_ra8_fs_sector_min
Smallest supported sector size.
Block-device interface that ra8_fs runs on top of.
void * ctx
Caller-owned context passed back into the function pointers.
ra8_err_t(* get_capacity)(void *ctx, uint64_t *block_count, uint32_t *block_size)
Report the device size.
Open-file state.
Computed on-disk geometry for one ra8_fs_format() run.
ra8_fs_type_t type
Resolved FAT variant.
Tunables for ra8_fs_format() (the on-disk geometry of a fresh volume).
Cached parse of one mounted FAT volume.
uint32_t reserved_sectors
BPB BPB_RsvdSecCnt.
uint32_t root_cluster
BPB BPB_RootClus (FAT32 only).
ra8_fs_type_t type
FAT12 / FAT16 / FAT32.
uint32_t bytes_per_sector
Sector size (BPB / VBR == backend).
uint32_t root_entries
BPB BPB_RootEntCnt (FAT12/16).
uint64_t total_sectors
BPB TotSec / exFAT VolumeLength.
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 first_root_lba
FAT12/16 fixed root-dir start.
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.