*BSD News Article 10025


Return to BSD News archive

Received: by minnie.vk1xwt.ampr.org with NNTP
	id AA7034 ; Fri, 15 Jan 93 19:03:50 EST
Path: sserve!manuel.anu.edu.au!munnari.oz.au!uunet!news.univie.ac.at!fstgds15.tu-graz.ac.at!fstgds01!chmr
From: chmr@fstgds01 (Christoph Robitschko)
Newsgroups: comp.unix.bsd
Subject: Re: 386BSD/clock.c/CMOS-Time
Date: 18 Jan 1993 08:17:27 GMT
Organization: Technical University of Graz, Austria
Lines: 524
Message-ID: <1jdp2nINNhjn@fstgds15.tu-graz.ac.at>
References: <1993Jan15.143725.153556@eratu.rz.uni-konstanz.de>
NNTP-Posting-Host: fstgds01.tu-graz.ac.at
X-Newsreader: TIN [version 1.1 PL7]


[ This is a second attempt. The patch in my first post had two mistakes:
  in machdep.c.diff, "settodr" should have been "resettodr", and in clock.c,
  the LEAPYEAR macro was wrong. ]

In article <1993Jan15.143725.153556@eratu.rz.uni-konstanz.de> Z. Horvat (zh@nike.rz.uni-konstanz.de) wrote:
:> In article 1j6aanINNll@fstgds15.tu-graz.ac.at, chmr@fstgds01 (Christoph Robitschko) writes:
:> >In article <1993Jan13.090434.141136@eratu.rz.uni-konstanz.de> Z. Horvat (zh@nike.rz.uni-konstanz.de) wrote:
:> >:> I noticed that 386BSD will not update the CMOS Time/Date after
:> >:> setting it in the running system.
:> >:> Maybe someone out there has already patches available ?
:> >:> Thanks in advance ...
:> >:> 
:> >I have put my modified clock  driver on ref.tfs.com in ~chmr/time .
:> >This can write the time back to the CMOS.
:> >
:> >							Christoph
:> 
:> Christoph,
:> 
:> first, thank you very much. 
:> I have already tried some time ago to contact ref.tfs.com for anon ftp, 
:> but the link here from germany is very poor. Would you mind posting
:> your driver ?
:> Hmm, maybe there is a mirror site of this host somwhere here in europe ?

So, here goes.
This is also available from ftp.tu-graz.ac.at:/pub/386BSD/0.1/unofficial/time .


							Christoph

P.S.: Headers are probably incorrect; Please respond to chmr@edvz.tu-graz.ac.at


--- CUT HERE ---
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	README
#	README.localtz
#	clock.c
#	cpu.h.diff
#	kern_time.c.diff
#	machdep.c.diff
#	rtc.h.diff
#
echo x - README
sed 's/^X//' >README << 'END-of-README'
XThis is a modified version of the realtime clock support functions.
X
Xadvantages over the original version:
X	fixed many bugs
X	is able to write the time back to the CMOS
X		(explicitly when you set it via date, and automagically
X		on shutdown).
X	has a settable option RTC_LOCALTZ which allows it to satisfy the
X		needs of 386BSD and DOS (386bsd wants GMT; DOS wants
X		localtime in the CMOS).
X		WARNING: This option is not recommended unless you know
X		exactly what you are doing ! (see README.localtz for details)
X
XIt does not have code to support daylight savings time, because this does
Xnot belong in the kernel, and different countries have different requirements
Xfor DST.
X
X
XThis dorectory is intentionally writable: You are welcome to put your
Xenhancements here, if you also write a README.enhancement with your name
Xand e-mail address in it.
X
X
X							Christoph Robitschko
X							chmr@edvz.tu-graz.ac.at
END-of-README
echo x - README.localtz
sed 's/^X//' >README.localtz << 'END-of-README.localtz'
XThe option RTC_LOCALTZ does the following:
X
X	disable the use of the "timezone" config line
X	On startup, read the time (local time) from the CMOS,
X		read and validate timezone information (from the CMOS ALARM
X		registers), and correct the
X		time to get GMT.
X	On shutdown or when the time has been changed, correct the time
X		to local time and write it to the CMOS.
X	When the timezone is being set (via the settimeofday system call,
X		and tz.tz_dsttime == -2, use the offset in tz_minuteswest
X		to save the timezone information in the ALARM registers.
X
X
XThe idea behind the thing is that somewhere in /etc/rc, you should have
Xsomething like "date -Z" (the option -Z would have to be added to date),
Xwhich calculates the local timezone offset from /usr/share/zoneinfo, and
Xpass it along to the kernel. The kernel writes this information to the 
XCMOS ALARM registers and uses it on the next boot.
X
X
X							Christoph Robitschko
X							chmr@edvz.tu-graz.ac.at
END-of-README.localtz
echo x - clock.c
sed 's/^X//' >clock.c << 'END-of-clock.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * William Jolitz and Don Ahn.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	@(#)clock.c	7.2 (Berkeley) 5/12/91
X */
X
X/*
X * inittodr, resettodr, setrtc_tz and support routines written
X * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at>
X *
X * You can put an 'options RTC_LOCALTZ' in your config file to make
X * both DOS and 386bsd happy about the time in the CMOS clock.
X * The system then holds localtime (according to your time zone and DST)
X * in the CMOS time registers. So that 386bsd can calculate GMT from this,
X * it also holds the current offset from GMT (in hours and minutes)
X * in the ALARM registers. 
X * it is a good idea to run 'date -C' from your /etc/rc file. That updates
X * the offset information in the CMOS. (it can change if you have DST).
X *
X * WARNING: This does not work if you have any applications
X * that modify the ALARM registers in the CMOS.
X */
X
X#include "param.h"
X#include "time.h"
X#include "kernel.h"
X#include "machine/segments.h"
X#include "i386/isa/icu.h"
X#include "i386/isa/isa.h"
X#include "i386/isa/rtc.h"
X
X#define XTALSPEED	1193182
X
X#define LEAPYEAR(_YR)	(((!(_YR%4) && (_YR%100)) || !((_YR)%1000)) ? 1: 0)
X#define DAYSPERYEAR	(31+28+31+30+31+30+31+31+30+31+30+31)
Xstatic const u_char	daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
X
X
X/*
X * Support routines
X */
Xstatic int
Xbcd2int(bcd)
X{
X	return(bcd/16 * 10 + bcd%16);
X}
X
X
Xstatic int
Xint2bcd(dez)
X{
X	return(dez/10 * 16 + dez%10);
X}
X
X
Xstatic void
Xwritertc(port, val)
X{
X	outb(IO_RTC, port);
X	outb(IO_RTC+1, val);
X}
X
X
Xstatic int
Xreadrtc(port)
X{
X	return(bcd2int(rtcin(port)));
X}
X
X
X/*
X * Initialize the real time clock
X */
Xstartrtclock()
X{
X	int s;
X
X	/* initialize 8253 timer */
X	outb (IO_TIMER1+3, 0x36);
X	outb (IO_TIMER1, XTALSPEED/hz);
X	outb (IO_TIMER1, (XTALSPEED/hz)/256);
X
X	/* initialize brain-dead battery powered clock */
X	writertc(RTC_STATUSA, 0x26);
X	writertc(RTC_STATUSB, RTCSB_24HR);
X
X	if (s = rtcin(RTC_DIAG))
X		printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS);
X	writertc(RTC_DIAG, 0);
X}
X
X
X/*
X * Read the system time from the RTC.
X */
Xinittodr(base)
Xtime_t		base;
X{
X	unsigned long	sec, days;
X	int		yd;
X	int		year, month;
X	int		y, m;
X
X
X	time.tv_sec  = base;
X	time.tv_usec = 0;
X
X	/* Look if we have a RTC present and the time is valid */
X	if (rtcin(RTC_STATUSD) != RTCSD_PWR)
X		goto wrong_time;
X
X	/* wait for time update to complete */
X	/* If RTCSA_TUP is zero, we have at least 244us before next update */
X	while (rtcin(RTC_STATUSA) & RTCSA_TUP);
X
X	days = 0;
X	year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100;
X	if (year < 1970)
X		goto wrong_time;
X	month = readrtc(RTC_MONTH);
X	for (m = 1; m < month; m++)
X		days += daysinmonth[m-1];
X	if ((month > 2) && LEAPYEAR(year))
X		days ++;
X	days += readrtc(RTC_DAY) - 1;
X	yd = days;
X	for (y = 1970; y < year; y++)
X		days += DAYSPERYEAR + LEAPYEAR(y);
X	sec = ((( days * 24 +
X		  readrtc(RTC_HRS)) * 60 +
X		  readrtc(RTC_MIN)) * 60 +
X		  readrtc(RTC_SEC));
X	/* sec now contains the number of seconds, since Jan 1 1970, 
X	   in the local time zone */
X
X#ifdef RTC_LOCALTZ
X	{
X	unsigned char	reg1 = rtcin(RTCLZ_REG1);
X	unsigned char	reg2 = rtcin(RTCLZ_REG2);
X	unsigned char	reg3 = rtcin(RTCLZ_REG3);
X	if (((reg1 & RTCLZ_M1MASK) == RTCLZ_MAGIC1) &&
X	    ((reg2 & RTCLZ_M2MASK) == RTCLZ_MAGIC2) &&
X	    ((reg3 & RTCLZ_M3MASK) == RTCLZ_MAGIC3)) {
X	    	int offset = 60 * (reg3 & RTCLZ_HRSOFF);
X	    	offset += reg2 & RTCLZ_MINOFF;
X	    	if (reg3 & RTCLZ_NEG)
X	    		sec += 60 * offset;
X	    	else
X	    		sec -= 60 * offset;
X	    	}
X	else
X		printf("Warning: No timezone info in CMOS\n");
X	}
X#else
X	sec += tz.tz_minuteswest * 60;
X#endif
X
X	time.tv_sec = sec;
X	return;
X
Xwrong_time:
X	printf("invalid time in real time clock.\n");
X	printf("check and reset the date immediately !\n");
X}
X
X
X/*
X * Write system time back to RTC
X */
Xresettodr()
X{
X	unsigned long	tm;
X	int		y, m;
X
X
X	tm = time.tv_sec;
X
X	/* First, disable clock updates */
X	writertc(RTC_STATUSB, RTCSB_SET | RTCSB_24HR);
X
X	/* Calculate local time to put in CMOS */
X#ifdef RTC_LOCALTZ
X	{
X	unsigned char	reg1 = rtcin(RTCLZ_REG1);
X	unsigned char	reg2 = rtcin(RTCLZ_REG2);
X	unsigned char	reg3 = rtcin(RTCLZ_REG3);
X	if (((reg1 & RTCLZ_M1MASK) == RTCLZ_MAGIC1) &&
X	    ((reg2 & RTCLZ_M2MASK) == RTCLZ_MAGIC2) &&
X	    ((reg3 & RTCLZ_M3MASK) == RTCLZ_MAGIC3)) {
X	    	int offset = 60 * (reg3 & RTCLZ_HRSOFF);
X	    	offset += reg2 & RTCLZ_MINOFF;
X	    	if (reg3 & RTCLZ_NEG)
X	    		tm -= 60 * offset;
X	    	else
X	    		tm += 60 * offset;
X	    	}
X	}
X#else
X	tm -= tz.tz_minuteswest * 60;
X#endif
X
X	writertc(RTC_SEC, int2bcd(tm%60)); tm /= 60;	/* Write back Seconds */
X	writertc(RTC_MIN, int2bcd(tm%60)); tm /= 60;	/* Write back Minutes */
X	writertc(RTC_HRS, int2bcd(tm%24)); tm /= 24;	/* Write back Hours   */
X
X	/* We have now the days since 01-01-1970 in tm */
X	writertc(RTC_WDAY, (tm+4)%7);			/* Write back Weekday */
X	for (y=1970;; y++)
X		if ((tm - DAYSPERYEAR - LEAPYEAR(y)) > tm)
X			break;
X		else
X			tm -= DAYSPERYEAR + LEAPYEAR(y);
X
X	/* Now we have the years in y and the day-of-the-year in tm */
X	writertc(RTC_YEAR, int2bcd(y%100));		/* Write back Year    */
X	writertc(RTC_CENTURY, int2bcd(y/100));		/* ... and Century    */
X	if (LEAPYEAR(y) && (tm >= 31+29))
X		tm--;					/* Subtract Feb-29 */
X	for (m=1;; m++)
X		if (tm - daysinmonth[m-1] > tm)
X			break;
X		else
X			tm -= daysinmonth[m-1];
X
X	writertc(RTC_MONTH, int2bcd(m));		/* Write back Month   */
X	writertc(RTC_DAY, int2bcd(tm+1));		/* Write back Day     */
X
X	/* enable time updates */
X	writertc(RTC_STATUSB, RTCSB_24HR);
X}
X
X
X#ifdef RTC_LOCALTZ
X/*
X * Write timezone offset to CMOS (abuse ALARM registers)
X */
Xsetrtc_tz(offset)
Xint		offset;
X{
X	unsigned char	reg1, reg2, reg3;
X
X
X	reg1 = RTCLZ_MAGIC1 & RTCLZ_M1MASK;
X	reg2 = RTCLZ_MAGIC2 & RTCLZ_M2MASK;
X	reg3 = RTCLZ_MAGIC3 & RTCLZ_M3MASK;
X
X	if (offset < 0)
X		reg3 |= RTCLZ_NEG, offset = -offset;
X	reg2 |= (offset % 60) & RTCLZ_MINOFF;
X	reg3 |= (offset / 60) & RTCLZ_HRSOFF;
X
X	writertc(RTCLZ_REG1, reg1);
X	writertc(RTCLZ_REG2, reg2);
X	writertc(RTCLZ_REG3, reg3);
X
X	/* update the RTC registers */
X	resettodr();
X}
X#endif
X
X
X/*
X * Wire clock interrupt in.
X */
X#define V(s)	__CONCAT(V, s)
Xextern V(clk)();
Xenablertclock() {
X	INTREN(IRQ0);
X	setidt(ICU_OFFSET+0, &V(clk), SDT_SYS386IGT, SEL_KPL);
X	splnone();
X}
END-of-clock.c
echo x - cpu.h.diff
sed 's/^X//' >cpu.h.diff << 'END-of-cpu.h.diff'
X*** /sys/i386/include/cpu.h.ori	Tue Dec 24 23:23:38 1991
X--- /sys/i386/include/cpu.h	Wed Sep  9 18:47:09 1992
X***************
X*** 71,78 ****
X  #define	CLKF_BASEPRI(framep)	((framep)->if_ppl == 0)
X  #define	CLKF_PC(framep)		((framep)->if_eip)
X  
X- #define	resettodr()	/* no todr to set */
X- 
X  /*
X   * Preempt the current process if in interrupt from user mode,
X   * or after the current trap/syscall if in system mode.
X--- 71,76 ----
END-of-cpu.h.diff
echo x - kern_time.c.diff
sed 's/^X//' >kern_time.c.diff << 'END-of-kern_time.c.diff'
X*** /sys/kern/kern_time.c.ori	Fri Oct  2 23:12:20 1992
X--- /sys/kern/kern_time.c	Fri Oct  2 23:16:40 1992
X***************
X*** 100,105 ****
X--- 100,110 ----
X  	}
X  	if (uap->tzp && (error = copyin((caddr_t)uap->tzp, (caddr_t)&atz,
X  	    sizeof (atz))) == 0)
X+ #ifdef RTC_LOCALTZ
X+ 		if (atz.tz_dsttime == -2)
X+ 			setrtc_tz(atz.tz_minuteswest);
X+ 		else
X+ #endif
X  		tz = atz;
X  	return (error);
X  }
END-of-kern_time.c.diff
echo x - machdep.c.diff
sed 's/^X//' >machdep.c.diff << 'END-of-machdep.c.diff'
X*** /sys/i386/i386/machdep.c.old	Sun Aug  9 08:43:08 1992
X--- /sys/i386/i386/machdep.c	Tue Jan  5 18:26:06 1993
X***************
X*** 442,447 ****
X--- 442,449 ----
X  		printf("hit reset please");
X  		for(;;);
X  	}
X+ 
X+ 	settodr();			/* Update CMOS clock */
X  	howto = arghowto;
X  	if ((howto&RB_NOSYNC) == 0 && waittime < 0 && bfreelist[0].b_forw) {
X  		register struct buf *bp;
END-of-machdep.c.diff
echo x - rtc.h.diff
sed 's/^X//' >rtc.h.diff << 'END-of-rtc.h.diff'
X*** /sys/i386/isa/rtc.h.ori	Mon May 25 23:30:57 1992
X--- /sys/i386/isa/rtc.h	Sun Oct  4 08:56:21 1992
X***************
X*** 54,61 ****
X  #define  RTCSA_TUP	 0x80	/* time update, don't look now */
X  
X  #define RTC_STATUSB	0x0b	/* status register B */
X  
X! #define RTC_INTR	0x0c	/* status register C (R) interrupt source */
X  #define  RTCIR_UPDATE	 0x10	/* update intr */
X  #define  RTCIR_ALARM	 0x20	/* alarm intr */
X  #define  RTCIR_PERIOD	 0x40	/* periodic intr */
X--- 54,69 ----
X  #define  RTCSA_TUP	 0x80	/* time update, don't look now */
X  
X  #define RTC_STATUSB	0x0b	/* status register B */
X+ #define  RTCSB_DST	 0x01	/* Daylight Savings Time enable */
X+ #define  RTCSB_24HR	 0x02	/* 0 = 12 hours, 1 = 24 hours */
X+ #define  RTCSB_BCD	 0x04	/* 0 = BCD, 1 = Binary coded time */
X+ #define  RTCSB_SQWE	 0x08	/* 1 = output sqare wave at SQW pin */
X+ #define  RTCSB_UIE	 0x10	/* 1 = enable update-ended interrupt */
X+ #define  RTCSB_AIE	 0x20	/* 1 = enable alarm interrupt */
X+ #define  RTCSB_PIE	 0x40	/* 1 = enable periodic interrupt */
X+ #define  RTCSB_SET	 0x80	/* 1 = disable clock update */
X  
X! #define RTC_INTR	0x0c	/* status register C (R/O) interrupt source */
X  #define  RTCIR_UPDATE	 0x10	/* update intr */
X  #define  RTCIR_ALARM	 0x20	/* alarm intr */
X  #define  RTCIR_PERIOD	 0x40	/* periodic intr */
X***************
X*** 82,85 ****
X  #define RTC_EXTLO	0x17	/* low byte of extended mem size */
X  #define RTC_EXTHI	0x18	/* low byte of extended mem size */
X  
X! #define RTC_CENTURY	0x32	/* current century - please increment in Dec99*/
X--- 90,109 ----
X  #define RTC_EXTLO	0x17	/* low byte of extended mem size */
X  #define RTC_EXTHI	0x18	/* low byte of extended mem size */
X  
X! #define RTC_CENTURY	0x32	/* current century */
X! 
X! 
X! #ifdef RTC_LOCALTZ		/* see clock.c for explanation */
X! #   define RTCLZ_REG1	RTC_SECALRM	/* abused for our purposes */
X! #   define RTCLZ_REG2	RTC_MINALRM
X! #   define RTCLZ_REG3	RTC_HRSALRM
X! #   define RTCLZ_MAGIC1	'j'		/* cannot be confused with time value */
X! #   define RTCLZ_M1MASK	0xff		/* whole register is magic */
X! #   define RTCLZ_MAGIC2	0x40		/* binary 01000000 */
X! #   define RTCLZ_M2MASK	0xc0		/* mask   11000000 */
X! #   define RTCLZ_MINOFF	0x3f		/*        00111111 */
X! #   define RTCLZ_MAGIC3	0x20		/* binary 001xxxxx */
X! #   define RTCLZ_M3MASK	0xe0		/* mask   11100000 */
X! #   define RTCLZ_NEG	0x10		/*        00010000 */
X! #   define RTCLZ_HRSOFF	0x0f		/*        00001111 */
X! #endif
END-of-rtc.h.diff
exit