static void rtc_set_time(RTCState *s) { struct tm *tm = &s->current_tm; struct domain *d = vrtc_domain(s); unsigned long before, after; /* XXX s_time_t */ ASSERT(spin_is_locked(&s->lock)); before = mktime(get_year(tm->tm_year), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); tm->tm_wday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_WEEK]); tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]); tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1; tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]) + 100; after = mktime(get_year(tm->tm_year), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); /* We use the guest's setting of the RTC to define the local-time * offset for this domain. */ d->time_offset_seconds += (after - before); update_domain_wallclock_time(d); /* Also tell qemu-dm about it so it will be remembered for next boot. */ send_timeoffset_req(after - before); }
static uint64_t get_next_alarm(RTCState *s) { int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; int32_t hour, min, sec; rtc_update_time(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 = alarm_hour == -1 ? -1 : 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_hour == -1) { alarm_hour = cur_hour; if (alarm_min == -1) { alarm_min = cur_min; if (alarm_sec == -1) { alarm_sec = cur_sec + 1; } else if (cur_sec > alarm_sec) { alarm_min++; } } else if (cur_min == alarm_min) { if (alarm_sec == -1) { alarm_sec = cur_sec + 1; } else { if (cur_sec > alarm_sec) { alarm_hour++; } } if (alarm_sec == SEC_PER_MIN) { /* wrap to next hour, minutes is not in don't care mode */ alarm_sec = 0; alarm_hour++; } } else if (cur_min > alarm_min) { alarm_hour++; } } else if (cur_hour == alarm_hour) { if (alarm_min == -1) { alarm_min = cur_min; if (alarm_sec == -1) { alarm_sec = cur_sec + 1; } else if (cur_sec > alarm_sec) { alarm_min++; } if (alarm_sec == SEC_PER_MIN) { alarm_sec = 0; alarm_min++; } /* wrap to next day, hour is not in don't care mode */ alarm_min %= MIN_PER_HOUR; } else if (cur_min == alarm_min) { if (alarm_sec == -1) { alarm_sec = cur_sec + 1; } /* wrap to next day, hours+minutes not in don't care mode */ alarm_sec %= SEC_PER_MIN; } } /* values that are still don't care fire at the next min/sec */ if (alarm_min == -1) { alarm_min = 0; } if (alarm_sec == -1) { alarm_sec = 0; } /* keep values in range */ if (alarm_sec == SEC_PER_MIN) { alarm_sec = 0; alarm_min++; } if (alarm_min == MIN_PER_HOUR) { alarm_min = 0; alarm_hour++; } alarm_hour %= HOUR_PER_DAY; hour = alarm_hour - cur_hour; min = hour * MIN_PER_HOUR + alarm_min - cur_min; sec = min * SEC_PER_MIN + alarm_sec - cur_sec; return sec <= 0 ? sec + SEC_PER_DAY : sec; }
/* handle alarm timer */ static void alarm_timer_update(RTCState *s) { uint64_t next_update_time, next_alarm_sec; uint64_t expire_time; int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; int32_t hour, min; struct domain *d = vrtc_domain(s); ASSERT(spin_is_locked(&s->lock)); stop_timer(&s->alarm_timer); if (!(s->hw.cmos_data[RTC_REG_C] & RTC_AF) && !(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); alarm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]); alarm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]); alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]); cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC); next_update_time = next_update_time * NS_PER_USEC + NOW(); if ((s->hw.cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; } else { if (cur_min < alarm_min) { min = alarm_min - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else if (cur_min == alarm_min) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else { min = alarm_min + MIN_PER_HOUR - cur_min; next_alarm_sec = alarm_sec + min * SEC_PER_MIN - cur_sec; } } else { min = alarm_min + MIN_PER_HOUR - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } } else { if (cur_hour < alarm_hour) { hour = alarm_hour - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else { next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } else if (cur_hour == alarm_hour) { if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; } else if (cur_min < alarm_min) { min = alarm_min - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else if (cur_min == alarm_min) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; next_alarm_sec += alarm_min * SEC_PER_MIN + alarm_sec; } } else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else { next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } } expire_time = (next_alarm_sec - 1) * NS_PER_SEC + next_update_time; /* release lock before set timer */ spin_unlock(&s->lock); set_timer(&s->alarm_timer, expire_time); /* fetch lock again */ spin_lock(&s->lock); } }