// HAL RTC support. This file reads the CMOS clock as the fallback wall-clock source. #include #include #include #include #include #include #include #define CMOS_INDEX_PORT 0x70 #define CMOS_DATA_PORT 0x71 #define RTC_REG_SECONDS 0x00 #define RTC_REG_MINUTES 0x02 #define RTC_REG_HOURS 0x04 #define RTC_REG_WEEKDAY 0x06 #define RTC_REG_DAY 0x07 #define RTC_REG_MONTH 0x08 #define RTC_REG_YEAR 0x09 #define RTC_REG_STATUS_A 0x0A #define RTC_REG_STATUS_B 0x0B static uint8_t gCenturyRegister; // Implements the rtc Read operation. static uint8_t RtcRead(uint8_t reg) { __outbyte(CMOS_INDEX_PORT, (uint8_t)(reg | 0x80u)); // keep NMI masked while selecting. return __inbyte(CMOS_DATA_PORT); } // Implements the rtc Update In Progress operation. static bool RtcUpdateInProgress(void) { return (RtcRead(RTC_REG_STATUS_A) & 0x80u) != 0; } // Implements the rtc Bcd To Binary operation. static uint8_t RtcBcdToBinary(uint8_t value) { return (uint8_t)(((value >> 4) * 10u) + (value & 0x0Fu)); } // Implements the rtc Read Stable operation. static bool RtcReadStable(HAL_RTC_TIME* out) { if (!out) return false; uint8_t sec1 = 0, min1 = 0, hour1 = 0, day1 = 0, month1 = 0, year1 = 0, century1 = 0; uint8_t sec2 = 0, min2 = 0, hour2 = 0, day2 = 0, month2 = 0, year2 = 0, century2 = 0; bool stable = false; for (uint32_t tries = 0; tries < 100000u; tries++) { while (RtcUpdateInProgress()) _mm_pause(); sec1 = RtcRead(RTC_REG_SECONDS); min1 = RtcRead(RTC_REG_MINUTES); hour1 = RtcRead(RTC_REG_HOURS); day1 = RtcRead(RTC_REG_DAY); month1 = RtcRead(RTC_REG_MONTH); year1 = RtcRead(RTC_REG_YEAR); century1 = gCenturyRegister ? RtcRead(gCenturyRegister) : 0; while (RtcUpdateInProgress()) _mm_pause(); sec2 = RtcRead(RTC_REG_SECONDS); min2 = RtcRead(RTC_REG_MINUTES); hour2 = RtcRead(RTC_REG_HOURS); day2 = RtcRead(RTC_REG_DAY); month2 = RtcRead(RTC_REG_MONTH); year2 = RtcRead(RTC_REG_YEAR); century2 = gCenturyRegister ? RtcRead(gCenturyRegister) : 0; if (sec1 == sec2 && min1 == min2 && hour1 == hour2 && day1 == day2 && month1 == month2 && year1 == year2 && century1 == century2) { stable = true; break; } } if (!stable) return false; uint8_t statusB = RtcRead(RTC_REG_STATUS_B); bool binary = (statusB & 0x04u) != 0; bool hour24 = (statusB & 0x02u) != 0; bool pm = (!hour24 && (hour1 & 0x80u) != 0); if (!binary) { sec1 = RtcBcdToBinary(sec1); min1 = RtcBcdToBinary(min1); hour1 = (uint8_t)((hour1 & 0x80u) | RtcBcdToBinary((uint8_t)(hour1 & 0x7Fu))); day1 = RtcBcdToBinary(day1); month1 = RtcBcdToBinary(month1); year1 = RtcBcdToBinary(year1); if (gCenturyRegister) century1 = RtcBcdToBinary(century1); } if (!hour24) { hour1 = (uint8_t)(hour1 & 0x7Fu); if (pm && hour1 < 12u) hour1 = (uint8_t)(hour1 + 12u); if (!pm && hour1 == 12u) hour1 = 0; } uint16_t year; if (gCenturyRegister && century1) { year = (uint16_t)((uint16_t)century1 * 100u + year1); } else { year = (uint16_t)((year1 < 70u) ? (2000u + year1) : (1900u + year1)); } out->Year = year; out->Month = month1; out->Day = day1; out->Hour = hour1; out->Minute = min1; out->Second = sec1; out->Nanosecond = 0; out->Flags = HAL_RTC_TIME_FLAG_VALID | HAL_RTC_TIME_FLAG_RTC; out->Reserved = 0; return true; } // Implements the rtc Leap Year operation. static bool RtcLeapYear(uint16_t year) { return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u); } // Implements the rtc Days In Month operation. static uint8_t RtcDaysInMonth(uint16_t year, uint8_t month) { static const uint8_t days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; if (month < 1u || month > 12u) return 0; if (month == 2u && RtcLeapYear(year)) return 29; return days[month - 1u]; } // Implements the rtc Valid operation. static bool RtcValid(const HAL_RTC_TIME* t) { if (!t) return false; if (t->Year < 1970u || t->Year > 9999u) return false; if (t->Month < 1u || t->Month > 12u) return false; uint8_t dim = RtcDaysInMonth(t->Year, t->Month); if (t->Day < 1u || t->Day > dim) return false; if (t->Hour > 23u || t->Minute > 59u || t->Second > 59u) return false; return true; } // Implements the rtc Query Time operation. static KSTATUS HalRtcQueryTime(HAL_RTC_TIME* outTime) { if (!outTime) return NTLSTATUS_INVALID_PARAMETER; HAL_RTC_TIME t; memset(&t, 0, sizeof(t)); if (!RtcReadStable(&t) || !RtcValid(&t)) return NTLSTATUS_DEVICE_ERROR; *outTime = t; return NTLSTATUS_SUCCESS; } // Implements the rtc Set Century Register operation. void HalRtcSetCenturyRegister(uint8_t centuryRegister) { gCenturyRegister = centuryRegister; } // Implements the rtc Initialize operation. void HalRtcInitialize(HAL_RTC* out) { if (!out) return; out->QueryTime = HalRtcQueryTime; HAL_RTC_TIME t; if (HalRtcQueryTime(&t) == NTLSTATUS_SUCCESS) { HalDbgPrint("HAL: RTC %04u-%02u-%02u %02u:%02u:%02u century-reg=0x%02x\n", t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second, gCenturyRegister); } else { HalDbgPrint("HAL: RTC unavailable or invalid\n"); } }