mirror of
https://github.com/RGBCube/serenity
synced 2025-05-30 23:58:12 +00:00

We only use the RTC code in the Kernel, so it doesn't make sense to make the RTC namespace outside of it. In addition to that, we will need later on to use the RTC in an x86 specific manner and this will help us to use this code in such fashion.
118 lines
3.3 KiB
C++
118 lines
3.3 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Time.h>
|
|
#include <Kernel/Arch/x86/IO.h>
|
|
#include <Kernel/CMOS.h>
|
|
#include <Kernel/RTC.h>
|
|
|
|
namespace Kernel::RTC {
|
|
|
|
static time_t s_boot_time;
|
|
|
|
void initialize()
|
|
{
|
|
s_boot_time = now();
|
|
}
|
|
|
|
time_t boot_time()
|
|
{
|
|
return s_boot_time;
|
|
}
|
|
|
|
static bool update_in_progress()
|
|
{
|
|
return CMOS::read(0x0a) & 0x80;
|
|
}
|
|
|
|
static u8 bcd_to_binary(u8 bcd)
|
|
{
|
|
return (bcd & 0x0F) + ((bcd >> 4) * 10);
|
|
}
|
|
|
|
static bool try_to_read_registers(unsigned& year, unsigned& month, unsigned& day, unsigned& hour, unsigned& minute, unsigned& second)
|
|
{
|
|
// Note: Let's wait 0.01 seconds until we stop trying to query the RTC CMOS
|
|
size_t time_passed_in_milliseconds = 0;
|
|
bool update_in_progress_ended_successfully = false;
|
|
while (time_passed_in_milliseconds < 100) {
|
|
if (!update_in_progress()) {
|
|
update_in_progress_ended_successfully = true;
|
|
break;
|
|
}
|
|
IO::delay(1000);
|
|
time_passed_in_milliseconds++;
|
|
}
|
|
|
|
if (!update_in_progress_ended_successfully) {
|
|
year = 1970;
|
|
month = 1;
|
|
day = 1;
|
|
hour = 0;
|
|
minute = 0;
|
|
second = 0;
|
|
return false;
|
|
}
|
|
|
|
u8 status_b = CMOS::read(0x0b);
|
|
|
|
second = CMOS::read(0x00);
|
|
minute = CMOS::read(0x02);
|
|
hour = CMOS::read(0x04);
|
|
day = CMOS::read(0x07);
|
|
month = CMOS::read(0x08);
|
|
year = CMOS::read(0x09);
|
|
|
|
bool is_pm = hour & 0x80;
|
|
|
|
if (!(status_b & 0x04)) {
|
|
second = bcd_to_binary(second);
|
|
minute = bcd_to_binary(minute);
|
|
hour = bcd_to_binary(hour & 0x7F);
|
|
day = bcd_to_binary(day);
|
|
month = bcd_to_binary(month);
|
|
year = bcd_to_binary(year);
|
|
}
|
|
|
|
if (!(status_b & 0x02)) {
|
|
// In the 12 hour clock, midnight and noon are 12, not 0. Map it to 0.
|
|
hour %= 12;
|
|
if (is_pm)
|
|
hour += 12;
|
|
}
|
|
|
|
year += 2000;
|
|
return true;
|
|
}
|
|
|
|
time_t now()
|
|
{
|
|
|
|
auto check_registers_against_preloaded_values = [](unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute, unsigned second) {
|
|
unsigned checked_year, checked_month, checked_day, checked_hour, checked_minute, checked_second;
|
|
if (!try_to_read_registers(checked_year, checked_month, checked_day, checked_hour, checked_minute, checked_second))
|
|
return false;
|
|
return checked_year == year && checked_month == month && checked_day == day && checked_hour == hour && checked_minute == minute && checked_second == second;
|
|
};
|
|
|
|
unsigned year, month, day, hour, minute, second;
|
|
bool did_read_rtc_successfully = false;
|
|
for (size_t attempt = 0; attempt < 5; attempt++) {
|
|
if (!try_to_read_registers(year, month, day, hour, minute, second))
|
|
break;
|
|
if (check_registers_against_preloaded_values(year, month, day, hour, minute, second)) {
|
|
did_read_rtc_successfully = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
dmesgln("RTC: {} Year: {}, month: {}, day: {}, hour: {}, minute: {}, second: {}", (did_read_rtc_successfully ? "" : "(failed to read)"), year, month, day, hour, minute, second);
|
|
|
|
time_t days_since_epoch = years_to_days_since_epoch(year) + day_of_year(year, month, day);
|
|
return ((days_since_epoch * 24 + hour) * 60 + minute) * 60 + second;
|
|
}
|
|
|
|
}
|