1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8
9 /*
10 * real time clock
11 */
12
13 enum {
14 Paddr= 0x70, /* address port */
15 Pdata= 0x71, /* data port */
16
17 Seconds= 0x00,
18 Minutes= 0x02,
19 Hours= 0x04,
20 Mday= 0x07,
21 Month= 0x08,
22 Year= 0x09,
23 Status= 0x0A,
24 };
25
26 typedef struct Rtc Rtc;
27 struct Rtc
28 {
29 int sec;
30 int min;
31 int hour;
32 int mday;
33 int mon;
34 int year;
35 };
36
37 enum{
38 Qdir = 0,
39 Qrtc,
40 };
41
42 Dirtab rtcdir[]={
43 ".", {Qdir, 0, QTDIR}, 0, 0555,
44 "rtc", {Qrtc, 0}, 0, 0664,
45 };
46
47 static Lock rtclock;
48
49 static ulong rtc2sec(Rtc*);
50 static void sec2rtc(ulong, Rtc*);
51
52 static Chan*
rtcattach(char * spec)53 rtcattach(char* spec)
54 {
55 return devattach('r', spec);
56 }
57
58 static Walkqid*
rtcwalk(Chan * c,Chan * nc,char ** name,int nname)59 rtcwalk(Chan* c, Chan *nc, char** name, int nname)
60 {
61 return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
62 }
63
64 static int
rtcstat(Chan * c,uchar * dp,int n)65 rtcstat(Chan* c, uchar* dp, int n)
66 {
67 return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
68 }
69
70 static Chan*
rtcopen(Chan * c,int omode)71 rtcopen(Chan* c, int omode)
72 {
73 omode = openmode(omode);
74 switch((ulong)c->qid.path){
75 case Qrtc:
76 if(strcmp(up->user, eve)!=0 && omode!=OREAD)
77 error(Eperm);
78 break;
79 }
80 return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
81 }
82
83 static void
rtcclose(Chan *)84 rtcclose(Chan*)
85 {
86 }
87
88 static long
_rtctime(void)89 _rtctime(void)
90 {
91 Rtc rtc;
92 int i;
93
94 /* don't do the read until the clock is no longer busy */
95 for(i = 0; i < 10000; i++){
96 outb(Paddr, Status);
97 if(inb(Pdata) & 0x80)
98 continue;
99
100 /* read clock values */
101 outb(Paddr, Seconds); rtc.sec = inb(Pdata);
102 outb(Paddr, Minutes); rtc.min = inb(Pdata);
103 outb(Paddr, Hours); rtc.hour = inb(Pdata);
104 outb(Paddr, Mday); rtc.mday = inb(Pdata);
105 outb(Paddr, Month); rtc.mon = inb(Pdata);
106 outb(Paddr, Year); rtc.year = inb(Pdata);
107
108 outb(Paddr, Status);
109 if((inb(Pdata) & 0x80) == 0)
110 break;
111 }
112
113 /*
114 * the world starts jan 1 1970
115 */
116 if(rtc.year < 70)
117 rtc.year += 2000;
118 else
119 rtc.year += 1900;
120 return rtc2sec(&rtc);
121 }
122
123 long
rtctime(void)124 rtctime(void)
125 {
126 int i;
127 long t, ot;
128
129 ilock(&rtclock);
130
131 /* loop till we get two reads in a row the same */
132 t = _rtctime();
133 for(i = 0; i < 100; i++){
134 ot = t;
135 t = _rtctime();
136 if(ot == t)
137 break;
138 }
139 if(i == 100) print("we are boofheads\n");
140
141 iunlock(&rtclock);
142
143 return t;
144 }
145
146 static long
rtcread(Chan * c,void * buf,long n,vlong off)147 rtcread(Chan* c, void* buf, long n, vlong off)
148 {
149 ulong t;
150 ulong offset = off;
151
152 if(c->qid.type & QTDIR)
153 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
154
155 switch((ulong)c->qid.path){
156 case Qrtc:
157 t = rtctime();
158 n = readnum(offset, buf, n, t, 12);
159 return n;
160 }
161 error(Ebadarg);
162 return 0;
163 }
164
165 static long
rtcwrite(Chan * c,void * buf,long n,vlong off)166 rtcwrite(Chan* c, void* buf, long n, vlong off)
167 {
168 Rtc rtc;
169 ulong secs;
170 char *cp, *ep;
171 ulong offset = off;
172
173 if(offset!=0)
174 error(Ebadarg);
175
176 switch((ulong)c->qid.path){
177 case Qrtc:
178 /*
179 * read the time
180 */
181 cp = ep = buf;
182 ep += n;
183 while(cp < ep){
184 if(*cp>='0' && *cp<='9')
185 break;
186 cp++;
187 }
188 secs = strtoul(cp, 0, 0);
189
190 /*
191 * convert to rtc
192 */
193 sec2rtc(secs, &rtc);
194
195 /*
196 * write the clock
197 */
198 ilock(&rtclock);
199 outb(Paddr, Seconds); outb(Pdata, rtc.sec);
200 outb(Paddr, Minutes); outb(Pdata, rtc.min);
201 outb(Paddr, Hours); outb(Pdata, rtc.hour);
202 outb(Paddr, Mday); outb(Pdata, rtc.mday);
203 outb(Paddr, Month); outb(Pdata, rtc.mon);
204 outb(Paddr, Year); outb(Pdata, rtc.year % 100);
205 iunlock(&rtclock);
206 return n;
207 }
208 error(Ebadarg);
209 return 0;
210 }
211
212 Dev rtcdevtab = {
213 'r',
214 "rtc",
215
216 devreset,
217 devinit,
218 devshutdown,
219 rtcattach,
220 rtcwalk,
221 rtcstat,
222 rtcopen,
223 devcreate,
224 rtcclose,
225 rtcread,
226 devbread,
227 rtcwrite,
228 devbwrite,
229 devremove,
230 devwstat,
231 };
232
233 #define SEC2MIN 60L
234 #define SEC2HOUR (60L*SEC2MIN)
235 #define SEC2DAY (24L*SEC2HOUR)
236
237 /*
238 * days per month plus days/year
239 */
240 static int dmsize[] =
241 {
242 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
243 };
244 static int ldmsize[] =
245 {
246 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
247 };
248
249 /*
250 * return the days/month for the given year
251 */
252 static int*
yrsize(int y)253 yrsize(int y)
254 {
255 if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
256 return ldmsize;
257 else
258 return dmsize;
259 }
260
261 /*
262 * compute seconds since Jan 1 1970
263 */
264 static ulong
rtc2sec(Rtc * rtc)265 rtc2sec(Rtc *rtc)
266 {
267 ulong secs;
268 int i;
269 int *d2m;
270
271 secs = 0;
272
273 /*
274 * seconds per year
275 */
276 for(i = 1970; i < rtc->year; i++){
277 d2m = yrsize(i);
278 secs += d2m[0] * SEC2DAY;
279 }
280
281 /*
282 * seconds per month
283 */
284 d2m = yrsize(rtc->year);
285 for(i = 1; i < rtc->mon; i++)
286 secs += d2m[i] * SEC2DAY;
287
288 secs += (rtc->mday-1) * SEC2DAY;
289 secs += rtc->hour * SEC2HOUR;
290 secs += rtc->min * SEC2MIN;
291 secs += rtc->sec;
292
293 return secs;
294 }
295
296 /*
297 * compute rtc from seconds since Jan 1 1970
298 */
299 static void
sec2rtc(ulong secs,Rtc * rtc)300 sec2rtc(ulong secs, Rtc *rtc)
301 {
302 int d;
303 long hms, day;
304 int *d2m;
305
306 /*
307 * break initial number into days
308 */
309 hms = secs % SEC2DAY;
310 day = secs / SEC2DAY;
311 if(hms < 0) {
312 hms += SEC2DAY;
313 day -= 1;
314 }
315
316 /*
317 * generate hours:minutes:seconds
318 */
319 rtc->sec = hms % 60;
320 d = hms / 60;
321 rtc->min = d % 60;
322 d /= 60;
323 rtc->hour = d;
324
325 /*
326 * year number
327 */
328 if(day >= 0)
329 for(d = 1970; day >= *yrsize(d); d++)
330 day -= *yrsize(d);
331 else
332 for (d = 1970; day < 0; d--)
333 day += *yrsize(d-1);
334 rtc->year = d;
335
336 /*
337 * generate month
338 */
339 d2m = yrsize(rtc->year);
340 for(d = 1; day >= d2m[d]; d++)
341 day -= d2m[d];
342 rtc->mday = day + 1;
343 rtc->mon = d;
344 }
345