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 #include "devtab.h" 8 9 /* 10 * real time clock and non-volatile ram 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 Nvoff= 128, /* where usable nvram lives */ 26 Nvsize= 256, 27 28 Nbcd= 6, 29 }; 30 31 typedef struct Rtc Rtc; 32 struct Rtc 33 { 34 int sec; 35 int min; 36 int hour; 37 int mday; 38 int mon; 39 int year; 40 }; 41 42 QLock rtclock; /* mutex on clock operations */ 43 44 enum{ 45 Qrtc = 1, 46 Qnvram, 47 }; 48 49 #define NRTC 2 50 Dirtab rtcdir[]={ 51 "nvram", {Qnvram, 0}, Nvsize, 0664, 52 "rtc", {Qrtc, 0}, 0, 0664, 53 }; 54 55 ulong rtc2sec(Rtc*); 56 void sec2rtc(ulong, Rtc*); 57 int *yrsize(int); 58 59 void 60 rtcreset(void) 61 { 62 } 63 64 void 65 rtcinit(void) 66 { 67 } 68 69 Chan* 70 rtcattach(char *spec) 71 { 72 return devattach('r', spec); 73 } 74 75 Chan* 76 rtcclone(Chan *c, Chan *nc) 77 { 78 return devclone(c, nc); 79 } 80 81 int 82 rtcwalk(Chan *c, char *name) 83 { 84 return devwalk(c, name, rtcdir, NRTC, devgen); 85 } 86 87 void 88 rtcstat(Chan *c, char *dp) 89 { 90 devstat(c, dp, rtcdir, NRTC, devgen); 91 } 92 93 Chan* 94 rtcopen(Chan *c, int omode) 95 { 96 omode = openmode(omode); 97 switch(c->qid.path){ 98 case Qrtc: 99 if(strcmp(u->p->user, eve)!=0 && omode!=OREAD) 100 error(Eperm); 101 break; 102 case Qnvram: 103 if(strcmp(u->p->user, eve)!=0) 104 error(Eperm); 105 } 106 return devopen(c, omode, rtcdir, NRTC, devgen); 107 } 108 109 void 110 rtccreate(Chan *c, char *name, int omode, ulong perm) 111 { 112 USED(c, name, omode, perm); 113 error(Eperm); 114 } 115 116 void 117 rtcclose(Chan *c) 118 { 119 USED(c); 120 } 121 122 #define GETBCD(o) ((bcdclock[o]&0xf) + 10*(bcdclock[o]>>4)) 123 124 long 125 rtctime(void) 126 { 127 uchar bcdclock[Nbcd]; 128 Rtc rtc; 129 int i; 130 131 for(i = 0; i < 10000; i++){ 132 outb(Paddr, Status); 133 if((inb(Pdata) & 1) == 0) 134 break; 135 } 136 outb(Paddr, Seconds); bcdclock[0] = inb(Pdata); 137 outb(Paddr, Minutes); bcdclock[1] = inb(Pdata); 138 outb(Paddr, Hours); bcdclock[2] = inb(Pdata); 139 outb(Paddr, Mday); bcdclock[3] = inb(Pdata); 140 outb(Paddr, Month); bcdclock[4] = inb(Pdata); 141 outb(Paddr, Year); bcdclock[5] = inb(Pdata); 142 143 /* 144 * convert from BCD 145 */ 146 rtc.sec = GETBCD(0); 147 rtc.min = GETBCD(1); 148 rtc.hour = GETBCD(2); 149 rtc.mday = GETBCD(3); 150 rtc.mon = GETBCD(4); 151 rtc.year = GETBCD(5); 152 153 /* 154 * the world starts jan 1 1970 155 */ 156 if(rtc.year < 70) 157 rtc.year += 2000; 158 else 159 rtc.year += 1900; 160 return rtc2sec(&rtc); 161 } 162 163 long 164 rtcread(Chan *c, void *buf, long n, ulong offset) 165 { 166 ulong t, ot; 167 char *a; 168 169 if(c->qid.path & CHDIR) 170 return devdirread(c, buf, n, rtcdir, NRTC, devgen); 171 172 switch(c->qid.path){ 173 case Qrtc: 174 qlock(&rtclock); 175 t = rtctime(); 176 do{ 177 ot = t; 178 t = rtctime(); /* make sure there's no skew */ 179 }while(t != ot); 180 qunlock(&rtclock); 181 n = readnum(offset, buf, n, t, 12); 182 return n; 183 case Qnvram: 184 a = buf; 185 if(waserror()){ 186 qunlock(&rtclock); 187 nexterror(); 188 } 189 qlock(&rtclock); 190 for(t = offset; t < offset + n; t++){ 191 if(t >= Nvsize) 192 break; 193 outb(Paddr, Nvoff+t); 194 delay(1); 195 *a++ = inb(Pdata); 196 } 197 qunlock(&rtclock); 198 poperror(); 199 return t - offset; 200 } 201 error(Ebadarg); 202 return 0; 203 } 204 205 #define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4) 206 207 long 208 rtcwrite(Chan *c, void *buf, long n, ulong offset) 209 { 210 int t; 211 char *a; 212 Rtc rtc; 213 ulong secs; 214 uchar bcdclock[Nbcd]; 215 char *cp, *ep; 216 217 USED(c); 218 if(offset!=0) 219 error(Ebadarg); 220 221 222 switch(c->qid.path){ 223 case Qrtc: 224 /* 225 * read the time 226 */ 227 cp = ep = buf; 228 ep += n; 229 while(cp < ep){ 230 if(*cp>='0' && *cp<='9') 231 break; 232 cp++; 233 } 234 secs = strtoul(cp, 0, 0); 235 236 /* 237 * convert to bcd 238 */ 239 sec2rtc(secs, &rtc); 240 PUTBCD(rtc.sec, 0); 241 PUTBCD(rtc.min, 1); 242 PUTBCD(rtc.hour, 2); 243 PUTBCD(rtc.mday, 3); 244 PUTBCD(rtc.mon, 4); 245 PUTBCD(rtc.year, 5); 246 247 /* 248 * write the clock 249 */ 250 qlock(&rtclock); 251 outb(Paddr, Seconds); outb(Pdata, bcdclock[0]); 252 outb(Paddr, Minutes); outb(Pdata, bcdclock[1]); 253 outb(Paddr, Hours); outb(Pdata, bcdclock[2]); 254 outb(Paddr, Mday); outb(Pdata, bcdclock[3]); 255 outb(Paddr, Month); outb(Pdata, bcdclock[4]); 256 outb(Paddr, Year); outb(Pdata, bcdclock[5]); 257 qunlock(&rtclock); 258 return n; 259 case Qnvram: 260 a = buf; 261 if(waserror()){ 262 qunlock(&rtclock); 263 nexterror(); 264 } 265 qlock(&rtclock); 266 for(t = offset; t < offset + n; t++){ 267 if(t >= Nvsize) 268 break; 269 outb(Paddr, Nvoff+t); 270 outb(Pdata, *a++); 271 } 272 qunlock(&rtclock); 273 poperror(); 274 return t - offset; 275 } 276 error(Ebadarg); 277 return 0; 278 } 279 280 void 281 rtcremove(Chan *c) 282 { 283 USED(c); 284 error(Eperm); 285 } 286 287 void 288 rtcwstat(Chan *c, char *dp) 289 { 290 USED(c, dp); 291 error(Eperm); 292 } 293 294 #define SEC2MIN 60L 295 #define SEC2HOUR (60L*SEC2MIN) 296 #define SEC2DAY (24L*SEC2HOUR) 297 298 /* 299 * days per month plus days/year 300 */ 301 static int dmsize[] = 302 { 303 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 304 }; 305 static int ldmsize[] = 306 { 307 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 308 }; 309 310 /* 311 * return the days/month for the given year 312 */ 313 int * 314 yrsize(int yr) 315 { 316 if((yr % 4) == 0) 317 return ldmsize; 318 else 319 return dmsize; 320 } 321 322 /* 323 * compute seconds since Jan 1 1970 324 */ 325 ulong 326 rtc2sec(Rtc *rtc) 327 { 328 ulong secs; 329 int i; 330 int *d2m; 331 332 secs = 0; 333 334 /* 335 * seconds per year 336 */ 337 for(i = 1970; i < rtc->year; i++){ 338 d2m = yrsize(i); 339 secs += d2m[0] * SEC2DAY; 340 } 341 342 /* 343 * seconds per month 344 */ 345 d2m = yrsize(rtc->year); 346 for(i = 1; i < rtc->mon; i++) 347 secs += d2m[i] * SEC2DAY; 348 349 secs += (rtc->mday-1) * SEC2DAY; 350 secs += rtc->hour * SEC2HOUR; 351 secs += rtc->min * SEC2MIN; 352 secs += rtc->sec; 353 354 return secs; 355 } 356 357 /* 358 * compute rtc from seconds since Jan 1 1970 359 */ 360 void 361 sec2rtc(ulong secs, Rtc *rtc) 362 { 363 int d; 364 long hms, day; 365 int *d2m; 366 367 /* 368 * break initial number into days 369 */ 370 hms = secs % SEC2DAY; 371 day = secs / SEC2DAY; 372 if(hms < 0) { 373 hms += SEC2DAY; 374 day -= 1; 375 } 376 377 /* 378 * generate hours:minutes:seconds 379 */ 380 rtc->sec = hms % 60; 381 d = hms / 60; 382 rtc->min = d % 60; 383 d /= 60; 384 rtc->hour = d; 385 386 /* 387 * year number 388 */ 389 if(day >= 0) 390 for(d = 1970; day >= *yrsize(d); d++) 391 day -= *yrsize(d); 392 else 393 for (d = 1970; day < 0; d--) 394 day += *yrsize(d-1); 395 rtc->year = d; 396 397 /* 398 * generate month 399 */ 400 d2m = yrsize(rtc->year); 401 for(d = 1; day >= d2m[d]; d++) 402 day -= d2m[d]; 403 rtc->mday = day + 1; 404 rtc->mon = d; 405 406 return; 407 } 408 409 uchar 410 nvramread(int offset) 411 { 412 outb(Paddr, offset); 413 delay(1); 414 return inb(Pdata); 415 } 416