Charles.Forsyth20060303644188019 years agoHistory
369 lines · 5.5 KB · C
RawDownload
/* *	DS1339 Timekeeper (on I2C) */ #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 Rtc	Rtc;typedef struct Rtcreg	Rtcreg; struct Rtc{	int	sec;	int	min;	int	hour;	int	wday;	int	mday;	int	mon;	int	year;}; struct Rtcreg{	uchar	sec;	uchar	min;	uchar	hour;	uchar	wday;	/* 1=Sun */	uchar	mday;	/* 00-31 */	uchar	mon;	/* 1-12 */	uchar	year;}; enum{	Qdir = 0,	Qrtc, 	Rtclen=	7,		/* bytes read and written to timekeeper */}; static QLock	rtclock;		/* mutex on nvram operations */static I2Cdev	rtdev; static Dirtab rtcdir[]={	".",		{Qdir, 0, QTDIR},	0,	DMDIR|0555,	"rtc",		{Qrtc, 0},	0,	0664,}; static ulong	rtc2sec(Rtc*);static void	sec2rtc(ulong, Rtc*);static void	setrtc(Rtc*); static voidrtcreset(void){	rtdev.addr = 0x68;	rtdev.salen = 1;	i2csetup(1);} static Chan*rtcattach(char *spec){	return devattach('r', spec);} static Walkqid*rtcwalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);} static int	 rtcstat(Chan *c, uchar *dp, int n){	return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);} static Chan*rtcopen(Chan *c, int omode){	omode = openmode(omode);	switch((ulong)c->qid.path){	case Qrtc:		if(strcmp(up->env->user, eve)!=0 && omode!=OREAD)			error(Eperm);		break;	}	return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);} static void	 rtcclose(Chan*){} static long	 rtcread(Chan *c, void *buf, long n, vlong offset){	ulong t, ot; 	if(c->qid.type & QTDIR)		return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen); 	switch((ulong)c->qid.path){	case Qrtc:		qlock(&rtclock);		t = rtctime();		do{			ot = t;			t = rtctime();	/* make sure there's no skew */		}while(t != ot);		qunlock(&rtclock);		return readnum(offset, buf, n, t, 12);	}	error(Egreg);	return -1;		/* never reached */} static long	 rtcwrite(Chan *c, void *buf, long n, vlong off){	Rtc rtc;	ulong secs;	char *cp, sbuf[32];	ulong offset = off; 	switch((ulong)c->qid.path){	case Qrtc:		if(offset!=0 || n >= sizeof(sbuf)-1)			error(Ebadarg);		memmove(sbuf, buf, n);		sbuf[n] = '\0';		/*		 *  read the time		 */		cp = sbuf;		while(*cp){			if(*cp>='0' && *cp<='9')				break;			cp++;		}		secs = strtoul(cp, 0, 0);		/*		 *  convert to bcd		 */		sec2rtc(secs, &rtc);		/*		 * write it		 */		setrtc(&rtc);		return n;	}	error(Egreg);	return -1;		/* never reached */} Dev rtcdevtab = {	'r',	"rtc", 	rtcreset,	devinit,	devshutdown,	rtcattach,	rtcwalk,	rtcstat,	rtcopen,	devcreate,	rtcclose,	rtcread,	devbread,	rtcwrite,	devbwrite,	devremove,	devwstat,}; static intgetbcd(int bcd){	return (bcd&0x0f) + 10 * (bcd>>4);} static intputbcd(int val){	return (val % 10) | (((val/10) % 10) << 4);} long	 rtctime(void){	Rtc rtc;	Rtcreg d;	int h; 	if(waserror()){		iprint("rtc: err %s\n", up->env->errstr);		return 0;	}	if(i2crecv(&rtdev, &d, Rtclen, 0) != Rtclen)		return 0;	poperror();	rtc.sec = getbcd(d.sec);	rtc.min = getbcd(d.min);	if(d.hour & (1<<6)){	/* 12 hour clock */		h = d.hour & 0x1F;		if(d.hour & (1<<5))			h += 0x12;		rtc.hour = getbcd(h);	}else		rtc.hour = getbcd(d.hour);	rtc.mday = getbcd(d.mday);	rtc.mon = getbcd(d.mon & 0x7f);	rtc.year = getbcd(d.year);	if(rtc.mon < 1 || rtc.mon > 12)		return 0;	if(d.mon & (1<<7))		rtc.year += 2000;	else		rtc.year += 1900;	return rtc2sec(&rtc);} static voidsetrtc(Rtc *rtc){	Rtcreg d; 	memset(&d, 0, sizeof(d));	d.year = putbcd(rtc->year % 100);	d.mon = putbcd(rtc->mon);	if(rtc->year >= 2000)		d.mon |= 1<<7;	d.wday = rtc->wday+1;	d.mday = putbcd(rtc->mday);	d.hour = putbcd(rtc->hour);	d.min = putbcd(rtc->min);	d.sec = putbcd(rtc->sec);	i2csend(&rtdev, &d, Rtclen, 0);} #define SEC2MIN 60L#define SEC2HOUR (60L*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 y){ 	if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))		return ldmsize;	else		return dmsize;} /* *  compute seconds since Jan 1 1970 */static ulongrtc2sec(Rtc *rtc){	ulong secs;	int i;	int *d2m; 	secs = 0; 	/*	 *  seconds per year	 */	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;	} 	/*	 * day is the day number.	 * generate day of the week.	 * The addend is 4 mod 7 (1/1/1970 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;}