/* #define HOWFAR */ /* * Real time clock driver * * (C) 1984 UniSoft Corp. of Berkeley CA * * UniPlus Source Code. This program is proprietary * with Unisoft Corporation and is not to be reproduced * or used in any manner except as authorized in * writing by Unisoft. * * Only reads and writes of 4 bytes (sizeof time_t, the standard unix * representation of time) are allowed for the real time clock. * The rtime structure contains the time most recently read or written. * * struct rtime { See page 35 LHRM * time_t rt_tod; Seconds since the Epoch (unix time) * long rt_alrm; Seconds remaining to trigger alarm (unused) * short rt_year; year (0 - 15) * short rt_day; julian day (1 - 366) * short rt_hour; hour (0 - 23) * short rt_min; minute (0 - 59) * short rt_sec; second (0 - 59) * short rt_tenth; tenths of a second (0 - 9) * }; */ #include "sys/param.h" #include "sys/config.h" #include "sys/mmu.h" #include "sys/types.h" #include "sys/sysmacros.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/utsname.h" #include "sys/buf.h" #include "sys/elog.h" #include "sys/erec.h" #include "sys/iobuf.h" #include "sys/systm.h" #include "sys/var.h" #include "setjmp.h" #include "sys/reg.h" #include "sys/tty.h" #include "sys/local.h" struct rtime rtime; int rtcrwait; /* flag to sleep on waiting for read to complete */ /* * Read the real time clock. The command to do this is issued through * the COPS. In response, the keyboard gets 6 special interrupts with * the data. kbintr stores this data in rtime and calls rtcsettod when * the sixth one has been handled. rtcsettod calculates the time as * unix likes to think of it, which is as it is returned. */ /* ARGSUSED */ rtcread(dev) dev_t dev; { time_t rt; if (u.u_count != sizeof(time_t)) { u.u_error = ENXIO; return; } rt = rtcdoread(); iomove((caddr_t)&rt, sizeof(time_t), B_READ); } /* * This routine is normally called from rtcread. However the kernel * reads the clock by calling rtcdoread directly from clkset. */ rtcdoread() { l2copscmd(READCLOCK); rtcrwait = 1; while (rtcrwait) (void) sleep((caddr_t)&rtcrwait, TTIPRI); return(rtime.rt_tod); } /* * Set rt_tod (real time in seconds since epoch) given the real * time clock time currently in the rest of the rtime structure. * Those values (rt_year, rt_day, rt_hour, rt_min, rt_sec) are * set in response to l2copscmd(READCLOCK), and are returned through * special keyboard interrupts (kbintr). */ #define dsize(x) (x%4==0 ?366 :365) #define YEAR 70 rtcsettod () { register struct rtime *p = &rtime; register short i, j; i = -1; for (j=YEAR; j-YEARrt_year; j++) i += dsize(j); p->rt_tod = i + p->rt_day; /* Days since epoch */ p->rt_tod *= 24; p->rt_tod += p->rt_hour; p->rt_tod *= 60; p->rt_tod += p->rt_min; p->rt_tod *= 60; p->rt_tod += p->rt_sec; #ifdef HOWFAR printf("DATE: %d.%d %d:%d.%d\n", p->rt_year, p->rt_day, p->rt_hour, p->rt_min, p->rt_sec); #endif HOWFAR if (rtcrwait) { rtcrwait = 0; wakeup((caddr_t)&rtcrwait); } } /* * Set the real time clock to the time indicated. Note that nt is in * seconds since midnight 1/1/1970 GMT. The timezone has already been * corrected for. */ /* ARGSUSED */ rtcwrite(dev) dev_t dev; { register struct rtime *p = &rtime; register long nt; register long i, j; if (u.u_count != sizeof(time_t)) { u.u_error = ENXIO; return; } iomove((caddr_t)&rtime.rt_tod, sizeof(time_t), B_WRITE); nt = p->rt_tod; i = nt % 86400; /* 86400 = 24*60*60 = number secs./day */ j = nt / 86400; if (i < 0) { i += 86400; j -= 1; } nt = j; p->rt_sec = i % 60; i /= 60; p->rt_min = i % 60; i /= 60; p->rt_hour = i % 24; j = YEAR; for (j=YEAR; i=dsize(j); j++) { if (nt < i) break; nt -= i; } p->rt_year = j - YEAR; if (p->rt_year < 10) { #ifdef HOWFAR printf("can't remember dates before 1980\n"); #endif HOWFAR u.u_error = EINVAL; return; } p->rt_day = ++nt; l2copscmd(SETCLOCK); /* stop clock, start setup mode */ for (i=0; i<5; i++) l2copscmd(CLKNIBBLE); /* no alarm implemented yet */ l2copscmd(CLKNIBBLE | (p->rt_year - 10)); i = p->rt_day / 10; j = i / 10; l2copscmd((char)(CLKNIBBLE | j)); l2copscmd((char)(CLKNIBBLE | (i % 10))); l2copscmd(CLKNIBBLE | (p->rt_day % 10)); l2copscmd(CLKNIBBLE | (p->rt_hour / 10)); l2copscmd(CLKNIBBLE | (p->rt_hour % 10)); l2copscmd(CLKNIBBLE | (p->rt_min / 10)); l2copscmd(CLKNIBBLE | (p->rt_min % 10)); l2copscmd(CLKNIBBLE | (p->rt_sec / 10)); l2copscmd(CLKNIBBLE | (p->rt_sec % 10)); l2copscmd(STRTCLOCK); /* start clock, leave timer disabled */ /* l2copscmd(READCLOCK); /* Now read it back */ }