FreeBSD/Linux Kernel Cross Reference
sys/pc/devrtc.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 /*
9 * real time clock and non-volatile ram
10 */
11
12 enum {
13 Paddr= 0x70, /* address port */
14 Pdata= 0x71, /* data port */
15
16 Seconds= 0x00,
17 Minutes= 0x02,
18 Hours= 0x04,
19 Mday= 0x07,
20 Month= 0x08,
21 Year= 0x09,
22 Status= 0x0A,
23
24 Nvoff= 128, /* where usable nvram lives */
25 Nvsize= 256,
26
27 Nbcd= 6,
28 };
29
30 typedef struct Rtc Rtc;
31 struct Rtc
32 {
33 int sec;
34 int min;
35 int hour;
36 int mday;
37 int mon;
38 int year;
39 };
40
41
42 enum{
43 Qdir = 0,
44 Qrtc,
45 Qnvram,
46 };
47
48 Dirtab rtcdir[]={
49 ".", {Qdir, 0, QTDIR}, 0, 0555,
50 "nvram", {Qnvram, 0}, Nvsize, 0664,
51 "rtc", {Qrtc, 0}, 0, 0664,
52 };
53
54 static ulong rtc2sec(Rtc*);
55 static void sec2rtc(ulong, Rtc*);
56
57 void
58 rtcinit(void)
59 {
60 if(ioalloc(Paddr, 2, 0, "rtc/nvr") < 0)
61 panic("rtcinit: ioalloc failed");
62 }
63
64 static Chan*
65 rtcattach(char* spec)
66 {
67 return devattach('r', spec);
68 }
69
70 static Walkqid*
71 rtcwalk(Chan* c, Chan *nc, char** name, int nname)
72 {
73 return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
74 }
75
76 static int
77 rtcstat(Chan* c, uchar* dp, int n)
78 {
79 return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
80 }
81
82 static Chan*
83 rtcopen(Chan* c, int omode)
84 {
85 omode = openmode(omode);
86 switch((ulong)c->qid.path){
87 case Qrtc:
88 if(strcmp(up->user, eve)!=0 && omode!=OREAD)
89 error(Eperm);
90 break;
91 case Qnvram:
92 if(strcmp(up->user, eve)!=0)
93 error(Eperm);
94 }
95 return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
96 }
97
98 static void
99 rtcclose(Chan*)
100 {
101 }
102
103 #define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4))
104
105 static long
106 _rtctime(void)
107 {
108 uchar bcdclock[Nbcd];
109 Rtc rtc;
110 int i;
111
112 /* don't do the read until the clock is no longer busy */
113 for(i = 0; i < 10000; i++){
114 outb(Paddr, Status);
115 if(inb(Pdata) & 0x80)
116 continue;
117
118 /* read clock values */
119 outb(Paddr, Seconds); bcdclock[0] = inb(Pdata);
120 outb(Paddr, Minutes); bcdclock[1] = inb(Pdata);
121 outb(Paddr, Hours); bcdclock[2] = inb(Pdata);
122 outb(Paddr, Mday); bcdclock[3] = inb(Pdata);
123 outb(Paddr, Month); bcdclock[4] = inb(Pdata);
124 outb(Paddr, Year); bcdclock[5] = inb(Pdata);
125
126 outb(Paddr, Status);
127 if((inb(Pdata) & 0x80) == 0)
128 break;
129 }
130
131 /*
132 * convert from BCD
133 */
134 rtc.sec = GETBCD(0);
135 rtc.min = GETBCD(1);
136 rtc.hour = GETBCD(2);
137 rtc.mday = GETBCD(3);
138 rtc.mon = GETBCD(4);
139 rtc.year = GETBCD(5);
140
141 /*
142 * the world starts jan 1 1970
143 */
144 if(rtc.year < 70)
145 rtc.year += 2000;
146 else
147 rtc.year += 1900;
148 return rtc2sec(&rtc);
149 }
150
151 static Lock nvrtlock;
152
153 long
154 rtctime(void)
155 {
156 int i;
157 long t, ot;
158
159 ilock(&nvrtlock);
160
161 /* loop till we get two reads in a row the same */
162 t = _rtctime();
163 for(i = 0; i < 100; i++){
164 ot = t;
165 t = _rtctime();
166 if(ot == t)
167 break;
168 }
169 if(i == 100) print("we are boofheads\n");
170
171 iunlock(&nvrtlock);
172
173 return t;
174 }
175
176 static long
177 rtcread(Chan* c, void* buf, long n, vlong off)
178 {
179 ulong t;
180 char *a, *start;
181 ulong offset = off;
182
183 if(c->qid.type & QTDIR)
184 return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
185
186 switch((ulong)c->qid.path){
187 case Qrtc:
188 t = rtctime();
189 n = readnum(offset, buf, n, t, 12);
190 return n;
191 case Qnvram:
192 if(n == 0)
193 return 0;
194 if(n > Nvsize)
195 n = Nvsize;
196 a = start = smalloc(n);
197
198 ilock(&nvrtlock);
199 for(t = offset; t < offset + n; t++){
200 if(t >= Nvsize)
201 break;
202 outb(Paddr, Nvoff+t);
203 *a++ = inb(Pdata);
204 }
205 iunlock(&nvrtlock);
206
207 if(waserror()){
208 free(start);
209 nexterror();
210 }
211 memmove(buf, start, t - offset);
212 poperror();
213
214 free(start);
215 return t - offset;
216 }
217 error(Ebadarg);
218 return 0;
219 }
220
221 #define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
222
223 static long
224 rtcwrite(Chan* c, void* buf, long n, vlong off)
225 {
226 int t;
227 char *a, *start;
228 Rtc rtc;
229 ulong secs;
230 uchar bcdclock[Nbcd];
231 char *cp, *ep;
232 ulong offset = off;
233
234 if(offset!=0)
235 error(Ebadarg);
236
237
238 switch((ulong)c->qid.path){
239 case Qrtc:
240 /*
241 * read the time
242 */
243 cp = ep = buf;
244 ep += n;
245 while(cp < ep){
246 if(*cp>='' && *cp<='9')
247 break;
248 cp++;
249 }
250 secs = strtoul(cp, 0, 0);
251
252 /*
253 * convert to bcd
254 */
255 sec2rtc(secs, &rtc);
256 PUTBCD(rtc.sec, 0);
257 PUTBCD(rtc.min, 1);
258 PUTBCD(rtc.hour, 2);
259 PUTBCD(rtc.mday, 3);
260 PUTBCD(rtc.mon, 4);
261 PUTBCD(rtc.year, 5);
262
263 /*
264 * write the clock
265 */
266 ilock(&nvrtlock);
267 outb(Paddr, Seconds); outb(Pdata, bcdclock[0]);
268 outb(Paddr, Minutes); outb(Pdata, bcdclock[1]);
269 outb(Paddr, Hours); outb(Pdata, bcdclock[2]);
270 outb(Paddr, Mday); outb(Pdata, bcdclock[3]);
271 outb(Paddr, Month); outb(Pdata, bcdclock[4]);
272 outb(Paddr, Year); outb(Pdata, bcdclock[5]);
273 iunlock(&nvrtlock);
274 return n;
275 case Qnvram:
276 if(n == 0)
277 return 0;
278 if(n > Nvsize)
279 n = Nvsize;
280
281 start = a = smalloc(n);
282 if(waserror()){
283 free(start);
284 nexterror();
285 }
286 memmove(a, buf, n);
287 poperror();
288
289 ilock(&nvrtlock);
290 for(t = offset; t < offset + n; t++){
291 if(t >= Nvsize)
292 break;
293 outb(Paddr, Nvoff+t);
294 outb(Pdata, *a++);
295 }
296 iunlock(&nvrtlock);
297
298 free(start);
299 return t - offset;
300 }
301 error(Ebadarg);
302 return 0;
303 }
304
305 Dev rtcdevtab = {
306 'r',
307 "rtc",
308
309 devreset,
310 rtcinit,
311 devshutdown,
312 rtcattach,
313 rtcwalk,
314 rtcstat,
315 rtcopen,
316 devcreate,
317 rtcclose,
318 rtcread,
319 devbread,
320 rtcwrite,
321 devbwrite,
322 devremove,
323 devwstat,
324 };
325
326 #define SEC2MIN 60L
327 #define SEC2HOUR (60L*SEC2MIN)
328 #define SEC2DAY (24L*SEC2HOUR)
329
330 /*
331 * days per month plus days/year
332 */
333 static int dmsize[] =
334 {
335 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
336 };
337 static int ldmsize[] =
338 {
339 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
340 };
341
342 /*
343 * return the days/month for the given year
344 */
345 static int*
346 yrsize(int y)
347 {
348 if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
349 return ldmsize;
350 else
351 return dmsize;
352 }
353
354 /*
355 * compute seconds since Jan 1 1970
356 */
357 static ulong
358 rtc2sec(Rtc *rtc)
359 {
360 ulong secs;
361 int i;
362 int *d2m;
363
364 secs = 0;
365
366 /*
367 * seconds per year
368 */
369 for(i = 1970; i < rtc->year; i++){
370 d2m = yrsize(i);
371 secs += d2m[0] * SEC2DAY;
372 }
373
374 /*
375 * seconds per month
376 */
377 d2m = yrsize(rtc->year);
378 for(i = 1; i < rtc->mon; i++)
379 secs += d2m[i] * SEC2DAY;
380
381 secs += (rtc->mday-1) * SEC2DAY;
382 secs += rtc->hour * SEC2HOUR;
383 secs += rtc->min * SEC2MIN;
384 secs += rtc->sec;
385
386 return secs;
387 }
388
389 /*
390 * compute rtc from seconds since Jan 1 1970
391 */
392 static void
393 sec2rtc(ulong secs, Rtc *rtc)
394 {
395 int d;
396 long hms, day;
397 int *d2m;
398
399 /*
400 * break initial number into days
401 */
402 hms = secs % SEC2DAY;
403 day = secs / SEC2DAY;
404 if(hms < 0) {
405 hms += SEC2DAY;
406 day -= 1;
407 }
408
409 /*
410 * generate hours:minutes:seconds
411 */
412 rtc->sec = hms % 60;
413 d = hms / 60;
414 rtc->min = d % 60;
415 d /= 60;
416 rtc->hour = d;
417
418 /*
419 * year number
420 */
421 if(day >= 0)
422 for(d = 1970; day >= *yrsize(d); d++)
423 day -= *yrsize(d);
424 else
425 for (d = 1970; day < 0; d--)
426 day += *yrsize(d-1);
427 rtc->year = d;
428
429 /*
430 * generate month
431 */
432 d2m = yrsize(rtc->year);
433 for(d = 1; day >= d2m[d]; d++)
434 day -= d2m[d];
435 rtc->mday = day + 1;
436 rtc->mon = d;
437
438 return;
439 }
440
441 uchar
442 nvramread(int addr)
443 {
444 uchar data;
445
446 ilock(&nvrtlock);
447 outb(Paddr, addr);
448 data = inb(Pdata);
449 iunlock(&nvrtlock);
450
451 return data;
452 }
453
454 void
455 nvramwrite(int addr, uchar data)
456 {
457 ilock(&nvrtlock);
458 outb(Paddr, addr);
459 outb(Pdata, data);
460 iunlock(&nvrtlock);
461 }
Cache object: f9ae0a07a60c2e7e4c993385f3ded62c
|