Add the alarm check when update cycle ended. If alarm is fired, also AIE bit is setting, then raise a interrupt Signed-off-by: Yang Zhang <yang.z.zhang@xxxxxxxxx> --- hw/mc146818rtc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 46 insertions(+), 2 deletions(-) diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index fae049e..384bdc1 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -114,6 +114,7 @@ typedef struct RTCState { static void rtc_set_time(RTCState *s); static void rtc_calibrate_time(RTCState *s); static void rtc_set_cmos(RTCState *s); +static inline int rtc_from_bcd(RTCState *s, int a); static int32_t divider_reset; @@ -267,15 +268,58 @@ static void rtc_update_timer(void *opaque) } } +static inline uint8_t convert_hour(RTCState *s, uint8_t hour) +{ + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) { + hour %= 12; + if (s->cmos_data[RTC_HOURS] & 0x80) { + hour += 12; + } + } + return hour; +} +static uint32_t check_alarm(RTCState *s) +{ + uint8_t alarm_hour, alarm_min, alarm_sec; + uint8_t cur_hour, cur_min, cur_sec; + + rtc_calibrate_time(s); + rtc_set_cmos(s); + + alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]); + alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]); + alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]); + alarm_hour = convert_hour(s, alarm_hour); + + cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); + cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); + cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]); + cur_hour = convert_hour(s, cur_hour); + + if (((alarm_sec & 0xc0) == 0xc0 || alarm_sec == cur_sec) && + ((alarm_min & 0xc0) == 0xc0 || alarm_min == cur_min) && + ((alarm_hour & 0xc0) == 0xc0 || alarm_hour == cur_hour)) { + return 1; + } + return 0; + +} + static void rtc_update_timer2(void *opaque) { RTCState *s = opaque; if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) { s->cmos_data[RTC_REG_C] |= REG_C_UF; + if (check_alarm(s)) { + s->cmos_data[RTC_REG_C] |= REG_C_AF; + } + s->cmos_data[RTC_REG_A] &= ~REG_A_UIP; - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - qemu_irq_raise(s->irq); + if (s->cmos_data[RTC_REG_B] & (REG_B_AIE | REG_B_UIE)) { + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); + } } check_update_timer(s); } -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html