ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_alloc.c
Go to the documentation of this file.
1
42
43#include <stddef.h>
44#include <stdint.h>
45
46#include "ra8_attributes.h"
47#include "ra8_fs.h"
48#include "ra8_fs_fat_internal.h"
49
50/* =============================================================================
51 * Module state
52 * =============================================================================
53 */
54
71typedef struct {
73 uint32_t next_free;
74 uint32_t free_count;
75 uint32_t fsinfo_lba;
76 uint64_t bitmap_lba;
77 uint8_t dirty;
79
90
91static_assert((uint32_t)k_fs_alloc_slots == (uint32_t)k_ra8_fs_max_mounts,
92 "one allocator state slot per mount slot, or a bind can fail");
93
103static uint8_t s_fat_cache[k_ra8_fs_sector_max] = {};
104
114
124static const ra8_fs_mount_t* s_fat_cache_owner = nullptr;
125
126/* =============================================================================
127 * Slot lookup
128 * =============================================================================
129 */
130
156{
157 for (uint32_t i = 0U; i < (uint32_t)k_fs_alloc_slots; i++) {
158 if (s_alloc[i].owner == m) {
159 return &s_alloc[i];
160 }
161 }
162 return nullptr;
163}
164
165/* `priv_alloc_state_bind()`: see header for the documented contract. */
167{
169 if (st == nullptr) {
170 st = internal_state_for(nullptr);
171 }
172 if (st == nullptr) {
173 return;
174 }
175 st->owner = m;
176 st->next_free = (uint32_t)k_cluster_first_data;
177 st->free_count = (uint32_t)k_fs_free_unknown;
178 st->fsinfo_lba = (uint32_t)k_fs_fsinfo_absent;
179 st->bitmap_lba = (uint64_t)k_fs_bitmap_unknown;
180 st->dirty = 0U;
181}
182
183/* `priv_exfat_bitmap_lba()`: see header for the documented contract. */
185{
186 /* Nested rather than joined: a mount with no bound slot is not a mount with
187 * a cold cache, and there is no input that can produce the first -- every
188 * accessor in this file is total by construction, because there is one slot
189 * per mount slot. Spelling the pair as one compound decision would add a
190 * condition nothing can vary, which is a permanent MC/DC hole. */
192 if (st != nullptr) {
193 if (st->bitmap_lba != (uint64_t)k_fs_bitmap_unknown) {
194 *out_lba = st->bitmap_lba;
195 return k_ra8_ok;
196 }
197 }
198 uint32_t clus = 0U;
199 uint32_t len = 0U;
200 const ra8_err_t e = priv_exfat_find_bitmap(m, &clus, &len);
201 if (e != k_ra8_ok) {
202 return e;
203 }
204 const uint64_t lba = priv_cluster_to_lba(m, clus);
205 if (st != nullptr) {
206 st->bitmap_lba = lba;
207 }
208 *out_lba = lba;
209 return k_ra8_ok;
210}
211
212/* `priv_alloc_state_release()`: see header for the documented contract. */
214{
216 if (st != nullptr) {
217 *st = (fat_alloc_state_t){};
218 }
219 if (s_fat_cache_owner == m) {
221 s_fat_cache_owner = nullptr;
222 }
223}
224
225/* =============================================================================
226 * FAT sector cache
227 * =============================================================================
228 */
229
265static bool internal_lba_is_cacheable(const ra8_fs_mount_t* m, uint64_t lba)
266{
267 return (lba - m->first_fat_lba) < m->fat_size_sectors;
268}
269
270/* `priv_fat_sector_read()`: see header for the documented contract. */
271ra8_err_t priv_fat_sector_read(const ra8_fs_mount_t* m, uint64_t lba, uint8_t* buf)
272{
273 if (!internal_lba_is_cacheable(m, lba)) {
274 return priv_read_sector(m, lba, buf);
275 }
276 if ((s_fat_cache_owner == m) && (s_fat_cache_lba == lba)) {
278 return k_ra8_ok;
279 }
280 const ra8_err_t err = priv_read_sector(m, lba, buf);
281 if (err != k_ra8_ok) {
282 return err;
283 }
285 s_fat_cache_lba = lba;
287 return k_ra8_ok;
288}
289
290/* `priv_fat_sector_wrote()`: see header for the documented contract. */
291void priv_fat_sector_wrote(const ra8_fs_mount_t* m, const uint8_t* buf, uint64_t lba)
292{
293 if (!internal_lba_is_cacheable(m, lba)) {
294 return;
295 }
297 s_fat_cache_lba = lba;
299}
300
301/* =============================================================================
302 * Next-free hint and free-cluster count
303 * =============================================================================
304 */
305
306/* `priv_alloc_hint_get()`: see header for the documented contract. */
308{
310 if (st == nullptr) {
311 return (uint32_t)k_cluster_first_data;
312 }
313 return st->next_free;
314}
315
316/* `priv_alloc_hint_set()`: see header for the documented contract. */
317void priv_alloc_hint_set(const ra8_fs_mount_t* m, uint32_t cluster)
318{
320 if (st == nullptr) {
321 return;
322 }
323 st->next_free = cluster;
324}
325
326/* `priv_alloc_hint_lower()`: see header for the documented contract. */
327void priv_alloc_hint_lower(const ra8_fs_mount_t* m, uint32_t cluster)
328{
330 if (st == nullptr) {
331 return;
332 }
333 if (cluster < st->next_free) {
334 st->next_free = cluster;
335 }
336}
337
338/* `priv_free_count_took()`: see header for the documented contract. */
339void priv_free_count_took(const ra8_fs_mount_t* m, uint32_t n)
340{
342 if (st == nullptr) {
343 return;
344 }
345 st->dirty = 1U;
346 if (st->free_count == (uint32_t)k_fs_free_unknown) {
347 return;
348 }
349 st->free_count = (st->free_count >= n) ? (st->free_count - n) : 0U;
350}
351
352/* `priv_free_count_gave()`: see header for the documented contract. */
353void priv_free_count_gave(const ra8_fs_mount_t* m, uint32_t n)
354{
356 if (st == nullptr) {
357 return;
358 }
359 st->dirty = 1U;
360 if (st->free_count == (uint32_t)k_fs_free_unknown) {
361 return;
362 }
363 const uint32_t raised = st->free_count + n;
364 st->free_count = (raised > m->count_of_clusters) ? m->count_of_clusters : raised;
365}
366
367/* `priv_free_count_peek()`: see header for the documented contract. */
369{
371 if (st == nullptr) {
372 return (uint32_t)k_fs_free_unknown;
373 }
374 return st->free_count;
375}
376
377/* `priv_free_count_cache()`: see header for the documented contract. */
378void priv_free_count_cache(const ra8_fs_mount_t* m, uint32_t n)
379{
381 if (st == nullptr) {
382 return;
383 }
384 st->free_count = (n > m->count_of_clusters) ? m->count_of_clusters : n;
385}
386
387/* =============================================================================
388 * FAT32 FSInfo (MS FAT spec sec 5)
389 * =============================================================================
390 */
391
418static ra8_err_t internal_fsinfo_locate(const ra8_fs_mount_t* m, uint32_t* out_lba)
419{
420 uint8_t* const boot = priv_sec_walk();
421 const ra8_err_t err = priv_read_sector(m, 0U, boot);
422 if (err != k_ra8_ok) {
423 return err;
424 }
425 const uint32_t lba = (uint32_t)priv_rd16(&boot[k_fmt_off_f32_fsinfo]);
426 *out_lba = (uint32_t)k_fs_fsinfo_absent;
427 if (lba == (uint32_t)k_fs_fsinfo_absent) {
428 return k_ra8_ok;
429 }
430 if (lba >= m->reserved_sectors) {
431 return k_ra8_ok;
432 }
433 *out_lba = lba;
434 return k_ra8_ok;
435}
436
460static bool internal_fsinfo_signatures_ok(const uint8_t* sec)
461{
462 if (priv_rd32(&sec[k_fmt_fsi_off_lead]) != (uint32_t)k_fmt_fsi_lead_sig) {
463 return false;
464 }
465 if (priv_rd32(&sec[k_fmt_fsi_off_struct]) != (uint32_t)k_fmt_fsi_struct_sig) {
466 return false;
467 }
468 return priv_rd32(&sec[k_fmt_fsi_off_trail]) == (uint32_t)k_fmt_fsi_trail_sig;
469}
470
471/* `priv_fsinfo_seed()`: see header for the documented contract. */
473{
475 if (st == nullptr) {
476 return k_ra8_ok;
477 }
478 if (m->type != k_ra8_fs_type_fat32) {
479 return k_ra8_ok;
480 }
481 uint32_t lba = (uint32_t)k_fs_fsinfo_absent;
482 ra8_err_t err = internal_fsinfo_locate(m, &lba);
483 if (err != k_ra8_ok) {
484 return err;
485 }
486 if (lba == (uint32_t)k_fs_fsinfo_absent) {
487 return k_ra8_ok;
488 }
489 uint8_t* const sec = priv_sec_walk();
490 err = priv_read_sector(m, lba, sec);
491 if (err != k_ra8_ok) {
492 return err;
493 }
495 return k_ra8_ok;
496 }
497 st->fsinfo_lba = lba;
498 const uint32_t nxt = priv_rd32(&sec[k_fmt_fsi_off_nxtfree]);
499 const uint32_t free = priv_rd32(&sec[k_fmt_fsi_off_free]);
500 if (nxt >= (uint32_t)k_cluster_first_data) {
501 if ((nxt - (uint32_t)k_cluster_first_data) < m->count_of_clusters) {
502 st->next_free = nxt;
503 }
504 }
505 if (free <= m->count_of_clusters) {
506 st->free_count = free;
507 }
508 return k_ra8_ok;
509}
510
511/* `priv_fsinfo_flush()`: see header for the documented contract. */
513{
515 if (st == nullptr) {
516 return k_ra8_ok;
517 }
518 if (st->fsinfo_lba == (uint32_t)k_fs_fsinfo_absent) {
519 return k_ra8_ok;
520 }
521 if (st->dirty == 0U) {
522 return k_ra8_ok;
523 }
524 uint8_t* const sec = priv_sec_walk();
525 ra8_err_t err = priv_read_sector(m, st->fsinfo_lba, sec);
526 if (err != k_ra8_ok) {
527 return err;
528 }
531 err = priv_write_sector(m, st->fsinfo_lba, sec);
532 if (err != k_ra8_ok) {
533 return err;
534 }
535 st->dirty = 0U;
536 return k_ra8_ok;
537}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
const char * owner
Owner.
@ 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
uint16_t priv_rd16(const uint8_t *p)
Decode a little-endian uint16_t from a byte buffer.
Definition ra8_fs_fat.c:42
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
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
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
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
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_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_free_count_gave(const ra8_fs_mount_t *m, uint32_t n)
Account n clusters as returned to the volume's free space.
uint32_t priv_free_count_peek(const ra8_fs_mount_t *m)
Report the tracked free-cluster count, or k_fs_free_unknown.
void priv_alloc_hint_lower(const ra8_fs_mount_t *m, uint32_t cluster)
Pull the next-free hint back to cluster if it is further on.
static fat_alloc_state_t * internal_state_for(const ra8_fs_mount_t *m)
Find the allocator state slot bound to m.
static fat_alloc_state_t s_alloc[k_fs_alloc_slots]
One allocator state slot per mount slot.
void priv_alloc_state_bind(const ra8_fs_mount_t *m)
Claim and reset the allocator state slot for a freshly mounted volume.
static uint64_t s_fat_cache_lba
Volume-relative LBA held in s_fat_cache, or k_fs_cache_empty.
static ra8_err_t internal_fsinfo_locate(const ra8_fs_mount_t *m, uint32_t *out_lba)
Report the FSInfo sector number this volume declares, or absent.
static bool internal_lba_is_cacheable(const ra8_fs_mount_t *m, uint64_t lba)
True when lba lies inside the first FAT copy of m.
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_fsinfo_flush(const ra8_fs_mount_t *m)
Write the tracked free count and next-free hint back into FSInfo.
static uint8_t s_fat_cache[k_ra8_fs_sector_max]
The one cached FAT sector, shared by every mount.
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.
static const ra8_fs_mount_t * s_fat_cache_owner
Mount whose volume s_fat_cache holds a sector of.
ra8_err_t priv_exfat_bitmap_lba(const ra8_fs_mount_t *m, uint64_t *out_lba)
Resolve the exFAT allocation bitmap's first LBA, once per mount.
void priv_alloc_state_release(const ra8_fs_mount_t *m)
Release the allocator state slot held by m.
static bool internal_fsinfo_signatures_ok(const uint8_t *sec)
Check the three FSInfo signatures in a candidate sector.
uint32_t priv_alloc_hint_get(const ra8_fs_mount_t *m)
Report the cluster a free-space scan should start from.
void priv_free_count_cache(const ra8_fs_mount_t *m, uint32_t n)
Cache a freshly counted free-cluster total in the allocator slot.
@ k_fs_fsinfo_absent
No usable FSInfo sector on this vol.
@ k_fs_alloc_slots
One state slot per mount slot.
@ k_fs_free_unknown
MS FAT spec sec 5: count not known.
@ k_fs_cache_empty
Cache holds no sector.
@ k_fs_bitmap_unknown
exFAT bitmap LBA not resolved yet.
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
ra8_err_t priv_exfat_find_bitmap(const ra8_fs_mount_t *m, uint32_t *out_clus, uint32_t *out_len)
Locate the allocation-bitmap entry in the exFAT root directory.
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
@ k_fmt_fsi_off_free
FSInfo FSI_Free_Count offset.
@ k_fmt_fsi_off_struct
FSInfo FSI_StrucSig offset.
@ k_fmt_off_f32_fsinfo
FAT32 BPB_FSInfo sector number.
@ k_fmt_fsi_off_nxtfree
FSInfo FSI_Nxt_Free offset.
@ k_fmt_fsi_off_lead
FSInfo FSI_LeadSig offset.
@ k_fmt_fsi_off_trail
FSInfo FSI_TrailSig offset.
@ k_fmt_fsi_lead_sig
FSInfo lead signature "RRaA".
@ k_fmt_fsi_trail_sig
FSInfo trailing signature.
@ k_fmt_fsi_struct_sig
FSInfo struct signature "rrAa".
@ k_cluster_first_data
Cluster numbers start at 2.
@ k_ra8_fs_type_fat32
count_of_clusters >= 65525.
@ k_ra8_fs_max_mounts
Max concurrent mount points.
@ k_ra8_fs_sector_max
Largest supported sector size (4Kn).
One mounted volume's answer to "where is the free space".
uint32_t fsinfo_lba
FSInfo sector, or k_fs_fsinfo_absent.
uint32_t free_count
Free clusters, or k_fs_free_unknown.
uint8_t dirty
1 = FSInfo needs a writeback.
uint64_t bitmap_lba
exFAT bitmap LBA, or unknown.
uint32_t next_free
Cluster the next scan starts at.
const ra8_fs_mount_t * owner
Mount this slot describes; NULL = free.
Cached parse of one mounted FAT volume.
uint32_t reserved_sectors
BPB BPB_RsvdSecCnt.
ra8_fs_type_t type
FAT12 / FAT16 / FAT32.
uint32_t fat_size_sectors
BPB BPB_FATSz16 / BPB_FATSz32.
uint32_t count_of_clusters
Per MS spec: data_sectors / SPC.
uint64_t first_fat_lba
Computed: first FAT sector.