commit f650305967f3e9a2fe96f59de3062fd9e8b189d0
Author: balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Date:   Sun Feb 17 11:42:19 2008 +0000

    Unify RTCs that use host time, fix M48t59 alarm.
    
    
    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3984 c046a42c-6fe2-441c-8c8c-71466251a162

diff --git a/hw/m48t59.c b/hw/m48t59.c
index 45c05a0..b781f50 100644
--- a/hw/m48t59.c
+++ b/hw/m48t59.c
@@ -53,7 +53,7 @@ struct m48t59_t {
     time_t   time_offset;
     time_t   stop_time;
     /* Alarm & watchdog */
-    time_t   alarm;
+    struct tm alarm;
     struct QEMUTimer *alrm_timer;
     struct QEMUTimer *wd_timer;
     /* NVRAM storage */
@@ -74,35 +74,10 @@ static inline uint8_t fromBCD (uint8_t BCD)
     return ((BCD >> 4) * 10) + (BCD & 0x0F);
 }
 
-/* RTC management helpers */
-static void get_time (m48t59_t *NVRAM, struct tm *tm)
-{
-    time_t t;
-
-    t = time(NULL) + NVRAM->time_offset;
-#ifdef _WIN32
-    memcpy(tm,localtime(&t),sizeof(*tm));
-#else
-    if (rtc_utc)
-        gmtime_r (&t, tm);
-    else
-        localtime_r (&t, tm) ;
-#endif
-}
-
-static void set_time (m48t59_t *NVRAM, struct tm *tm)
-{
-    time_t now, new_time;
-
-    new_time = mktime(tm);
-    now = time(NULL);
-    NVRAM->time_offset = new_time - now;
-}
-
 /* Alarm management */
 static void alarm_cb (void *opaque)
 {
-    struct tm tm, tm_now;
+    struct tm tm;
     uint64_t next_time;
     m48t59_t *NVRAM = opaque;
 
@@ -111,62 +86,62 @@ static void alarm_cb (void *opaque)
 	(NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
 	(NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
 	(NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-	/* Repeat once a month */
-	get_time(NVRAM, &tm_now);
-	memcpy(&tm, &tm_now, sizeof(struct tm));
-	tm.tm_mon++;
-	if (tm.tm_mon == 13) {
-	    tm.tm_mon = 1;
-	    tm.tm_year++;
-	}
-	next_time = mktime(&tm);
+        /* Repeat once a month */
+        qemu_get_timedate(&tm, NVRAM->time_offset);
+        tm.tm_mon++;
+        if (tm.tm_mon == 13) {
+            tm.tm_mon = 1;
+            tm.tm_year++;
+        }
+        next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset;
     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
 	       (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
 	       (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-	/* Repeat once a day */
-	next_time = 24 * 60 * 60 + mktime(&tm_now);
+        /* Repeat once a day */
+        next_time = 24 * 60 * 60;
     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
 	       (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-	/* Repeat once an hour */
-	next_time = 60 * 60 + mktime(&tm_now);
+        /* Repeat once an hour */
+        next_time = 60 * 60;
     } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
 	       (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-	/* Repeat once a minute */
-	next_time = 60 + mktime(&tm_now);
+        /* Repeat once a minute */
+        next_time = 60;
     } else {
-	/* Repeat once a second */
-	next_time = 1 + mktime(&tm_now);
+        /* Repeat once a second */
+        next_time = 1;
     }
-    qemu_mod_timer(NVRAM->alrm_timer, next_time * 1000);
+    qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock(vm_clock) +
+                    next_time * 1000);
     qemu_set_irq(NVRAM->IRQ, 0);
 }
 
+static void set_alarm (m48t59_t *NVRAM)
+{
+    int diff;
+    if (NVRAM->alrm_timer != NULL) {
+        qemu_del_timer(NVRAM->alrm_timer);
+        diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
+        if (diff > 0)
+            qemu_mod_timer(NVRAM->alrm_timer, diff * 1000);
+    }
+}
 
-static void get_alarm (m48t59_t *NVRAM, struct tm *tm)
+/* RTC management helpers */
+static inline void get_time (m48t59_t *NVRAM, struct tm *tm)
 {
-#ifdef _WIN32
-    memcpy(tm,localtime(&NVRAM->alarm),sizeof(*tm));
-#else
-    if (rtc_utc)
-        gmtime_r (&NVRAM->alarm, tm);
-    else
-        localtime_r (&NVRAM->alarm, tm);
-#endif
+    qemu_get_timedate(tm, NVRAM->time_offset);
 }
 
-static void set_alarm (m48t59_t *NVRAM, struct tm *tm)
+static void set_time (m48t59_t *NVRAM, struct tm *tm)
 {
-    NVRAM->alarm = mktime(tm);
-    if (NVRAM->alrm_timer != NULL) {
-        qemu_del_timer(NVRAM->alrm_timer);
-        if (NVRAM->alarm - time(NULL) > 0)
-            qemu_mod_timer(NVRAM->alrm_timer, NVRAM->alarm * 1000);
-    }
+    NVRAM->time_offset = qemu_timedate_diff(tm);
+    set_alarm(NVRAM);
 }
 
 /* Watchdog management */
@@ -229,40 +204,36 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
         /* alarm seconds */
         tmp = fromBCD(val & 0x7F);
         if (tmp >= 0 && tmp <= 59) {
-            get_alarm(NVRAM, &tm);
-            tm.tm_sec = tmp;
+            NVRAM->alarm.tm_sec = tmp;
             NVRAM->buffer[0x1FF2] = val;
-            set_alarm(NVRAM, &tm);
+            set_alarm(NVRAM);
         }
         break;
     case 0x1FF3:
         /* alarm minutes */
         tmp = fromBCD(val & 0x7F);
         if (tmp >= 0 && tmp <= 59) {
-            get_alarm(NVRAM, &tm);
-            tm.tm_min = tmp;
+            NVRAM->alarm.tm_min = tmp;
             NVRAM->buffer[0x1FF3] = val;
-            set_alarm(NVRAM, &tm);
+            set_alarm(NVRAM);
         }
         break;
     case 0x1FF4:
         /* alarm hours */
         tmp = fromBCD(val & 0x3F);
         if (tmp >= 0 && tmp <= 23) {
-            get_alarm(NVRAM, &tm);
-            tm.tm_hour = tmp;
+            NVRAM->alarm.tm_hour = tmp;
             NVRAM->buffer[0x1FF4] = val;
-            set_alarm(NVRAM, &tm);
+            set_alarm(NVRAM);
         }
         break;
     case 0x1FF5:
         /* alarm date */
         tmp = fromBCD(val & 0x1F);
         if (tmp != 0) {
-            get_alarm(NVRAM, &tm);
-            tm.tm_mday = tmp;
+            NVRAM->alarm.tm_mday = tmp;
             NVRAM->buffer[0x1FF5] = val;
-            set_alarm(NVRAM, &tm);
+            set_alarm(NVRAM);
         }
         break;
     case 0x1FF6:
@@ -288,7 +259,7 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
 	    tm.tm_sec = tmp;
 	    set_time(NVRAM, &tm);
 	}
-       if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
+        if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
 	    if (val & 0x80) {
 		NVRAM->stop_time = time(NULL);
 	    } else {
@@ -296,7 +267,7 @@ void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
 		NVRAM->stop_time = 0;
 	    }
 	}
-       NVRAM->buffer[addr] = val & 0x80;
+        NVRAM->buffer[addr] = val & 0x80;
         break;
     case 0x1FFA:
     case 0x07FA:
@@ -682,6 +653,7 @@ m48t59_t *m48t59_init (qemu_irq IRQ, target_phys_addr_t mem_base,
         s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
     }
     s->lock = 0;
+    qemu_get_timedate(&s->alarm, 0);
 
     qemu_register_reset(m48t59_reset, s);
     save_base = mem_base ? mem_base : io_base;
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index e1e6427..30bb044 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -392,24 +392,14 @@ void rtc_set_date(RTCState *s, const struct tm *tm)
 
 static void rtc_set_date_from_host(RTCState *s)
 {
-    time_t ti;
-    struct tm *tm;
+    struct tm tm;
     int val;
 
     /* set the CMOS date */
-    if (rtc_start_date == -1) {
-        time(&ti);
-        if (rtc_utc)
-            tm = gmtime(&ti);
-        else
-            tm = localtime(&ti);
-    } else {
-        ti = rtc_start_date;
-        tm = gmtime(&ti);
-    }
-    rtc_set_date(s, tm);
+    qemu_get_timedate(&tm, 0);
+    rtc_set_date(s, &tm);
 
-    val = to_bcd(s, (tm->tm_year / 100) + 19);
+    val = to_bcd(s, (tm.tm_year / 100) + 19);
     rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val);
     rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val);
 }
diff --git a/hw/omap.c b/hw/omap.c
index ce63597..ee426dc 100644
--- a/hw/omap.c
+++ b/hw/omap.c
@@ -4345,7 +4345,6 @@ struct omap_rtc_s {
     int pm_am;
     int auto_comp;
     int round;
-    struct tm *(*convert)(const time_t *timep, struct tm *result);
     struct tm alarm_tm;
     time_t alarm_ti;
 
@@ -4668,7 +4667,7 @@ static void omap_rtc_tick(void *opaque)
         s->round = 0;
     }
 
-    localtime_r(&s->ti, &s->current_tm);
+    memcpy(&s->current_tm, localtime(&s->ti), sizeof(s->current_tm));
 
     if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) {
         s->status |= 0x40;
@@ -4719,6 +4718,8 @@ static void omap_rtc_tick(void *opaque)
 
 static void omap_rtc_reset(struct omap_rtc_s *s)
 {
+    struct tm tm;
+
     s->interrupts = 0;
     s->comp_reg = 0;
     s->running = 0;
@@ -4729,8 +4730,8 @@ static void omap_rtc_reset(struct omap_rtc_s *s)
     memset(&s->alarm_tm, 0, sizeof(s->alarm_tm));
     s->alarm_tm.tm_mday = 0x01;
     s->status = 1 << 7;
-    time(&s->ti);
-    s->ti = mktime(s->convert(&s->ti, &s->current_tm));
+    qemu_get_timedate(&tm, 0);
+    s->ti = mktime(&tm);
 
     omap_rtc_alarm_update(s);
     omap_rtc_tick(s);
@@ -4747,7 +4748,6 @@ struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base,
     s->irq = irq[0];
     s->alarm = irq[1];
     s->clk = qemu_new_timer(rt_clock, omap_rtc_tick, s);
-    s->convert = rtc_utc ? gmtime_r : localtime_r;
 
     omap_rtc_reset(s);
 
diff --git a/hw/pl031.c b/hw/pl031.c
index 68e9005..bb5a69d 100644
--- a/hw/pl031.c
+++ b/hw/pl031.c
@@ -195,8 +195,7 @@ void pl031_init(uint32_t base, qemu_irq irq)
 {
     int iomemtype;
     pl031_state *s;
-    time_t ti;
-    struct tm *tm;
+    struct tm tm;
 
     s = qemu_mallocz(sizeof(pl031_state));
     if (!s)
@@ -211,12 +210,8 @@ void pl031_init(uint32_t base, qemu_irq irq)
     s->base = base;
     s->irq  = irq;
     /* ??? We assume vm_clock is zero at this point.  */
-    time(&ti);
-    if (rtc_utc)
-        tm = gmtime(&ti);
-    else
-        tm = localtime(&ti);
-    s->tick_offset = mktime(tm);
+    qemu_get_timedate(&tm, 0);
+    s->tick_offset = mktime(&tm);
 
     s->timer = qemu_new_timer(vm_clock, pl031_interrupt, s);
 }
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index 14e3b95..0cb1672 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -1183,27 +1183,22 @@ static CPUWriteMemoryFunc *pxa2xx_rtc_writefn[] = {
 
 static void pxa2xx_rtc_init(struct pxa2xx_state_s *s)
 {
-    struct tm *tm;
-    time_t ti;
+    struct tm tm;
     int wom;
 
     s->rttr = 0x7fff;
     s->rtsr = 0;
 
-    time(&ti);
-    if (rtc_utc)
-        tm = gmtime(&ti);
-    else
-        tm = localtime(&ti);
-    wom = ((tm->tm_mday - 1) / 7) + 1;
-
-    s->last_rcnr = (uint32_t) ti;
-    s->last_rdcr = (wom << 20) | ((tm->tm_wday + 1) << 17) |
-            (tm->tm_hour << 12) | (tm->tm_min << 6) | tm->tm_sec;
-    s->last_rycr = ((tm->tm_year + 1900) << 9) |
-            ((tm->tm_mon + 1) << 5) | tm->tm_mday;
-    s->last_swcr = (tm->tm_hour << 19) |
-            (tm->tm_min << 13) | (tm->tm_sec << 7);
+    qemu_get_timedate(&tm, 0);
+    wom = ((tm.tm_mday - 1) / 7) + 1;
+
+    s->last_rcnr = (uint32_t) mktime(&tm);
+    s->last_rdcr = (wom << 20) | ((tm.tm_wday + 1) << 17) |
+            (tm.tm_hour << 12) | (tm.tm_min << 6) | tm.tm_sec;
+    s->last_rycr = ((tm.tm_year + 1900) << 9) |
+            ((tm.tm_mon + 1) << 5) | tm.tm_mday;
+    s->last_swcr = (tm.tm_hour << 19) |
+            (tm.tm_min << 13) | (tm.tm_sec << 7);
     s->last_rtcpicr = 0;
     s->last_hz = s->last_sw = s->last_pi = qemu_get_clock(rt_clock);
 
diff --git a/qemu-common.h b/qemu-common.h
index e8ea687..746dcc5 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -76,6 +76,9 @@ int qemu_bh_poll(void);
 
 uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
 
+void qemu_get_timedate(struct tm *tm, int offset);
+int qemu_timedate_diff(struct tm *tm);
+
 /* cutils.c */
 void pstrcpy(char *buf, int buf_size, const char *str);
 char *pstrcat(char *buf, int buf_size, const char *s);
diff --git a/sysemu.h b/sysemu.h
index 29377bf..296f179 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -71,8 +71,6 @@ void do_info_slirp(void);
 
 extern int ram_size;
 extern int bios_size;
-extern int rtc_utc;
-extern int rtc_start_date;
 extern int cirrus_vga_enabled;
 extern int vmsvga_enabled;
 extern int graphic_width;
diff --git a/vl.c b/vl.c
index c87e8bc..d5a1dbc 100644
--- a/vl.c
+++ b/vl.c
@@ -180,8 +180,8 @@ int pit_min_timer_count = 0;
 int nb_nics;
 NICInfo nd_table[MAX_NICS];
 int vm_running;
-int rtc_utc = 1;
-int rtc_start_date = -1; /* -1 means now */
+static int rtc_utc = 1;
+static int rtc_date_offset = -1; /* -1 means no change */
 int cirrus_vga_enabled = 1;
 int vmsvga_enabled = 0;
 #ifdef TARGET_SPARC
@@ -1565,6 +1565,43 @@ static void quit_timers(void)
 }
 
 /***********************************************************/
+/* host time/date access */
+void qemu_get_timedate(struct tm *tm, int offset)
+{
+    time_t ti;
+    struct tm *ret;
+
+    time(&ti);
+    ti += offset;
+    if (rtc_date_offset == -1) {
+        if (rtc_utc)
+            ret = gmtime(&ti);
+        else
+            ret = localtime(&ti);
+    } else {
+        ti -= rtc_date_offset;
+        ret = gmtime(&ti);
+    }
+
+    memcpy(tm, ret, sizeof(struct tm));
+}
+
+int qemu_timedate_diff(struct tm *tm)
+{
+    time_t seconds;
+
+    if (rtc_date_offset == -1)
+        if (rtc_utc)
+            seconds = mktimegm(tm);
+        else
+            seconds = mktime(tm);
+    else
+        seconds = mktimegm(tm) + rtc_date_offset;
+
+    return seconds - time(NULL);
+}
+
+/***********************************************************/
 /* character device */
 
 static void qemu_chr_event(CharDriverState *s, int event)
@@ -8698,8 +8735,9 @@ int main(int argc, char **argv)
             case QEMU_OPTION_startdate:
                 {
                     struct tm tm;
+                    time_t rtc_start_date;
                     if (!strcmp(optarg, "now")) {
-                        rtc_start_date = -1;
+                        rtc_date_offset = -1;
                     } else {
                         if (sscanf(optarg, "%d-%d-%dT%d:%d:%d",
                                &tm.tm_year,
@@ -8728,6 +8766,7 @@ int main(int argc, char **argv)
                                     "'now' or '2006-06-17T16:01:21' or '2006-06-17'\n");
                             exit(1);
                         }
+                        rtc_date_offset = time(NULL) - rtc_start_date;
                     }
                 }
                 break;
