ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_fileio.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: read / write / seek / tell / size
23 * =============================================================================
24 */
25
52static ra8_err_t
53internal_skip_clusters(const ra8_fs_mount_t* m, uint32_t start, uint32_t n, uint32_t* out)
54{
55 uint32_t cur = start;
56 for (uint32_t i = 0; i < n; i++) {
57 uint32_t next = 0;
58 ra8_err_t err = priv_fat_get(m, cur, &next);
59 if (err != k_ra8_ok) {
60 return err;
61 }
62 if (priv_is_eoc(m, next) != 0U) {
63 *out = cur;
65 }
66 cur = next;
67 }
68 *out = cur;
69 return k_ra8_ok;
70}
71
97static ra8_err_t
98internal_read_one_chunk(ra8_fs_file_t* file, uint8_t* buf, uint32_t remaining, uint32_t* out_take)
99{
100 const uint32_t cluster_bytes = priv_cluster_bytes(file->mount);
101 const uint32_t cluster_idx_now = (uint32_t)(file->offset / cluster_bytes);
102 uint32_t target = 0;
103 /* exFAT contiguous files (NoFatChain) have no valid FAT chain: clusters are
104 * sequential from the first. FAT files (no_fat_chain == 0) walk the chain. */
105 if (file->no_fat_chain != 0U) {
106 target = file->first_cluster + cluster_idx_now;
107 } else {
108 /* Read accelerator: a sequential read advances the offset one cluster at a
109 * time, so resume the FAT walk from the cached forward waypoint instead of
110 * re-walking from the chain head on every sector -- O(n) over a file, not the
111 * O(n^2) a per-sector walk-from-head would cost. The cache holds a valid chain
112 * position (walk_cache_cluster at walk_cache_idx); use it only when it is set
113 * (a real data cluster) AND the target is at or ahead of it, else walk from
114 * the head.
115 *
116 * @par MC/DC:
117 * Decision: `(walk_cache_cluster >= k_cluster_first_data) && (cluster_idx_now
118 * >= walk_cache_idx)` (2 conditions)
119 * - cache set, target ahead -> true (resume from waypoint)
120 * - cache unset (cluster < 2) -> false (first condition independent)
121 * - cache set, target behind -> false (second condition independent; a
122 * backward seek re-walks from the head)
123 */
124 uint32_t walk_from = file->first_cluster;
125 uint32_t walk_n = cluster_idx_now;
126 if ((file->walk_cache_cluster >= (uint32_t)k_cluster_first_data) &&
127 (cluster_idx_now >= file->walk_cache_idx)) {
128 walk_from = file->walk_cache_cluster;
129 walk_n = cluster_idx_now - file->walk_cache_idx;
130 }
131 ra8_err_t werr = internal_skip_clusters(file->mount, walk_from, walk_n, &target);
132 if (werr != k_ra8_ok) {
133 return werr;
134 }
135 file->walk_cache_idx = cluster_idx_now;
136 file->walk_cache_cluster = target;
137 }
138 file->cur_cluster = target;
139 const uint32_t off_in_cluster = (uint32_t)(file->offset % cluster_bytes);
140 const uint32_t sector_in_cluster = off_in_cluster / priv_bps(file->mount);
141 const uint32_t off_in_sector = off_in_cluster % priv_bps(file->mount);
142 const uint64_t lba = priv_cluster_to_lba(file->mount, file->cur_cluster) + sector_in_cluster;
143 uint8_t* const sec = priv_sec_io();
144 const ra8_err_t err = priv_read_sector(file->mount, lba, sec);
145 if (err != k_ra8_ok) {
146 return err;
147 }
148 uint32_t take = priv_bps(file->mount) - off_in_sector;
149 if (take > remaining) {
150 take = remaining;
151 }
152 priv_byte_copy(buf, &sec[off_in_sector], take);
153 *out_take = take;
154 return k_ra8_ok;
155}
156
194static ra8_err_t
195internal_read_span(ra8_fs_file_t* file, uint8_t* buf, uint32_t remaining, uint32_t* out_take)
196{
197 const uint64_t valid =
198 (file->mount->type == k_ra8_fs_type_exfat) ? file->valid_bytes : file->size_bytes;
199 if (file->offset >= valid) {
200 for (uint32_t i = 0U; i < remaining; i++) {
201 buf[i] = 0U;
202 }
203 *out_take = remaining;
204 return k_ra8_ok;
205 }
206 uint32_t want = remaining;
207 const uint64_t cap = valid - file->offset;
208 if ((uint64_t)want > cap) {
209 want = (uint32_t)cap;
210 }
211 return internal_read_one_chunk(file, buf, want, out_take);
212}
213
243RA8_EXPECTS_LOCK("ra8_fs_lock")
244static ra8_err_t
245internal_read_locked(ra8_fs_file_t* file, uint8_t* buf, uint32_t max_len, uint32_t* got_len)
246{
247 if (file == nullptr || buf == nullptr || got_len == nullptr) {
248 return k_ra8_err_null_ptr;
249 }
250 if (file->in_use == 0U) {
251 return k_ra8_err_invalid_state;
252 }
253 *got_len = 0;
254 if (file->offset >= file->size_bytes || max_len == 0U) {
255 return k_ra8_ok;
256 }
257 const uint64_t left = file->size_bytes - file->offset;
258 uint32_t remaining = max_len;
259 if ((uint64_t)max_len > left) {
260 remaining = (uint32_t)left;
261 }
262 uint32_t produced = 0;
263 while (remaining > 0U) {
264 uint32_t take = 0;
265 ra8_err_t err = internal_read_span(file, &buf[produced], remaining, &take);
266 if (err != k_ra8_ok) {
267 return err;
268 }
269 produced += take;
270 file->offset += take;
271 remaining -= take;
272 }
273 *got_len = produced;
274 return k_ra8_ok;
275}
276
277/* `priv_alloc_eoc_cluster()`: see header for the documented contract. */
279{
280 ra8_err_t err = priv_alloc_cluster(m, out_c);
281 if (err != k_ra8_ok) {
282 return err;
283 }
284 return priv_fat_set(m, *out_c, priv_eoc_write(m));
285}
286
312static ra8_err_t
313internal_walk_grow(const ra8_fs_mount_t* m, uint32_t start, uint32_t idx, uint32_t* out_cluster)
314{
315 uint32_t cur = start;
316 for (uint32_t i = 0; i < idx; i++) {
317 uint32_t next = 0;
318 ra8_err_t err = priv_fat_get(m, cur, &next);
319 if (err != k_ra8_ok) {
320 return err;
321 }
322 if (priv_is_eoc(m, next) != 0U) {
323 uint32_t newc = 0;
324 err = priv_alloc_eoc_cluster(m, &newc);
325 if (err != k_ra8_ok) {
326 return err;
327 }
328 err = priv_fat_set(m, cur, newc);
329 if (err != k_ra8_ok) {
330 return err;
331 }
332 next = newc;
333 }
334 cur = next;
335 }
336 *out_cluster = cur;
337 return k_ra8_ok;
338}
339
342 uint64_t lba,
343 uint32_t off_in_sector,
344 const uint8_t* src,
345 uint32_t put)
346{
347 uint8_t* const sec = priv_sec_io();
348 ra8_err_t err = priv_read_sector(m, lba, sec);
349 if (err != k_ra8_ok) {
350 return err;
351 }
352 priv_byte_copy(&sec[off_in_sector], src, put);
353 return priv_write_sector(m, lba, sec);
354}
355
370typedef struct {
371 uint32_t cluster;
372 uint32_t index;
374
403static ra8_err_t
404internal_write_position(ra8_fs_file_t* file, write_walk_t* way, uint32_t idx, uint32_t* out_cluster)
405{
406 const ra8_fs_mount_t* m = file->mount;
407 if (file->first_cluster < k_cluster_first_data) {
408 uint32_t c = 0;
409 const ra8_err_t err = priv_alloc_eoc_cluster(m, &c);
410 if (err != k_ra8_ok) {
411 return err;
412 }
413 file->first_cluster = c;
414 file->cur_cluster = c;
415 way->cluster = c;
416 way->index = idx;
417 }
418 uint32_t cur = 0;
419 const ra8_err_t err = internal_walk_grow(m, way->cluster, idx - way->index, &cur);
420 if (err != k_ra8_ok) {
421 return err;
422 }
423 way->cluster = cur;
424 way->index = idx;
425 *out_cluster = cur;
426 return k_ra8_ok;
427}
428
454static ra8_err_t internal_write_stream(ra8_fs_file_t* file, const uint8_t* buf, uint32_t len)
455{
456 const ra8_fs_mount_t* m = file->mount;
457 const uint32_t cluster_bytes = priv_cluster_bytes(m);
458 uint32_t consumed = 0;
459 /* A write may allocate/grow the chain, invalidating any cached read waypoint. */
460 file->walk_cache_cluster = 0;
461 write_walk_t way = {.cluster = file->first_cluster, .index = 0U};
462 while (consumed < len) {
463 const uint32_t cluster_idx_now = (uint32_t)(file->offset / cluster_bytes);
464 uint32_t cur = 0;
465 ra8_err_t err = internal_write_position(file, &way, cluster_idx_now, &cur);
466 if (err != k_ra8_ok) {
467 return err;
468 }
469 file->cur_cluster = cur;
470 const uint32_t off_in_cluster = (uint32_t)(file->offset % cluster_bytes);
471 const uint32_t sector_in_cluster = off_in_cluster / priv_bps(m);
472 const uint32_t off_in_sector = off_in_cluster % priv_bps(m);
473 const uint64_t lba = priv_cluster_to_lba(m, file->cur_cluster) + sector_in_cluster;
474 uint32_t put = priv_bps(m) - off_in_sector;
475 if (put > (len - consumed)) {
476 put = len - consumed;
477 }
478 err = priv_write_into_sector(m, lba, off_in_sector, &buf[consumed], put);
479 if (err != k_ra8_ok) {
480 return err;
481 }
482 consumed += put;
483 file->offset += put;
484 if (file->offset > file->size_bytes) {
485 file->size_bytes = file->offset;
486 }
487 }
488 return k_ra8_ok;
489}
490
518RA8_EXPECTS_LOCK("ra8_fs_lock")
519static ra8_err_t internal_write_locked(ra8_fs_file_t* file, const uint8_t* buf, uint32_t len)
520{
521 if (file == nullptr || buf == nullptr) {
522 return k_ra8_err_null_ptr;
523 }
524 if (file->in_use == 0U || file->mode == k_ra8_fs_mode_read) {
525 return k_ra8_err_invalid_state;
526 }
527 if (len == 0U) {
528 return k_ra8_ok;
529 }
530 if (file->mount->type == k_ra8_fs_type_exfat) {
531 const ra8_err_t xe = priv_exfat_write_stream(file, buf, len);
532 if (xe != k_ra8_ok) {
533 return xe;
534 }
535 file->dirty = 1U;
536 return priv_exfat_flush_set(file);
537 }
538 /* The FAT boundary of #676: `DIR_FileSize` is 32-bit, so a write that would
539 * push the file past 4 GiB - 1 cannot be recorded and is refused whole --
540 * truncating the count silently would corrupt the entry on the next flush.
541 * exFAT never reaches this test (its branch returned above). */
542 if (len > ((uint64_t)k_ra8_fs_fat_max_file_bytes - file->offset)) {
543 return k_ra8_err_invalid_size;
544 }
545 ra8_err_t err = internal_write_stream(file, buf, len);
546 if (err != k_ra8_ok) {
547 return err;
548 }
549 const ra8_fs_mount_t* m = file->mount;
550 uint8_t* const dirsec = priv_sec_walk();
551 err = priv_read_sector(m, file->dir_entry_lba, dirsec);
552 if (err != k_ra8_ok) {
553 return err;
554 }
555 priv_entry_set_cluster_size(&dirsec[file->dir_entry_idx],
556 file->first_cluster,
557 (uint32_t)file->size_bytes);
558 /* The contents just changed, so the modification time has to move with them
559 * (#601). Doing it here as well as at close means a writer that is cut off
560 * mid-stream still leaves an mtime describing what is actually on the card,
561 * rather than whatever a PC wrote when it created the file. */
562 priv_fat_entry_stamp_write(&dirsec[file->dir_entry_idx]);
563 /* Same reason the archive attribute rides along: the file was modified, which
564 * is exactly what the bit records (#681). */
565 priv_fat_entry_apply_attr(&dirsec[file->dir_entry_idx], (uint8_t)k_ra8_fs_attr_archive, 0U);
566 file->dirty = 1U;
567 return priv_write_sector(m, file->dir_entry_lba, dirsec);
568}
569
606RA8_EXPECTS_LOCK("ra8_fs_lock")
608 const char* path,
609 const uint8_t* data,
610 uint32_t len)
611{
612 if (handle == nullptr) {
613 return k_ra8_err_null_ptr;
614 }
615 if (path == nullptr) {
616 return k_ra8_err_null_ptr;
617 }
618 if (data == nullptr) {
619 return k_ra8_err_null_ptr;
620 }
621 if (handle->in_use == 0U) {
622 return k_ra8_err_invalid_state;
623 }
624 ra8_fs_file_t* f = nullptr;
625 ra8_err_t e = priv_open_locked(handle, path, k_ra8_fs_mode_write, &f);
626 if (e != k_ra8_ok) {
627 return e;
628 }
629 e = internal_write_locked(f, data, len);
630 const ra8_err_t cerr = priv_close_locked(f);
631 if (e != k_ra8_ok) {
632 return e;
633 }
634 return cerr;
635}
636
661RA8_EXPECTS_LOCK("ra8_fs_lock")
662static ra8_err_t internal_seek_locked(ra8_fs_file_t* file, uint64_t offset_bytes)
663{
664 if (file == nullptr) {
665 return k_ra8_err_null_ptr;
666 }
667 if (file->in_use == 0U) {
668 return k_ra8_err_invalid_state;
669 }
670 uint64_t target = offset_bytes;
671 if (target > file->size_bytes) {
672 target = file->size_bytes;
673 }
674 file->offset = target;
675 return k_ra8_ok;
676}
677
702RA8_EXPECTS_LOCK("ra8_fs_lock")
703static ra8_err_t internal_tell_locked(const ra8_fs_file_t* file, uint64_t* out_offset)
704{
705 if (file == nullptr || out_offset == nullptr) {
706 return k_ra8_err_null_ptr;
707 }
708 if (file->in_use == 0U) {
709 return k_ra8_err_invalid_state;
710 }
711 *out_offset = file->offset;
712 return k_ra8_ok;
713}
714
739RA8_EXPECTS_LOCK("ra8_fs_lock")
740static ra8_err_t internal_size_locked(const ra8_fs_file_t* file, uint64_t* out_bytes)
741{
742 if (file == nullptr || out_bytes == nullptr) {
743 return k_ra8_err_null_ptr;
744 }
745 if (file->in_use == 0U) {
746 return k_ra8_err_invalid_state;
747 }
748 *out_bytes = file->size_bytes;
749 return k_ra8_ok;
750}
751
752/* =============================================================================
753 * Public entry points -- the lock brackets
754 * =============================================================================
755 */
756
757RA8_OWNS_RESOURCE("ra8_fs_lock")
758ra8_err_t ra8_fs_read(ra8_fs_file_t* file, uint8_t* buf, uint32_t max_len, uint32_t* got_len)
759{
761 const ra8_err_t err = internal_read_locked(file, buf, max_len, got_len);
763 return err;
764}
765
766RA8_OWNS_RESOURCE("ra8_fs_lock")
767ra8_err_t ra8_fs_write(ra8_fs_file_t* file, const uint8_t* buf, uint32_t len)
768{
770 const ra8_err_t err = internal_write_locked(file, buf, len);
772 return err;
773}
774
775RA8_OWNS_RESOURCE("ra8_fs_lock")
777ra8_fs_write_file(ra8_fs_mount_t* handle, const char* path, const uint8_t* data, uint32_t len)
778{
780 const ra8_err_t err = internal_write_file_locked(handle, path, data, len);
782 return err;
783}
784
785RA8_OWNS_RESOURCE("ra8_fs_lock")
786ra8_err_t ra8_fs_seek(ra8_fs_file_t* file, uint64_t offset_bytes)
787{
789 const ra8_err_t err = internal_seek_locked(file, offset_bytes);
791 return err;
792}
793
794RA8_OWNS_RESOURCE("ra8_fs_lock")
795ra8_err_t ra8_fs_tell(const ra8_fs_file_t* file, uint64_t* out_offset)
796{
798 const ra8_err_t err = internal_tell_locked(file, out_offset);
800 return err;
801}
802
803RA8_OWNS_RESOURCE("ra8_fs_lock")
804ra8_err_t ra8_fs_size(const ra8_fs_file_t* file, uint64_t* out_bytes)
805{
807 const ra8_err_t err = internal_size_locked(file, out_bytes);
809 return err;
810}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_OWNS_RESOURCE(kind)
RAII-style resource ownership contract.
#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_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ 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
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_write(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Write len bytes; allocate new clusters from FAT free-space scan as needed.
ra8_err_t ra8_fs_size(const ra8_fs_file_t *file, uint64_t *out_bytes)
Report the file's size in bytes (64-bit on exFAT, #676).
ra8_err_t ra8_fs_read(ra8_fs_file_t *file, uint8_t *buf, uint32_t max_len, uint32_t *got_len)
Read up to max_len bytes; advance the cluster chain on cluster crossings.
ra8_err_t ra8_fs_seek(ra8_fs_file_t *file, uint64_t offset_bytes)
Move the file offset to offset_bytes (clamped to size).
ra8_err_t ra8_fs_tell(const ra8_fs_file_t *file, uint64_t *out_offset)
Report the current offset (64-bit; see ra8_fs_seek).
ra8_err_t ra8_fs_write_file(ra8_fs_mount_t *handle, const char *path, const uint8_t *data, uint32_t len)
Create a whole file in one call (provisioning helper).
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
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
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
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
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
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
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
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
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
uint8_t * priv_sec_io(void)
The IO-role sector buffer (leaf data / bitmap sector transfers).
uint8_t * priv_sec_walk(void)
The WALK-role sector buffer (directory scans and entry RMW).
ra8_err_t priv_exfat_flush_set(ra8_fs_file_t *file)
Rewrite a file's entry set from the handle, checksum last.
ra8_err_t priv_close_locked(ra8_fs_file_t *file)
Close an open file – the guarded body of ra8_fs_close().
void priv_entry_set_cluster_size(uint8_t *entry, uint32_t cluster, uint32_t size)
Patch first-cluster + size back into a 32-byte directory entry.
void priv_fat_entry_apply_attr(uint8_t *entry, uint8_t set_mask, uint8_t clear_mask)
Clear then set attribute bits in a 32-byte FAT directory entry.
ra8_err_t priv_open_locked(ra8_fs_mount_t *handle, const char *path, ra8_fs_mode_t mode, ra8_fs_file_t **out_file)
Open a file by path – the guarded body of ra8_fs_open().
static ra8_err_t internal_write_stream(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Inner write loop: stream len bytes from buf into the file's chain.
static ra8_err_t internal_skip_clusters(const ra8_fs_mount_t *m, uint32_t start, uint32_t n, uint32_t *out)
Walk n clusters forward from start along the FAT chain.
static ra8_err_t internal_seek_locked(ra8_fs_file_t *file, uint64_t offset_bytes)
Move the cursor – the guarded body of ra8_fs_seek().
static ra8_err_t internal_tell_locked(const ra8_fs_file_t *file, uint64_t *out_offset)
Report the cursor – the guarded body of ra8_fs_tell().
static ra8_err_t internal_walk_grow(const ra8_fs_mount_t *m, uint32_t start, uint32_t idx, uint32_t *out_cluster)
Walk to cluster index idx from start, growing the chain as needed.
static ra8_err_t internal_write_position(ra8_fs_file_t *file, write_walk_t *way, uint32_t idx, uint32_t *out_cluster)
Position the write cursor at chain index idx, growing as needed.
static ra8_err_t internal_write_locked(ra8_fs_file_t *file, const uint8_t *buf, uint32_t len)
Write bytes – the guarded body of ra8_fs_write().
static ra8_err_t internal_size_locked(const ra8_fs_file_t *file, uint64_t *out_bytes)
Report the size – the guarded body of ra8_fs_size().
static ra8_err_t internal_read_one_chunk(ra8_fs_file_t *file, uint8_t *buf, uint32_t remaining, uint32_t *out_take)
Read up to one sector's worth of bytes at the file's current offset.
ra8_err_t priv_alloc_eoc_cluster(const ra8_fs_mount_t *m, uint32_t *out_c)
Allocate a fresh cluster, mark it EOC, and return its number.
static ra8_err_t internal_read_span(ra8_fs_file_t *file, uint8_t *buf, uint32_t remaining, uint32_t *out_take)
Read one span, serving zeros past exFAT's ValidDataLength.
ra8_err_t priv_write_into_sector(const ra8_fs_mount_t *m, uint64_t lba, uint32_t off_in_sector, const uint8_t *src, uint32_t put)
Implementation of priv_write_into_sector() – one read-modify-write.
static ra8_err_t internal_read_locked(ra8_fs_file_t *file, uint8_t *buf, uint32_t max_len, uint32_t *got_len)
Read bytes – the guarded body of ra8_fs_read().
static ra8_err_t internal_write_file_locked(ra8_fs_mount_t *handle, const char *path, const uint8_t *data, uint32_t len)
Create a whole file – the guarded body of ra8_fs_write_file().
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.
void priv_fat_entry_stamp_write(uint8_t *entry)
Advance a FAT directory entry's modification time and access date.
@ k_cluster_first_data
Cluster numbers start at 2.
@ k_ra8_fs_fat_max_file_bytes
4 GiB - 1: max DIR_FileSize.
@ k_ra8_fs_type_exfat
exFAT (read + streaming write + format).
@ k_ra8_fs_attr_archive
MS FAT spec sec 6 "ATTR_ARCHIVE".
@ k_ra8_fs_mode_read
Read-only, must exist.
@ k_ra8_fs_mode_write
Truncate (or create) for writing.
Open-file state.
Cached parse of one mounted FAT volume.
Forward waypoint carried across one write's grow-walks.
uint32_t index
Chain index of cluster.
uint32_t cluster
Cluster the last walk reached.