ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_fmt.c
Go to the documentation of this file.
1
13
14#include <stddef.h>
15#include <stdint.h>
16
17#include "ra8_attributes.h"
18#include "ra8_fs.h"
19#include "ra8_fs_fat_internal.h"
20
21/* =============================================================================
22 * Public API: format (mkfs)
23 * =============================================================================
24 */
25
26/* `priv_fmt_reserved_for()`: see header for the documented contract. */
28{
29 return (type == k_ra8_fs_type_fat32) ? (uint32_t)k_fmt_resv_f32 : (uint32_t)k_fmt_resv_f16;
30}
31
60static uint64_t
61internal_fmt_clusters_for(const ra8_fs_fmt_geom_t* g, uint32_t spc, uint32_t* out_fatsz)
62{
63 /* FAT entries per DEVICE sector: 4-byte FAT32 entries or 2-byte FAT16 ones
64 * (FAT12 sizes its FAT with the FAT16 figure, a deliberate overestimate). */
65 const uint32_t entry_bytes = (g->type == k_ra8_fs_type_fat32) ? (uint32_t)k_fmt_fat32_entry_bytes
66 : (uint32_t)k_fmt_fat16_entry_bytes;
67 const uint32_t entry_cap = g->bytes_per_sector / entry_bytes;
68 const uint64_t overhead = (uint64_t)g->reserved_sectors + g->root_sectors;
69 if (g->total_sectors <= overhead) {
70 *out_fatsz = 0U;
71 return 0U;
72 }
73 const uint64_t avail = g->total_sectors - overhead;
74 const uint64_t denom = ((uint64_t)entry_cap * spc) + (uint32_t)k_fmt_num_fats;
75 const uint64_t fatsz = (avail + denom - 1U) / denom;
76 *out_fatsz = (uint32_t)fatsz;
77 const uint64_t fat_total = (uint64_t)k_fmt_num_fats * fatsz;
78 if (avail <= fat_total) {
79 return 0U;
80 }
81 return (avail - fat_total) / spc;
82}
83
108static bool internal_fmt_count_in_band(ra8_fs_type_t type, uint64_t count)
109{
110 if (type == k_ra8_fs_type_fat12) {
111 return count < (uint32_t)k_cluster_count_fat12_max;
112 }
113 if (type == k_ra8_fs_type_fat16) {
114 return (count >= (uint32_t)k_cluster_count_fat12_max) &&
115 (count < (uint32_t)k_cluster_count_fat16_max);
116 }
117 return (count >= (uint32_t)k_cluster_count_fat16_max) &&
118 (count <= (uint32_t)k_fmt_fat32_clus_cap);
119}
120
143static uint32_t internal_fmt_log2(uint32_t v)
144{
145 uint32_t r = 0U;
146 uint32_t w = v;
147 while (w > 1U) {
148 w >>= 1U;
149 r++;
150 }
151 return r;
152}
153
185static uint32_t internal_fmt_fat32_default_spc(uint64_t total_sectors, uint32_t bps)
186{
187 const uint32_t bps_shift = internal_fmt_log2(bps);
188 const uint64_t total_512 = total_sectors
189 << (bps_shift - internal_fmt_log2((uint32_t)k_ra8_fs_sector_min));
190 uint32_t clus_shift = (uint32_t)k_fmt_f32_clus_32k;
191 if (total_512 <= (uint64_t)k_fmt_f32_thr_260m) {
192 clus_shift = (uint32_t)k_fmt_f32_clus_512b;
193 } else if (total_512 <= (uint64_t)k_fmt_f32_thr_8g) {
194 clus_shift = (uint32_t)k_fmt_f32_clus_4k;
195 } else if (total_512 <= (uint64_t)k_fmt_f32_thr_16g) {
196 clus_shift = (uint32_t)k_fmt_f32_clus_8k;
197 } else if (total_512 <= (uint64_t)k_fmt_f32_thr_32g) {
198 clus_shift = (uint32_t)k_fmt_f32_clus_16k;
199 } else {
200 clus_shift = (uint32_t)k_fmt_f32_clus_32k;
201 }
202 if (clus_shift <= bps_shift) {
203 return 1U;
204 }
205 return 1U << (clus_shift - bps_shift);
206}
207
208/* `priv_fmt_choose_geometry()`: see header for the documented contract. */
210{
211 /* FAT's BPB records the volume size in a 32-bit field: a device whose
212 * sector count does not fit cannot be described by the format at all --
213 * exFAT is the right choice there, so this fails cleanly instead of
214 * truncating the size (#683). */
215 if (g->total_sectors > (uint64_t)UINT32_MAX) {
217 }
218 bool auto_mode = (spc_hint == 0U);
219 uint32_t spc = spc_hint;
220 if (auto_mode) {
221 /* FAT32 starts the sweep at the size-appropriate cluster (MS table) so a
222 * big card gets a small FAT; FAT12/16 start at the minimum and grow. */
223 spc = (g->type == k_ra8_fs_type_fat32)
225 : 1U;
226 }
227 for (uint32_t guard = 0U; guard <= (uint32_t)k_fmt_spc_max; guard++) {
228 uint32_t fatsz = 0U;
229 const uint64_t count = internal_fmt_clusters_for(g, spc, &fatsz);
230 if (internal_fmt_count_in_band(g->type, count)) {
231 g->sectors_per_cluster = spc;
232 g->fat_size_sectors = fatsz;
233 g->count_of_clusters = (uint32_t)count;
234 return k_ra8_ok;
235 }
236 if (!auto_mode || spc >= (uint32_t)k_fmt_spc_max) {
238 }
239 spc *= 2U;
240 }
242}
243
262static void internal_fmt_boot_prologue(uint8_t* sec)
263{
264 static const uint8_t k_oem[k_filename_base_len] = {'M', 'S', 'D', 'O', 'S', '5', '.', '0'};
265 sec[k_fmt_off_jmp0] = (uint8_t)k_fmt_jmp_byte0;
266 sec[k_fmt_off_jmp1] = (uint8_t)k_fmt_jmp_byte1;
267 sec[k_fmt_off_jmp2] = (uint8_t)k_fmt_jmp_byte2;
268 priv_byte_copy(&sec[k_fmt_off_oem], k_oem, (uint32_t)k_filename_base_len);
269}
270
271/* `priv_fmt_label_field()`: see header for the documented contract. */
272void priv_fmt_label_field(uint8_t* dst, const char* label)
273{
274 /* An unlabelled FAT volume stores the spec sentinel "NO NAME ", never
275 * zeros and never a bare run of spaces: fsck.fat reads a blank BS_VolLab as a
276 * corrupt label and strips it on sight, which trains people to ignore fsck
277 * output on every card this firmware writes (#634). A NULL or empty label
278 * therefore resolves to that sentinel before padding. */
279 static const char k_no_name[] = "NO NAME";
280 const char* eff = ((label != nullptr) && (label[0] != '\0')) ? label : k_no_name;
281 bool past_end = false;
282 for (uint32_t i = 0U; i < (uint32_t)k_fmt_label_len; i++) {
283 if (!past_end && (eff[i] == '\0')) {
284 past_end = true;
285 }
286 dst[i] = past_end ? (uint8_t)' ' : (uint8_t)eff[i];
287 }
288}
289
311static void internal_fmt_write_totals(uint8_t* sec, const ra8_fs_fmt_geom_t* g)
312{
313 if (g->type == k_ra8_fs_type_fat32) {
314 priv_wr32(&sec[k_bpb_off_tot_sec_32], (uint32_t)g->total_sectors);
316 return;
317 }
318 if (g->total_sectors < (uint64_t)(k_word_mask + 1U)) {
319 priv_wr16(&sec[k_bpb_off_tot_sec_16], (uint16_t)g->total_sectors);
320 } else {
321 priv_wr32(&sec[k_bpb_off_tot_sec_32], (uint32_t)g->total_sectors);
322 }
324}
325
349static void internal_fmt_build_bpb_f16(uint8_t* sec, const ra8_fs_fmt_geom_t* g, const char* label)
350{
351 static const uint8_t k_t12[k_filename_base_len] = {'F', 'A', 'T', '1', '2', ' ', ' ', ' '};
352 static const uint8_t k_t16[k_filename_base_len] = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '};
357 sec[k_bpb_off_num_fats] = (uint8_t)k_fmt_num_fats;
358 priv_wr16(&sec[k_bpb_off_root_ent_cnt], (uint16_t)g->root_entries);
359 sec[k_fmt_off_media] = (uint8_t)k_fmt_media_fixed;
365 priv_wr32(&sec[k_fmt_off_f16_volid], (uint32_t)k_fmt_volid_base | (uint32_t)g->total_sectors);
368 (g->type == k_ra8_fs_type_fat12) ? k_t12 : k_t16,
369 (uint32_t)k_filename_base_len);
370 sec[k_bpb_off_signature_lo] = (uint8_t)k_bpb_sig_lo;
371 sec[k_bpb_off_signature_hi] = (uint8_t)k_bpb_sig_hi;
372}
373
396static void internal_fmt_build_bpb_f32(uint8_t* sec, const ra8_fs_fmt_geom_t* g, const char* label)
397{
398 static const uint8_t k_t32[k_filename_base_len] = {'F', 'A', 'T', '3', '2', ' ', ' ', ' '};
403 sec[k_bpb_off_num_fats] = (uint8_t)k_fmt_num_fats;
404 sec[k_fmt_off_media] = (uint8_t)k_fmt_media_fixed;
413 priv_wr32(&sec[k_fmt_off_f32_volid], (uint32_t)k_fmt_volid_base | (uint32_t)g->total_sectors);
416 sec[k_bpb_off_signature_lo] = (uint8_t)k_bpb_sig_lo;
417 sec[k_bpb_off_signature_hi] = (uint8_t)k_bpb_sig_hi;
418}
419
421static const uint8_t s_fmt_zero_chunk[k_fmt_zero_chunk_bytes] = {};
422
452static ra8_err_t
453internal_fmt_zero_run(const ra8_fs_backend_t* backend, uint64_t lba, uint64_t count, uint32_t bps)
454{
455 const uint32_t chunk_secs = (uint32_t)k_fmt_zero_chunk_bytes / bps;
456 uint64_t done = 0U;
457 while (done < count) {
458 uint32_t chunk = chunk_secs;
459 if ((uint64_t)chunk > (count - done)) {
460 chunk = (uint32_t)(count - done);
461 }
462 const ra8_err_t err = backend->write_block(backend->ctx, lba + done, chunk, s_fmt_zero_chunk);
463 if (err != k_ra8_ok) {
464 return err;
465 }
466 done += chunk;
467 }
468 return k_ra8_ok;
469}
470
471/* `priv_fmt_clear_region()`: see header for the documented contract. */
473priv_fmt_clear_region(const ra8_fs_backend_t* backend, uint64_t lba, uint64_t count, uint32_t bps)
474{
475 if ((backend->erase_blocks != nullptr) &&
476 (backend->erase_blocks(backend->ctx, lba, count) == k_ra8_ok)) {
477 return k_ra8_ok; /* erased and verified zero -- skip the zero-write */
478 }
479 /* No erase hook, or erase could not guarantee a zero read-back: write zeros. */
480 return internal_fmt_zero_run(backend, lba, count, bps);
481}
482
510{
511 for (uint32_t i = 0U; i < g->bytes_per_sector; i++) {
512 g_fs_scratch[i] = 0U;
513 }
514 if (g->type == k_ra8_fs_type_fat32) {
518 } else if (g->type == k_ra8_fs_type_fat16) {
520 (uint16_t)((uint32_t)k_cluster_eoc_min_fat16 | (uint32_t)k_fmt_media_fixed));
522 } else {
523 /* FAT12: 1.5-byte entries. FAT[0]=0xF<media>, FAT[1]=0xFFF, packed LE. */
524 g_fs_scratch[0] = (uint8_t)k_fmt_media_fixed;
525 g_fs_scratch[1] = (uint8_t)k_byte_mask;
526 g_fs_scratch[2] = (uint8_t)k_byte_mask;
527 }
528 for (uint32_t f = 0U; f < (uint32_t)k_fmt_num_fats; f++) {
529 const uint64_t fat_lba = (uint64_t)g->reserved_sectors + ((uint64_t)f * g->fat_size_sectors);
530 const ra8_err_t err = backend->write_block(backend->ctx, fat_lba, 1U, g_fs_scratch);
531 if (err != k_ra8_ok) {
532 return err;
533 }
534 }
535 return k_ra8_ok;
536}
537
564 const ra8_fs_fmt_geom_t* g,
565 const uint8_t* boot_sec)
566{
567 if (g->type != k_ra8_fs_type_fat32) {
568 return k_ra8_ok;
569 }
570 for (uint32_t i = 0U; i < g->bytes_per_sector; i++) {
571 g_fs_scratch[i] = 0U;
572 }
575 const uint32_t free_count =
576 (g->count_of_clusters > 0U) ? (g->count_of_clusters - 1U) : (uint32_t)k_fmt_fsi_unknown;
580 ra8_err_t err =
581 backend->write_block(backend->ctx, (uint64_t)k_fmt_fsinfo_sector, 1U, g_fs_scratch);
582 if (err != k_ra8_ok) {
583 return err;
584 }
585 return backend->write_block(backend->ctx, (uint64_t)k_fmt_bkboot_sector, 1U, boot_sec);
586}
587
588/* `priv_fmt_spc_valid()`: see header for the documented contract. */
589bool priv_fmt_spc_valid(uint8_t spc)
590{
591 if (spc == 0U) {
592 return true;
593 }
594 if (spc > (uint32_t)k_fmt_spc_max) {
595 return false;
596 }
597 return ((uint32_t)spc & ((uint32_t)spc - 1U)) == 0U;
598}
599
600/* `priv_fmt_emit_volume()`: see header for the documented contract. */
602priv_fmt_emit_volume(const ra8_fs_backend_t* backend, const ra8_fs_fmt_geom_t* g, const char* label)
603{
604 uint8_t* const boot = priv_sec_walk();
605 priv_byte_fill(boot, 0U, g->bytes_per_sector);
606 if (g->type == k_ra8_fs_type_fat32) {
607 internal_fmt_build_bpb_f32(boot, g, label);
608 } else {
609 internal_fmt_build_bpb_f16(boot, g, label);
610 }
611 /* Clear the FAT region plus the (contiguous) root region first, so no stale
612 * non-zero FAT word survives as an orphan cluster. Erase the whole span in
613 * one go when the card guarantees zero-after-erase, else stream zeros. Doing
614 * this BEFORE the metadata writes means an erase that rounds into the reserved
615 * region cannot clobber the boot sector / FSInfo written below. */
616 const uint64_t fat_total = (uint64_t)k_fmt_num_fats * g->fat_size_sectors;
617 const uint64_t root_span =
619 ra8_err_t err =
620 priv_fmt_clear_region(backend, g->reserved_sectors, fat_total + root_span, g->bytes_per_sector);
621 if (err != k_ra8_ok) {
622 return err;
623 }
624 /* Now lay the metadata: boot sector, FAT seeds (rewrite FAT sector 0 of each
625 * copy with the media + EOC reserved entries), then FAT32 FSInfo + backup. */
626 err = backend->write_block(backend->ctx, 0U, 1U, boot);
627 if (err != k_ra8_ok) {
628 return err;
629 }
630 err = internal_fmt_seed_fats(backend, g);
631 if (err != k_ra8_ok) {
632 return err;
633 }
634 return internal_fmt_write_fsinfo(backend, g, boot);
635}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
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).
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
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
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
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
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
static void internal_fmt_build_bpb_f32(uint8_t *sec, const ra8_fs_fmt_geom_t *g, const char *label)
Build the FAT32 boot sector into the scratch buffer.
static ra8_err_t internal_fmt_write_fsinfo(const ra8_fs_backend_t *backend, const ra8_fs_fmt_geom_t *g, const uint8_t *boot_sec)
Write the FAT32 FSInfo sector (and the backup boot sector copy).
static ra8_err_t internal_fmt_seed_fats(const ra8_fs_backend_t *backend, const ra8_fs_fmt_geom_t *g)
Seed the reserved FAT entries (and FAT32 root-cluster EOC) per copy.
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.
static const uint8_t s_fmt_zero_chunk[k_fmt_zero_chunk_bytes]
All-zero source for the chunked FAT/root wipe (read-only, in flash).
static uint32_t internal_fmt_fat32_default_spc(uint64_t total_sectors, uint32_t bps)
Default FAT32 cluster size for a device, per the Microsoft table.
static void internal_fmt_build_bpb_f16(uint8_t *sec, const ra8_fs_fmt_geom_t *g, const char *label)
Build the FAT12/FAT16 boot sector into the scratch buffer.
static uint32_t internal_fmt_log2(uint32_t v)
log2 of a power-of-two value.
static bool internal_fmt_count_in_band(ra8_fs_type_t type, uint64_t count)
Test whether a cluster count is valid for a given FAT type.
static ra8_err_t internal_fmt_zero_run(const ra8_fs_backend_t *backend, uint64_t lba, uint64_t count, uint32_t bps)
Zero a run of sectors on the backend in multi-sector chunks.
ra8_err_t priv_fmt_clear_region(const ra8_fs_backend_t *backend, uint64_t lba, uint64_t count, uint32_t bps)
Clear count blocks at lba to zero – bulk-erase if the backend can.
static void internal_fmt_write_totals(uint8_t *sec, const ra8_fs_fmt_geom_t *g)
Write a totals/FAT-size pair into the BPB, choosing 16- vs 32-bit.
bool priv_fmt_spc_valid(uint8_t spc)
Validate a caller-pinned sectors-per-cluster value.
void priv_fmt_label_field(uint8_t *dst, const char *label)
Pad an ASCII volume label into an 11-byte BS_VolLab / label field.
static uint64_t internal_fmt_clusters_for(const ra8_fs_fmt_geom_t *g, uint32_t spc, uint32_t *out_fatsz)
Compute the cluster count for a trial cluster size.
uint32_t priv_fmt_reserved_for(ra8_fs_type_t type)
Map a requested FAT type to its reserved-sector count.
static void internal_fmt_boot_prologue(uint8_t *sec)
Write the shared boot prologue (jump + OEM) into the scratch sector.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
uint8_t g_fs_scratch[k_ra8_fs_sector_max]
Single max-sector scratch buffer reused across all I/O.
@ k_fmt_off_f16_volid
FAT12/16 BS_VolID (4 bytes).
@ k_fmt_off_jmp1
Jump-boot byte 1 (offset).
@ k_fmt_off_media
BPB_Media descriptor.
@ k_fmt_fsi_off_free
FSInfo FSI_Free_Count offset.
@ k_fmt_off_f16_drvnum
FAT12/16 BS_DrvNum.
@ k_fmt_off_f32_bkboot
FAT32 BPB_BkBootSec (backup boot).
@ k_fmt_fsi_off_struct
FSInfo FSI_StrucSig offset.
@ k_fmt_off_f32_fsinfo
FAT32 BPB_FSInfo sector number.
@ k_fmt_off_f16_bootsig
FAT12/16 BS_BootSig (0x29).
@ k_fmt_off_oem
OEM name field (8 bytes).
@ k_fmt_fsi_off_nxtfree
FSInfo FSI_Nxt_Free offset.
@ k_fmt_off_f32_fstype
FAT32 BS_FilSysType (8 bytes).
@ k_fmt_off_f32_label
FAT32 BS_VolLab (11 bytes).
@ k_fmt_off_sec_per_trk
BPB_SecPerTrk.
@ k_fmt_off_f16_fstype
FAT12/16 BS_FilSysType (8 bytes).
@ k_fmt_off_num_heads
BPB_NumHeads.
@ k_fmt_off_jmp2
Jump-boot byte 2 (0x90 NOP).
@ k_fmt_off_f32_bootsig
FAT32 BS_BootSig (0x29).
@ k_fmt_off_f32_drvnum
FAT32 BS_DrvNum.
@ k_fmt_off_f32_volid
FAT32 BS_VolID (4 bytes).
@ k_fmt_off_f16_label
FAT12/16 BS_VolLab (11 bytes).
@ k_fmt_fsi_off_lead
FSInfo FSI_LeadSig offset.
@ k_fmt_fsi_off_trail
FSInfo FSI_TrailSig offset.
@ k_fmt_off_jmp0
Jump-boot byte 0 (0xEB).
@ k_bpb_sig_hi
Bpb sig hi.
@ k_bpb_sig_lo
Bpb sig lo.
@ k_fmt_f32_thr_16g
<= 16 GB -> 8 KB clusters.
@ k_fmt_fat16_entry_bytes
Bytes per FAT16 (and FAT12-sizing) entry.
@ k_fmt_f32_clus_512b
log2(512): <= 260 MB cards.
@ k_fmt_f32_thr_260m
<= 260 MB -> 512 B clusters.
@ k_fmt_media_fixed
Media descriptor: non-removable.
@ k_fmt_label_len
Volume-label field width (bytes).
@ k_fmt_num_fats
Two FAT copies, like every mkfs.
@ k_fmt_fsi_lead_sig
FSInfo lead signature "RRaA".
@ k_fmt_resv_f16
FAT12/16 reserved (boot) sectors.
@ k_fmt_sec_per_trk
Conventional CHS sectors/track.
@ k_fmt_f32_clus_8k
log2(8 KB): <= 16 GB cards.
@ k_fmt_num_heads
Conventional CHS heads.
@ k_fmt_fat32_clus_cap
Max FAT32 cluster count we accept.
@ k_fmt_f32_clus_16k
log2(16 KB): <= 32 GB cards.
@ k_fmt_spc_max
Largest sectors-per-cluster we set.
@ k_fmt_fsi_trail_sig
FSInfo trailing signature.
@ k_fmt_jmp_byte1
Jump displacement (to +0x5A).
@ k_fmt_drvnum_hd
BS_DrvNum: first fixed disk.
@ k_fmt_bkboot_sector
FAT32 backup boot sector LBA.
@ k_fmt_fat32_entry_bytes
Bytes per FAT32 FAT entry.
@ k_fmt_volid_base
Arbitrary volume-serial base.
@ k_fmt_root_clus_f32
FAT32 root directory cluster.
@ k_fmt_f32_clus_32k
log2(32 KB): > 32 GB cards.
@ k_fmt_fat32_nxt_free
First allocatable FAT32 cluster.
@ k_fmt_f32_thr_32g
<= 32 GB -> 16 KB clusters.
@ k_fmt_fsi_struct_sig
FSInfo struct signature "rrAa".
@ k_fmt_f32_thr_8g
<= 8 GB -> 4 KB clusters.
@ k_fmt_fsinfo_sector
FAT32 FSInfo sector LBA.
@ k_fmt_resv_f32
FAT32 reserved sectors.
@ k_fmt_jmp_byte2
NOP padding.
@ k_fmt_ext_bootsig
Extended boot signature present.
@ k_fmt_zero_chunk_bytes
Zero-fill bounce buffer size.
@ k_fmt_f32_clus_4k
log2(4 KB): <= 8 GB cards.
@ k_fmt_jmp_byte0
Short jump opcode.
@ k_fmt_fsi_unknown
FSInfo free-count "unknown".
@ 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_fat16
MS FAT spec sec 4.1 EOC threshold.
@ k_cluster_eoc_write_fat16
Cluster eoc write fat16.
@ 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_byte_mask
Byte mask.
@ k_filename_base_len
Filename base length.
@ k_word_mask
Word mask.
@ 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).
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_fat16
4085 <= count_of_clusters < 65525.
@ k_ra8_fs_sector_min
Smallest supported sector size.
Block-device interface that ra8_fs runs on top of.
ra8_err_t(* write_block)(void *ctx, uint64_t lba, uint32_t count, const uint8_t *buf)
Write count consecutive blocks starting at lba.
ra8_err_t(* erase_blocks)(void *ctx, uint64_t lba, uint64_t count)
OPTIONAL: bulk-erase count blocks at lba to all-zero bytes.
void * ctx
Caller-owned context passed back into the function pointers.
Computed on-disk geometry for one ra8_fs_format() run.
uint32_t sectors_per_cluster
Chosen cluster size (sectors).
uint32_t bytes_per_sector
Device sector size in bytes.
uint64_t total_sectors
Whole-device sector count.
uint32_t reserved_sectors
Boot + (FAT32) FSInfo/backup region.
uint32_t count_of_clusters
Resulting data-region cluster count.
ra8_fs_type_t type
Resolved FAT variant.
uint32_t root_sectors
FAT12/16 fixed-root sector span.
uint32_t root_entries
FAT12/16 root entries (0 on FAT32).
uint32_t fat_size_sectors
Sectors per FAT copy.