The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/pc/devrtc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.