ref: 47d4282ebda5607a5bb7ad02310bf13295c96626
dir: /rtc7940fs.c/
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#define CTL "/dev/i2c1/i2c.6f.ctl"
#define DATA "/dev/i2c1/i2c.6f.data"
enum {
/* registers */
Seconds= 0,
Minutes= 1,
Hours= 2,
Weekday= 3,
Mday= 4,
Month= 5,
Year= 6,
Nbcd= 7,
/* Hours register may be in 12-hour or 24-hour mode */
Twelvehr= 0x40,
Pm= 0x20,
Start= 0x80,
Vbaten= 0x08,
};
typedef struct Rtc Rtc;
struct Rtc
{
int sec;
int min;
int hour;
int mday;
int mon;
int year;
};
#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 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
*/
static 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;
}
void
ctl(char *s)
{
int fd = open(CTL, OWRITE);
if (fd < 0)
sysfatal("open: %r");
write(fd, s, strlen(s));
close(fd);
}
static int
bcd(int n)
{
return (n & 0xF) + (10 * (n >> 4));
}
void
fsread(Req *r)
{
int fd;
uchar clk[Nbcd];
Rtc rtc;
char buf[256];
ulong t;
clk[0] = 0;
if ((intptr)(r->fid->file->aux) == 173) {
fd = open(DATA, ORDWR);
if (fd < 0)
sysfatal("open: %r");
write(fd, clk, 1);
read(fd, clk, Nbcd);
close(fd);
rtc.sec = bcd(clk[Seconds] & 0x7F);
rtc.min = bcd(clk[Minutes] & 0x7F);
rtc.hour = bcd(clk[Hours] & 0x3F);
if(clk[Hours] & Twelvehr){
rtc.hour = bcd(clk[Hours] & 0x1F);
if(clk[Hours] & Pm)
rtc.hour += 12;
}
rtc.mday = bcd(clk[Mday] & 0x3F);
rtc.mon = bcd(clk[Month] & 0x1F);
rtc.year = bcd(clk[Year]);
if(rtc.year < 70)
rtc.year += 2000;
else
rtc.year += 1900;
t = rtc2sec(&rtc);
snprint(buf, 256, "%12lud", t);
readstr(r, buf);
respond(r, nil);
} else {
respond(r, "file not found");
}
}
#define PUTBCD(n,o) bcdclock[1+o] = (n % 10) | (((n / 10) % 10)<<4)
void
fswrite(Req *r)
{
Rtc rtc;
ulong secs;
uchar bcdclock[1+Nbcd];
char *p, *ep;
int fd;
if ((intptr)(r->fid->file->aux) == 173) {
p = r->ifcall.data;
ep = p + r->ifcall.count;
while(p < ep)
if (*p >= '0' && *p <= '9')
break;
else
p++;
secs = strtoul(p, 0, 0);
sec2rtc(secs, &rtc);
PUTBCD(rtc.sec, Seconds);
PUTBCD(rtc.min, Minutes); /* forces 24 hour mode */
PUTBCD(rtc.hour, Hours);
PUTBCD(0, Weekday); /* hope no other OS uses this */
PUTBCD(rtc.mday, Mday);
PUTBCD(rtc.mon, Month);
PUTBCD(rtc.year, Year);
bcdclock[0] = 0;
bcdclock[1+Seconds] |= Start;
bcdclock[1+Weekday] |= Vbaten;
fd = open(DATA, OWRITE);
if (fd < 0)
sysfatal("open: %r");
write(fd, bcdclock, 1+Nbcd);
close(fd);
respond(r, nil);
} else {
respond(r, "file not found");
}
}
Srv fs = {
.read = fsread,
.write = fswrite,
};
void
main()
{
ctl("size 10");
ctl("subaddress 0");
fs.tree = alloctree("rtcfs", "rtcfs", DMDIR|0555, nil);
createfile(fs.tree->root, "rtc", "rtcfs", 0666, (void*)173);
postmountsrv(&fs, nil, "/dev", MBEFORE);
}