ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fs_fat_time.c
Go to the documentation of this file.
1
41
42#include <stddef.h>
43#include <stdint.h>
44
45#include "ra8_attributes.h"
46#include "ra8_fs.h"
47#include "ra8_fs_fat_internal.h"
48
49/* =============================================================================
50 * Module state -- the installed binding
51 * =============================================================================
52 */
53
69
82static bool s_clock_bound;
83
97typedef struct {
98 uint16_t date;
99 uint16_t time;
100 uint8_t tenth;
102
117typedef struct {
118 uint32_t stamp;
119 uint8_t inc10;
120 uint8_t utc;
122
123/* =============================================================================
124 * Clamping
125 * =============================================================================
126 */
127
153static uint32_t internal_clamp_u32(uint32_t v, uint32_t lo, uint32_t hi)
154{
155 if (v < lo) {
156 return lo;
157 }
158 if (v > hi) {
159 return hi;
160 }
161 return v;
162}
163
190{
191 t->year = (uint16_t)internal_clamp_u32(t->year,
192 (uint32_t)k_fs_time_epoch_year,
193 (uint32_t)k_fs_time_year_max);
194 t->month = (uint8_t)internal_clamp_u32(t->month,
195 (uint32_t)k_fs_time_month_min,
196 (uint32_t)k_fs_time_month_max);
197 t->day =
198 (uint8_t)internal_clamp_u32(t->day, (uint32_t)k_fs_time_day_min, (uint32_t)k_fs_time_day_max);
199 t->hour = (uint8_t)internal_clamp_u32(t->hour, 0U, (uint32_t)k_fs_time_hour_max);
200 t->minute = (uint8_t)internal_clamp_u32(t->minute, 0U, (uint32_t)k_fs_time_minute_max);
201 t->second = (uint8_t)internal_clamp_u32(t->second, 0U, (uint32_t)k_fs_time_second_max);
202 t->centisecond = (uint8_t)internal_clamp_u32(t->centisecond, 0U, (uint32_t)k_fs_time_centi_max);
203}
204
238{
239 *out = (ra8_fs_datetime_t){};
240 out->year = (uint16_t)k_fs_time_epoch_year;
241 out->month = (uint8_t)k_fs_time_month_min;
242 out->day = (uint8_t)k_fs_time_day_min;
243 bool real = false;
244 if (s_clock_bound) {
245 ra8_fs_datetime_t got = {};
246 if (s_clock.now(s_clock.ctx, &got) == k_ra8_ok) {
247 *out = got;
248 real = true;
249 }
250 }
252 return real;
253}
254
255/* =============================================================================
256 * Packing
257 * =============================================================================
258 */
259
284static uint8_t internal_tenths_of(const ra8_fs_datetime_t* t)
285{
286 const uint32_t odd = (uint32_t)t->second % (uint32_t)k_fs_time_second_div;
287 return (uint8_t)((odd * (uint32_t)k_fs_centi_per_second) + (uint32_t)t->centisecond);
288}
289
316static uint8_t internal_utc_byte(int16_t minutes)
317{
318 if (minutes < (int16_t)k_fs_utc_span_min) {
319 return (uint8_t)k_fs_utc_unknown;
320 }
321 if (minutes > (int16_t)k_fs_utc_span_max) {
322 return (uint8_t)k_fs_utc_unknown;
323 }
324 if ((int32_t)minutes % (int32_t)k_fs_utc_step_min != 0) {
325 return (uint8_t)k_fs_utc_unknown;
326 }
327 const int32_t steps = (int32_t)minutes / (int32_t)k_fs_utc_step_min;
328 return (uint8_t)((uint32_t)k_fs_utc_valid_bit |
329 ((uint32_t)steps & (uint32_t)k_fs_utc_field_mask));
330}
331
357{
358 const uint32_t years = (uint32_t)t->year - (uint32_t)k_fs_time_epoch_year;
359 out->date =
360 (uint16_t)((years << (uint32_t)k_fs_date_shift_year) |
361 ((uint32_t)t->month << (uint32_t)k_fs_date_shift_month) | (uint32_t)t->day);
362 out->time = (uint16_t)(((uint32_t)t->hour << (uint32_t)k_fs_time_shift_hour) |
363 ((uint32_t)t->minute << (uint32_t)k_fs_time_shift_minute) |
364 ((uint32_t)t->second / (uint32_t)k_fs_time_second_div));
365 out->tenth = internal_tenths_of(t);
366}
367
390{
391 ra8_fs_datetime_t t = {};
392 (void)internal_now_or_epoch(&t); /* FAT has no offset field: real or epoch, same packing */
393 internal_fat_pack(&t, out);
394}
395
423static void internal_exfat_pack(const ra8_fs_datetime_t* t, bool real, exfat_stamp_t* out)
424{
425 const uint32_t years = (uint32_t)t->year - (uint32_t)k_fs_time_epoch_year;
426 const uint32_t date = (years << (uint32_t)k_fs_xf_shift_year) |
427 ((uint32_t)t->month << (uint32_t)k_fs_xf_shift_month) | (uint32_t)t->day;
428 const uint32_t time = ((uint32_t)t->hour << (uint32_t)k_fs_time_shift_hour) |
429 ((uint32_t)t->minute << (uint32_t)k_fs_time_shift_minute) |
430 ((uint32_t)t->second / (uint32_t)k_fs_time_second_div);
431 out->stamp = (date << (uint32_t)k_fs_xf_shift_date) | time;
432 out->inc10 = internal_tenths_of(t);
433 /* An offset is a claim about a real instant. The epoch placeholder is not
434 * one, so saying "this is UTC" about it would be a fabrication -- exFAT's
435 * OffsetValid-clear encoding exists for exactly that case. A caller-supplied
436 * `utime` reading IS a real instant, so its offset is honoured. */
437 out->utc = real ? internal_utc_byte(t->utc_offset_min) : (uint8_t)k_fs_utc_unknown;
438}
439
462{
463 ra8_fs_datetime_t t = {};
464 const bool real = internal_now_or_epoch(&t);
465 internal_exfat_pack(&t, real, out);
466}
467
468/* =============================================================================
469 * Stampers -- what the create / write / rename paths call
470 * =============================================================================
471 */
472
473/* `priv_fat_entry_stamp_create()`: see header for the documented contract. */
474void priv_fat_entry_stamp_create(uint8_t* entry)
475{
476 fat_stamp_t s = {};
484}
485
486/* `priv_fat_entry_stamp_write()`: see header for the documented contract. */
487void priv_fat_entry_stamp_write(uint8_t* entry)
488{
489 fat_stamp_t s = {};
494}
495
496/* `priv_fat_entry_stamp_access()`: see header for the documented contract. */
497void priv_fat_entry_stamp_access(uint8_t* entry)
498{
499 fat_stamp_t s = {};
502}
503
504/* `priv_exfat_file_stamp_create()`: see header for the documented contract. */
505void priv_exfat_file_stamp_create(uint8_t* file_entry)
506{
507 exfat_stamp_t s = {};
509 priv_wr32(&file_entry[k_exfat_off_file_ctime], s.stamp);
510 priv_wr32(&file_entry[k_exfat_off_file_mtime], s.stamp);
511 priv_wr32(&file_entry[k_exfat_off_file_atime], s.stamp);
512 file_entry[k_exfat_off_file_c10ms] = s.inc10;
513 file_entry[k_exfat_off_file_m10ms] = s.inc10;
514 file_entry[k_exfat_off_file_cutc] = s.utc;
515 file_entry[k_exfat_off_file_mutc] = s.utc;
516 file_entry[k_exfat_off_file_autc] = s.utc;
517}
518
519/* `priv_exfat_file_stamp_write()`: see header for the documented contract. */
520void priv_exfat_file_stamp_write(uint8_t* file_entry)
521{
522 exfat_stamp_t s = {};
524 priv_wr32(&file_entry[k_exfat_off_file_mtime], s.stamp);
525 priv_wr32(&file_entry[k_exfat_off_file_atime], s.stamp);
526 file_entry[k_exfat_off_file_m10ms] = s.inc10;
527 file_entry[k_exfat_off_file_mutc] = s.utc;
528 file_entry[k_exfat_off_file_autc] = s.utc;
529}
530
531/* `priv_exfat_file_stamp_access()`: see header for the documented contract. */
532void priv_exfat_file_stamp_access(uint8_t* file_entry)
533{
534 exfat_stamp_t s = {};
536 priv_wr32(&file_entry[k_exfat_off_file_atime], s.stamp);
537 file_entry[k_exfat_off_file_autc] = s.utc;
538}
539
540/* `priv_fat_entry_set_times()`: see header for the documented contract. */
541void priv_fat_entry_set_times(uint8_t* entry,
542 const ra8_fs_datetime_t* create,
543 const ra8_fs_datetime_t* modify,
544 const ra8_fs_datetime_t* access)
545{
546 if (create != nullptr) {
547 ra8_fs_datetime_t t = *create;
549 fat_stamp_t s = {};
550 internal_fat_pack(&t, &s);
554 }
555 if (modify != nullptr) {
556 ra8_fs_datetime_t t = *modify;
558 fat_stamp_t s = {};
559 internal_fat_pack(&t, &s);
562 }
563 if (access != nullptr) {
564 ra8_fs_datetime_t t = *access;
566 fat_stamp_t s = {};
567 internal_fat_pack(&t, &s);
569 }
570}
571
572/* `priv_exfat_file_set_times()`: see header for the documented contract. */
573void priv_exfat_file_set_times(uint8_t* file_entry,
574 const ra8_fs_datetime_t* create,
575 const ra8_fs_datetime_t* modify,
576 const ra8_fs_datetime_t* access)
577{
578 if (create != nullptr) {
579 ra8_fs_datetime_t t = *create;
581 exfat_stamp_t s = {};
582 internal_exfat_pack(&t, true, &s);
583 priv_wr32(&file_entry[k_exfat_off_file_ctime], s.stamp);
584 file_entry[k_exfat_off_file_c10ms] = s.inc10;
585 file_entry[k_exfat_off_file_cutc] = s.utc;
586 }
587 if (modify != nullptr) {
588 ra8_fs_datetime_t t = *modify;
590 exfat_stamp_t s = {};
591 internal_exfat_pack(&t, true, &s);
592 priv_wr32(&file_entry[k_exfat_off_file_mtime], s.stamp);
593 file_entry[k_exfat_off_file_m10ms] = s.inc10;
594 file_entry[k_exfat_off_file_mutc] = s.utc;
595 }
596 if (access != nullptr) {
597 ra8_fs_datetime_t t = *access;
599 exfat_stamp_t s = {};
600 internal_exfat_pack(&t, true, &s);
601 priv_wr32(&file_entry[k_exfat_off_file_atime], s.stamp);
602 file_entry[k_exfat_off_file_autc] = s.utc;
603 }
604}
605
606/* =============================================================================
607 * Public API: clock binding
608 * =============================================================================
609 */
610
612{
613 if (clock == nullptr) {
614 s_clock_bound = false;
616 return k_ra8_ok;
617 }
618 if (clock->now == nullptr) {
620 }
621 s_clock = *clock;
622 s_clock_bound = true;
623 return k_ra8_ok;
624}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ 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).
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_wr16(uint8_t *p, uint16_t v)
Encode a little-endian uint16_t into a byte buffer.
Definition ra8_fs_fat.c:61
Cross-TU shared declarations for the FAT/exFAT ra8_fs adapter.
void priv_fat_entry_set_times(uint8_t *entry, const ra8_fs_datetime_t *create, const ra8_fs_datetime_t *modify, const ra8_fs_datetime_t *access)
Set chosen FAT directory-entry timestamps from caller-supplied readings.
void priv_exfat_file_stamp_write(uint8_t *file_entry)
Advance an exFAT File entry's modification (and access) stamps.
void priv_exfat_file_stamp_create(uint8_t *file_entry)
Stamp a fresh exFAT File entry's create, modify, and access fields.
static bool internal_now_or_epoch(ra8_fs_datetime_t *out)
Read the installed clock, or hand back the FAT epoch.
void priv_exfat_file_stamp_access(uint8_t *file_entry)
Advance only an exFAT File entry's last-accessed stamp.
static void internal_fat_stamp_now(fat_stamp_t *out)
Pack the installed clock's reading into FAT's two words plus tenths.
void priv_fat_entry_stamp_access(uint8_t *entry)
Advance only a FAT directory entry's last-access date.
static ra8_fs_clock_t s_clock
The installed calendar binding; meaningful only when s_clock_bound.
void priv_exfat_file_set_times(uint8_t *file_entry, const ra8_fs_datetime_t *create, const ra8_fs_datetime_t *modify, const ra8_fs_datetime_t *access)
Set chosen exFAT File-entry timestamps from caller-supplied readings.
static uint8_t internal_utc_byte(int16_t minutes)
Encode a UTC offset in minutes as an exFAT UtcOffset byte.
static uint8_t internal_tenths_of(const ra8_fs_datetime_t *t)
Count the 10 ms increments the 2-second stamp granularity discards.
void priv_fat_entry_stamp_write(uint8_t *entry)
Advance a FAT directory entry's modification time and access date.
static void internal_fat_pack(const ra8_fs_datetime_t *t, fat_stamp_t *out)
Pack an already-clamped reading into FAT's two words plus the tenths.
static uint32_t internal_clamp_u32(uint32_t v, uint32_t lo, uint32_t hi)
Clamp v into the closed range [lo, hi].
ra8_err_t ra8_fs_set_clock(const ra8_fs_clock_t *clock)
Install (or remove) the calendar source used to stamp files.
void priv_fat_entry_stamp_create(uint8_t *entry)
Stamp a fresh FAT directory entry's create, write, and access fields.
static void internal_exfat_stamp_now(exfat_stamp_t *out)
Pack the installed clock's reading into exFAT's stamp plus side fields.
static void internal_clamp_datetime(ra8_fs_datetime_t *t)
Clamp every field of a broken-down time into its on-disk range.
static bool s_clock_bound
True once a complete binding has been installed.
static void internal_exfat_pack(const ra8_fs_datetime_t *t, bool real, exfat_stamp_t *out)
Pack an already-clamped reading into exFAT's stamp plus side fields.
@ k_fs_utc_span_min
UTC-12:00 expressed in minutes.
@ k_fs_utc_span_max
UTC+14:00 expressed in minutes.
@ k_exfat_off_file_mutc
exFAT spec sec 7.4.14 "LastModifiedUtcOffset".
@ k_exfat_off_file_m10ms
exFAT spec sec 7.4.12 "LastModified10ms".
@ k_exfat_off_file_atime
exFAT spec sec 7.4.10 "LastAccessedTimestamp".
@ k_exfat_off_file_mtime
exFAT spec sec 7.4.9 "LastModifiedTimestamp".
@ k_exfat_off_file_ctime
exFAT spec sec 7.4.8 "CreateTimestamp".
@ k_exfat_off_file_cutc
exFAT spec sec 7.4.13 "CreateUtcOffset".
@ k_exfat_off_file_autc
exFAT spec sec 7.4.15 "LastAccessedUtcOffset".
@ k_exfat_off_file_c10ms
exFAT spec sec 7.4.11 "Create10msIncrement".
@ k_dir_off_lst_acc_date
MS FAT spec sec 6 "DIR_LstAccDate".
@ k_dir_off_crt_time_tenth
MS FAT spec sec 6 "DIR_CrtTimeTenth".
@ k_dir_off_crt_date
MS FAT spec sec 6 "DIR_CrtDate".
@ k_dir_off_crt_time
MS FAT spec sec 6 "DIR_CrtTime".
@ k_dir_off_wrt_date
MS FAT spec sec 6 "DIR_WrtDate".
@ k_dir_off_wrt_time
MS FAT spec sec 6 "DIR_WrtTime".
@ k_fs_time_year_max
1980 + 127: last representable year.
@ k_fs_time_shift_hour
FAT time: hour field position.
@ k_fs_time_month_min
Month is 1-based on disk.
@ k_fs_time_second_max
Highest second.
@ k_fs_time_shift_minute
FAT time: minute field position.
@ k_fs_utc_valid_bit
exFAT UtcOffset bit 7 = OffsetValid.
@ k_fs_time_hour_max
Highest hour.
@ k_fs_time_minute_max
Highest minute.
@ k_fs_centi_per_second
Hundredths in one second.
@ k_fs_utc_step_min
exFAT UtcOffset granularity, minutes.
@ k_fs_date_shift_year
FAT date: year field position.
@ k_fs_xf_shift_year
exFAT date half: year field position.
@ k_fs_utc_field_mask
exFAT UtcOffset bits 6:0 = the offset.
@ k_fs_time_second_div
FAT/exFAT store seconds/2.
@ k_fs_time_epoch_year
Year 0 of both on-disk formats.
@ k_fs_xf_shift_month
exFAT date half: month field position.
@ k_fs_time_day_max
Highest day the field can express.
@ k_fs_time_day_min
Day is 1-based on disk.
@ k_fs_utc_unknown
OffsetValid clear: zone not recorded.
@ k_fs_xf_shift_date
exFAT stamp: date half position.
@ k_fs_time_centi_max
Highest hundredth within a second.
@ k_fs_time_month_max
Highest month.
@ k_fs_date_shift_month
FAT date: month field position.
One reading already packed into exFAT's 32-bit stamp plus its extras.
uint8_t utc
UtcOffset byte, or k_fs_utc_unknown.
uint32_t stamp
exFAT spec sec 7.4.8 packed timestamp.
uint8_t inc10
10 ms increment, 0..199.
One reading already packed into the two 16-bit FAT words plus tenths.
uint16_t time
MS FAT spec sec 6 packed time word.
uint8_t tenth
DIR_CrtTimeTenth: 0..199 in 10 ms.
uint16_t date
MS FAT spec sec 6 packed date word.
Caller-supplied calendar source used to stamp files.
ra8_err_t(* now)(void *ctx, ra8_fs_datetime_t *out)
Report the current civil time.
Broken-down civil date and time used at the filesystem boundary.
uint8_t hour
Hour of day, 0..23.
uint8_t minute
Minute of hour, 0..59.
int16_t utc_offset_min
Offset of the civil fields from UTC, in minutes.
uint8_t second
Second of minute, 0..59.
uint8_t month
Month of year, 1..12.
uint8_t centisecond
Hundredths within second, 0..99.
uint16_t year
Full civil year, e.g.
uint8_t day
Day of month, 1..31.