RTC.cpp 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #include "RTC.h"
  2. #include "CMOS.h"
  3. #include <AK/Assertions.h>
  4. namespace RTC {
  5. static time_t s_boot_time;
  6. void initialize()
  7. {
  8. byte cmos_mode = CMOS::read(0x0b);
  9. cmos_mode |= 2; // 24 hour mode
  10. cmos_mode |= 4; // No BCD mode
  11. CMOS::write(0x0b, cmos_mode);
  12. s_boot_time = now();
  13. }
  14. time_t boot_time()
  15. {
  16. return s_boot_time;
  17. }
  18. static bool update_in_progress()
  19. {
  20. return CMOS::read(0x0a) & 0x80;
  21. }
  22. inline bool is_leap_year(unsigned year)
  23. {
  24. return ((year % 4 == 0) && ((year % 100 != 0) || (year % 400) == 0));
  25. }
  26. static unsigned days_in_months_since_start_of_year(unsigned month, unsigned year)
  27. {
  28. switch (month) {
  29. case 11: return 30;
  30. case 10: return 31;
  31. case 9: return 30;
  32. case 8: return 31;
  33. case 7: return 31;
  34. case 6: return 30;
  35. case 5: return 31;
  36. case 4: return 30;
  37. case 3: return 31;
  38. case 2:
  39. if (is_leap_year(year))
  40. return 29;
  41. return 28;
  42. case 1: return 31;
  43. default: return 0;
  44. }
  45. }
  46. static unsigned days_in_years_since_epoch(unsigned year)
  47. {
  48. unsigned days = 0;
  49. while (year > 1969) {
  50. days += 365;
  51. if (is_leap_year(year))
  52. ++days;
  53. --year;
  54. }
  55. return days;
  56. }
  57. time_t now()
  58. {
  59. // FIXME: We should probably do something more robust here.
  60. // Perhaps read all the values twice and verify that they were identical.
  61. // We don't want to be caught in the middle of an RTC register update.
  62. while (update_in_progress())
  63. ;
  64. unsigned year = (CMOS::read(0x32) * 100) + CMOS::read(0x09);
  65. unsigned month = CMOS::read(0x08);
  66. unsigned day = CMOS::read(0x07);
  67. unsigned hour = CMOS::read(0x04);
  68. unsigned minute = CMOS::read(0x02);
  69. unsigned second = CMOS::read(0x00);
  70. ASSERT(year >= 2018);
  71. return days_in_years_since_epoch(year - 1) * 86400
  72. + days_in_months_since_start_of_year(month - 1, year) * 86400
  73. + day * 86400
  74. + hour * 3600
  75. + minute * 60
  76. + second;
  77. }
  78. }