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->env->user, eve)!=0 && omode!=OREAD) 89 error(Eperm); 90 break; 91 case Qnvram: 92 if(strcmp(up->env->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>='0' && *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 } 462