plan 9 kernel history: overview | file list | diff list

1995/0108/pc/devrtc.c (diff list | history)

pc/devrtc.c on 1991/0911
1991/0911    
#include	"u.h" 
1992/0321    
#include	"../port/lib.h" 
1991/0911    
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
1992/0111    
#include	"../port/error.h" 
1991/0911    
#include	"devtab.h" 
 
1991/1211    
/* 
 *  real time clock and non-volatile ram 
 */ 
 
1991/0911    
enum { 
	Paddr=		0x70,	/* address port */ 
	Pdata=		0x71,	/* data port */ 
 
	Seconds=	0x00, 
	Minutes=	0x02, 
	Hours=		0x04,  
	Mday=		0x07, 
	Month=		0x08, 
	Year=		0x09, 
	Status=		0x0A, 
 
1993/0319    
	Nvoff=		128,	/* where usable nvram lives */ 
1993/0915    
	Nvsize=		256, 
1993/0319    
 
1991/0911    
	Nbcd=		6, 
}; 
 
typedef struct Rtc	Rtc; 
struct Rtc 
{ 
	int	sec; 
	int	min; 
	int	hour; 
	int	mday; 
	int	mon; 
	int	year; 
}; 
 
QLock rtclock;	/* mutex on clock operations */ 
 
1992/0820    
enum{ 
	Qrtc = 1, 
	Qnvram, 
}; 
 
1993/0319    
#define	NRTC	2 
1991/0911    
Dirtab rtcdir[]={ 
1993/0915    
	"nvram",	{Qnvram, 0},	Nvsize,	0664, 
	"rtc",		{Qrtc, 0},	0,	0664, 
1991/0911    
}; 
 
ulong rtc2sec(Rtc*); 
void sec2rtc(ulong, Rtc*); 
int *yrsize(int); 
 
void 
rtcreset(void) 
{ 
} 
 
void 
rtcinit(void) 
{ 
} 
 
Chan* 
rtcattach(char *spec) 
{ 
	return devattach('r', spec); 
} 
 
Chan* 
rtcclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int	  
rtcwalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, rtcdir, NRTC, devgen); 
} 
 
void	  
rtcstat(Chan *c, char *dp) 
{ 
	devstat(c, dp, rtcdir, NRTC, devgen); 
} 
 
Chan* 
rtcopen(Chan *c, int omode) 
{ 
1992/0819    
	omode = openmode(omode); 
	switch(c->qid.path){ 
	case Qrtc: 
1993/0915    
		if(strcmp(up->user, eve)!=0 && omode!=OREAD) 
1992/0826    
			error(Eperm); 
		break; 
1992/0819    
	case Qnvram: 
1993/0915    
		if(strcmp(up->user, eve)!=0) 
1992/0819    
			error(Eperm); 
	} 
1991/0911    
	return devopen(c, omode, rtcdir, NRTC, devgen); 
} 
 
void	  
rtccreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1992/0711    
	USED(c, name, omode, perm); 
1991/0911    
	error(Eperm); 
} 
 
void	  
rtcclose(Chan *c) 
{ 
1992/0711    
	USED(c); 
1991/0911    
} 
 
#define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4)) 
 
long	  
rtctime(void) 
{ 
	uchar bcdclock[Nbcd]; 
	Rtc rtc; 
1992/1208    
	int i; 
1991/0911    
 
1992/1208    
	for(i = 0; i < 10000; i++){ 
		outb(Paddr, Status); 
		if((inb(Pdata) & 1) == 0) 
			break; 
	} 
1991/0911    
	outb(Paddr, Seconds);	bcdclock[0] = inb(Pdata); 
	outb(Paddr, Minutes);	bcdclock[1] = inb(Pdata); 
	outb(Paddr, Hours);	bcdclock[2] = inb(Pdata); 
	outb(Paddr, Mday);	bcdclock[3] = inb(Pdata); 
	outb(Paddr, Month);	bcdclock[4] = inb(Pdata); 
	outb(Paddr, Year);	bcdclock[5] = inb(Pdata); 
 
	/* 
	 *  convert from BCD 
	 */ 
	rtc.sec = GETBCD(0); 
	rtc.min = GETBCD(1); 
	rtc.hour = GETBCD(2); 
	rtc.mday = GETBCD(3); 
	rtc.mon = GETBCD(4); 
	rtc.year = GETBCD(5); 
 
	/* 
	 *  the world starts jan 1 1970 
	 */ 
	if(rtc.year < 70) 
		rtc.year += 2000; 
	else 
		rtc.year += 1900; 
	return rtc2sec(&rtc); 
} 
 
long	  
rtcread(Chan *c, void *buf, long n, ulong offset) 
{ 
	ulong t, ot; 
1993/0319    
	char *a; 
1991/0911    
 
	if(c->qid.path & CHDIR) 
		return devdirread(c, buf, n, rtcdir, NRTC, devgen); 
 
1993/0319    
	switch(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); 
		n = readnum(offset, buf, n, t, 12); 
		return n; 
	case Qnvram: 
		a = buf; 
		if(waserror()){ 
			qunlock(&rtclock); 
			nexterror(); 
		} 
		qlock(&rtclock); 
		for(t = offset; t < offset + n; t++){ 
			if(t >= Nvsize) 
				break; 
			outb(Paddr, Nvoff+t); 
			*a++ = inb(Pdata); 
		} 
		qunlock(&rtclock); 
		poperror(); 
		return t - offset; 
	} 
	error(Ebadarg); 
	return 0; 
1991/0911    
} 
 
1995/0108    
Block* 
rtcbread(Chan *c, long n, ulong offset) 
{ 
	return devbread(c, n, offset); 
} 
 
1991/0911    
#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4) 
 
long	  
rtcwrite(Chan *c, void *buf, long n, ulong offset) 
{ 
1993/0319    
	int t; 
	char *a; 
1991/0911    
	Rtc rtc; 
	ulong secs; 
	uchar bcdclock[Nbcd]; 
	char *cp, *ep; 
 
1992/0711    
	USED(c); 
1991/0911    
	if(offset!=0) 
		error(Ebadarg); 
 
 
1993/0319    
	switch(c->qid.path){ 
	case Qrtc: 
		/* 
		 *  read the time 
		 */ 
		cp = ep = buf; 
		ep += n; 
		while(cp < ep){ 
			if(*cp>='0' && *cp<='9') 
				break; 
			cp++; 
		} 
		secs = strtoul(cp, 0, 0); 
	 
		/* 
		 *  convert to bcd 
		 */ 
		sec2rtc(secs, &rtc); 
		PUTBCD(rtc.sec, 0); 
		PUTBCD(rtc.min, 1); 
		PUTBCD(rtc.hour, 2); 
		PUTBCD(rtc.mday, 3); 
		PUTBCD(rtc.mon, 4); 
		PUTBCD(rtc.year, 5); 
1991/0911    
 
1993/0319    
		/* 
		 *  write the clock 
		 */ 
		qlock(&rtclock); 
		outb(Paddr, Seconds);	outb(Pdata, bcdclock[0]); 
		outb(Paddr, Minutes);	outb(Pdata, bcdclock[1]); 
		outb(Paddr, Hours);	outb(Pdata, bcdclock[2]); 
		outb(Paddr, Mday);	outb(Pdata, bcdclock[3]); 
		outb(Paddr, Month);	outb(Pdata, bcdclock[4]); 
		outb(Paddr, Year);	outb(Pdata, bcdclock[5]); 
		qunlock(&rtclock); 
		return n; 
	case Qnvram: 
		a = buf; 
		if(waserror()){ 
			qunlock(&rtclock); 
			nexterror(); 
		} 
		qlock(&rtclock); 
		for(t = offset; t < offset + n; t++){ 
			if(t >= Nvsize) 
				break; 
			outb(Paddr, Nvoff+t); 
			outb(Pdata, *a++); 
		} 
		qunlock(&rtclock); 
		poperror(); 
		return t - offset; 
	} 
	error(Ebadarg); 
	return 0; 
1995/0108    
} 
 
long 
rtcbwrite(Chan *c, Block *bp, ulong offset) 
{ 
	return devbwrite(c, bp, offset); 
1991/0911    
} 
 
void	  
rtcremove(Chan *c) 
{ 
1992/0711    
	USED(c); 
1991/0911    
	error(Eperm); 
} 
 
void	  
rtcwstat(Chan *c, char *dp) 
{ 
1992/0711    
	USED(c, dp); 
1991/0911    
	error(Eperm); 
} 
 
#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 
 */ 
int * 
yrsize(int yr) 
{ 
	if((yr % 4) == 0) 
		return ldmsize; 
	else 
		return dmsize; 
} 
 
/* 
 *  compute seconds since Jan 1 1970 
 */ 
ulong 
rtc2sec(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 
 */ 
void 
sec2rtc(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; 
	} 
 
	/* 
	 * 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; 
 
	return; 
} 
1992/1006    
 
uchar 
nvramread(int offset) 
{ 
	outb(Paddr, offset); 
	return inb(Pdata); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)