// SPDX-License-Identifier: MPL-2.0 use core::{ops::Range, time::Duration}; use time::{OffsetDateTime, PrimitiveDateTime, Time}; use super::fat::ClusterID; use crate::prelude::*; pub fn make_hash_index(cluster: ClusterID, offset: u32) -> usize { (cluster as usize) << 32usize | (offset as usize & 0xffffffffusize) } pub fn calc_checksum_32(data: &[u8]) -> u32 { let mut checksum: u32 = 0; for &value in data { checksum = ((checksum << 31) | (checksum >> 1)).wrapping_add(value as u32); } checksum } /// Calculating checksum, ignoring certarin bytes in the range pub fn calc_checksum_16(data: &[u8], ignore: core::ops::Range, prev_checksum: u16) -> u16 { let mut result = prev_checksum; for (pos, &value) in data.iter().enumerate() { // Ignore the checksum field if ignore.contains(&pos) { continue; } result = ((result << 15) | (result >> 1)).wrapping_add(value as u16); } result } pub fn get_value_from_range(value: u16, range: Range) -> u16 { (value >> range.start) & ((1 << (range.end - range.start)) - 1) } const DOUBLE_SECOND_RANGE: Range = 0..5; const MINUTE_RANGE: Range = 5..11; const HOUR_RANGE: Range = 11..16; const DAY_RANGE: Range = 0..5; const MONTH_RANGE: Range = 5..9; const YEAR_RANGE: Range = 9..16; const EXFAT_TIME_ZONE_VALID: u8 = 1 << 7; #[derive(Default, Debug, Clone, Copy)] pub struct DosTimestamp { // Timestamp at the precesion of double seconds. pub(super) time: u16, pub(super) date: u16, // Precise time in 10ms. pub(super) increament_10ms: u8, pub(super) utc_offset: u8, } impl DosTimestamp { pub fn now() -> Result { #[cfg(not(ktest))] { use crate::time::now_as_duration; DosTimestamp::from_duration(now_as_duration(&crate::time::ClockID::CLOCK_REALTIME)?) } // When ktesting, the time module has not been initialized yet, return a fake value instead. #[cfg(ktest)] { use crate::time::SystemTime; return DosTimestamp::from_duration( SystemTime::UNIX_EPOCH.duration_since(&SystemTime::UNIX_EPOCH)?, ); } } pub fn new(time: u16, date: u16, increament_10ms: u8, utc_offset: u8) -> Result { let time = Self { time, date, increament_10ms, utc_offset, }; Ok(time) } pub fn from_duration(duration: Duration) -> Result { // FIXME:UTC offset information is missing. let date_time_result = OffsetDateTime::from_unix_timestamp_nanos(duration.as_nanos() as i128); if date_time_result.is_err() { return_errno_with_message!(Errno::EINVAL, "failed to parse date time.") } let date_time = date_time_result.unwrap(); let time = ((date_time.hour() as u16) << HOUR_RANGE.start) | ((date_time.minute() as u16) << MINUTE_RANGE.start) | ((date_time.second() as u16) >> 1); let date = (((date_time.year() - 1980) as u16) << YEAR_RANGE.start) | ((date_time.month() as u16) << MONTH_RANGE.start) | ((date_time.day() as u16) << DAY_RANGE.start); const NSEC_PER_10MSEC: u32 = 10000000; let increament_10ms = (date_time.second() as u32 % 2 * 100 + date_time.nanosecond() / NSEC_PER_10MSEC) as u8; Ok(Self { time, date, increament_10ms, utc_offset: 0, }) } pub fn as_duration(&self) -> Result { let year = 1980 + get_value_from_range(self.date, YEAR_RANGE) as u32; let month_result = time::Month::try_from(get_value_from_range(self.date, MONTH_RANGE) as u8); if month_result.is_err() { return_errno_with_message!(Errno::EINVAL, "invalid month") } let month = month_result.unwrap(); let day = get_value_from_range(self.date, DAY_RANGE); let hour = get_value_from_range(self.time, HOUR_RANGE); let minute = get_value_from_range(self.time, MINUTE_RANGE); let second = get_value_from_range(self.time, DOUBLE_SECOND_RANGE) * 2; let day_result = time::Date::from_calendar_date(year as i32, month, day as u8); if day_result.is_err() { return_errno_with_message!(Errno::EINVAL, "invalid day") } let time_result = Time::from_hms(hour as u8, minute as u8, second as u8); if time_result.is_err() { return_errno_with_message!(Errno::EINVAL, "invalid time") } let date_time = PrimitiveDateTime::new(day_result.unwrap(), time_result.unwrap()); let mut sec = date_time.assume_utc().unix_timestamp() as u64; let mut nano_sec: u32 = 0; if self.increament_10ms != 0 { const NSEC_PER_MSEC: u32 = 1000000; sec += self.increament_10ms as u64 / 100; nano_sec = (self.increament_10ms as u32 % 100) * 10 * NSEC_PER_MSEC; } /* Adjust timezone to UTC0. */ if (self.utc_offset & EXFAT_TIME_ZONE_VALID) != 0u8 { sec = Self::ajust_time_zone(sec, self.utc_offset & (!EXFAT_TIME_ZONE_VALID)); } else { // TODO: Use mount info for timezone adjustment. } Ok(Duration::new(sec, nano_sec)) } fn ajust_time_zone(sec: u64, time_zone: u8) -> u64 { if time_zone <= 0x3F { sec + Self::time_zone_sec(time_zone) } else { sec + Self::time_zone_sec(0x80_u8 - time_zone) } } fn time_zone_sec(x: u8) -> u64 { // Each time zone represents 15 minutes. x as u64 * 15 * 60 } }