368 lines · 5.9 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * devrtc - real-time clock, for kirkwood */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "io.h" typedef	struct	RtcReg	RtcReg;typedef	struct	Rtc	Rtc; struct RtcReg{	ulong	time;	ulong	date;	ulong	alarmtm;	ulong	alarmdt;	ulong	intrmask;	ulong	intrcause;}; struct Rtc{	int	sec;	int	min;	int	hour;	int	wday;	int	mday;	int	mon;	int	year;}; enum {	Qdir,	Qrtc,}; static Dirtab rtcdir[] = {	".",	{Qdir, 0, QTDIR},	0,		0555,	"rtc",	{Qrtc},			NUMSIZE,	0664,};static	RtcReg	*rtcreg;		/* filled in by attach */static	Lock	rtclock; #define SEC2MIN	60#define SEC2HOUR (60*SEC2MIN)#define SEC2DAY (24L*SEC2HOUR) /* * days per month plus days/year */static	int	dmsize[] ={	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};static	int	ldmsize[] ={	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* *  return the days/month for the given year */static int *yrsize(int yr){	if((yr % 4) == 0)		return ldmsize;	else		return dmsize;} /* *  compute seconds since Jan 1 1970 */static ulongrtc2sec(Rtc *rtc){	ulong secs;	int i;	int *d2m; 	/*	 *  seconds per year	 */	secs = 0;	for(i = 1970; i < rtc->year; i++){		d2m = yrsize(i);		secs += d2m[0] * SEC2DAY;	} 	/*	 *  seconds per month	 */	d2m = yrsize(rtc->year);	for(i = 1; i < rtc->mon; i++)		secs += d2m[i] * SEC2DAY; 	secs += (rtc->mday-1) * SEC2DAY;	secs += rtc->hour * SEC2HOUR;	secs += rtc->min * SEC2MIN;	secs += rtc->sec; 	return secs;} /* *  compute rtc from seconds since Jan 1 1970 */static voidsec2rtc(ulong secs, Rtc *rtc){	int d;	long hms, day;	int *d2m; 	/*	 * break initial number into days	 */	hms = secs % SEC2DAY;	day = secs / SEC2DAY;	if(hms < 0) {		hms += SEC2DAY;		day -= 1;	} 	/*	 * 19700101 was thursday	 */	rtc->wday = (day + 7340036L) % 7; 	/*	 * generate hours:minutes:seconds	 */	rtc->sec = hms % 60;	d = hms / 60;	rtc->min = d % 60;	d /= 60;	rtc->hour = d; 	/*	 * year number	 */	if(day >= 0)		for(d = 1970; day >= *yrsize(d); d++)			day -= *yrsize(d);	else		for (d = 1970; day < 0; d--)			day += *yrsize(d-1);	rtc->year = d; 	/*	 * generate month	 */	d2m = yrsize(rtc->year);	for(d = 1; day >= d2m[d]; d++)		day -= d2m[d];	rtc->mday = day + 1;	rtc->mon = d;} enum {	Rtcsec	= 0x00007f,	Rtcmin	= 0x007f00,	Rtcms	= 8,	Rtchr12	= 0x1f0000,	Rtchr24	= 0x3f0000,	Rtchrs	= 16, 	Rdmday	= 0x00003f,	Rdmon	= 0x001f00,	Rdms	= 8,	Rdyear	= 0x7f0000,	Rdys	= 16, 	Rtcpm	= 1<<21,		/* pm bit */	Rtc12	= 1<<22,		/* 12 hr clock */}; static ulongbcd2dec(ulong bcd){	ulong d, m, i; 	d = 0;	m = 1;	for(i = 0; i < 2 * sizeof d; i++){		d += ((bcd >> (4*i)) & 0xf) * m;		m *= 10;	}	return d;} static ulongdec2bcd(ulong d){	ulong bcd, i; 	bcd = 0;	for(i = 0; d != 0; i++){		bcd |= (d%10) << (4*i);		d /= 10;	}	return bcd;} static long_rtctime(void){	ulong t, d;	Rtc rtc; 	t = rtcreg->time;	d = rtcreg->date; 	rtc.sec = bcd2dec(t & Rtcsec);	rtc.min = bcd2dec((t & Rtcmin) >> Rtcms); 	if(t & Rtc12){		rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */		if(t & Rtcpm)			rtc.hour += 12;	}else		rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs);	/* 0—23 */ 	rtc.mday = bcd2dec(d & Rdmday);				/* 1—31 */	rtc.mon = bcd2dec((d & Rdmon) >> Rdms);			/* 1—12 */	rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000;	/* year%100 */ //	print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD *///		rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);	return rtc2sec(&rtc);} longrtctime(void){	int i;	long t, ot; 	ilock(&rtclock); 	/* loop until we get two reads in a row the same */	t = _rtctime();	ot = ~t;	for(i = 0; i < 100 && ot != t; i++){		ot = t;		t = _rtctime();	}	if(ot != t)		print("rtctime: we are boofheads\n"); 	iunlock(&rtclock);	return t;} static voidsetrtc(Rtc *rtc){	ilock(&rtclock);	rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |		dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);	rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |		dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);	iunlock(&rtclock);} static Chan*rtcattach(char *spec){	rtcreg = (RtcReg*)soc.rtc;	return devattach(L'r', spec);} static Walkqid*rtcwalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);} static intrtcstat(Chan *c, uchar *dp, int n){	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);} static Chan*rtcopen(Chan *c, int omode){	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);} static voidrtcclose(Chan*){} static longrtcread(Chan *c, void *buf, long n, vlong off){	if(c->qid.type & QTDIR)		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); 	switch((ulong)c->qid.path){	default:		error(Egreg);	case Qrtc:		return readnum(off, buf, n, rtctime(), NUMSIZE);	}} static longrtcwrite(Chan *c, void *buf, long n, vlong off){	ulong offset = off;	char *cp, sbuf[32];	Rtc rtc; 	switch((ulong)c->qid.path){	default:		error(Egreg);	case Qrtc:		if(offset != 0 || n >= sizeof(sbuf)-1)			error(Ebadarg);		memmove(sbuf, buf, n);		sbuf[n] = '\0';		for(cp = sbuf; *cp != '\0'; cp++)			if(*cp >= '0' && *cp <= '9')				break;		sec2rtc(strtoul(cp, 0, 0), &rtc);		setrtc(&rtc);		return n;	}} Dev rtcdevtab = {	L'r',	"rtc", 	devreset,	devinit,	devshutdown,	rtcattach,	rtcwalk,	rtcstat,	rtcopen,	devcreate,	rtcclose,	rtcread,	devbread,	rtcwrite,	devbwrite,	devremove,	devwstat,	devpower,};