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/drivers/cmos/cmos.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 /* This file contains a device driver that can access the CMOS chip to 
    2  * get or set the system time. It drives the special file:
    3  *
    4  *     /dev/cmos        - CMOS chip
    5  *
    6  * Changes:
    7  *     Aug 04, 2005     Created. Read CMOS time.  (Jorrit N. Herder)
    8  *
    9  * Manufacturers usually use the ID value of the IBM model they emulate.
   10  * However some manufacturers, notably HP and COMPAQ, have had different
   11  * ideas in the past.
   12  *
   13  * Machine ID byte information source:
   14  *      _The Programmer's PC Sourcebook_ by Thom Hogan,
   15  *      published by Microsoft Press
   16  */
   17 
   18 #include "../drivers.h"
   19 #include <sys/ioc_cmos.h>
   20 #include <time.h>
   21 #include <ibm/cmos.h>
   22 #include <ibm/bios.h>
   23 
   24 extern int errno;                       /* error number for PM calls */
   25 
   26 FORWARD _PROTOTYPE( int gettime, (int who, int y2kflag, vir_bytes dst_time));
   27 FORWARD _PROTOTYPE( void reply, (int reply, int replyee, int proc, int s));
   28 
   29 FORWARD _PROTOTYPE( int read_register, (int register_address));
   30 FORWARD _PROTOTYPE( int get_cmostime, (struct tm *tmp, int y2kflag));
   31 FORWARD _PROTOTYPE( int dec_to_bcd, (int dec));
   32 FORWARD _PROTOTYPE( int bcd_to_dec, (int bcd));
   33 
   34 /*===========================================================================*
   35  *                                 main                                      *
   36  *===========================================================================*/
   37 PUBLIC void main(void)
   38 {
   39   message m;
   40   int y2kflag;
   41   int result;
   42   int suspended = NONE;
   43   int s;
   44 
   45   while(TRUE) {
   46 
   47       /* Get work. */
   48       if (OK != (s=receive(ANY, &m)))
   49           panic("CMOS", "attempt to receive work failed", s);
   50 
   51       /* Handle request. */
   52       switch(m.m_type) {
   53 
   54       case DEV_OPEN:
   55       case DEV_CLOSE:
   56       case CANCEL:
   57           reply(TASK_REPLY, m.m_source, m.PROC_NR, OK);
   58           break;
   59 
   60       case DEV_PING:
   61           notify(m.m_source);
   62           break;
   63       case DEV_IOCTL:                           
   64 
   65           /* Probably best to SUSPEND the caller, CMOS I/O has nasty timeouts. 
   66            * This way we don't block the rest of the system. First check if
   67            * another process is already suspended. We cannot handle multiple
   68            * requests at a time. 
   69            */
   70           if (suspended != NONE) {
   71               reply(TASK_REPLY, m.m_source, m.PROC_NR, EBUSY);
   72               break;
   73           }
   74           suspended = m.PROC_NR;
   75           reply(TASK_REPLY, m.m_source, m.PROC_NR, SUSPEND);
   76 
   77           switch(m.REQUEST) {
   78           case CIOCGETTIME:                     /* get CMOS time */ 
   79           case CIOCGETTIMEY2K:
   80               y2kflag = (m.REQUEST = CIOCGETTIME) ? 0 : 1;
   81               result = gettime(m.PROC_NR, y2kflag, (vir_bytes) m.ADDRESS);
   82               break;
   83           case CIOCSETTIME:
   84           case CIOCSETTIMEY2K:
   85           default:                              /* unsupported ioctl */
   86               result = ENOSYS;
   87           }
   88 
   89           /* Request completed. Tell the caller to check our status. */
   90           notify(m.m_source);
   91           break;
   92 
   93       case DEV_STATUS:
   94 
   95           /* The FS calls back to get our status. Revive the suspended 
   96            * processes and return the status of reading the CMOS.
   97            */
   98           if (suspended == NONE)
   99               reply(DEV_NO_STATUS, m.m_source, NONE, OK);
  100           else 
  101               reply(DEV_REVIVE, m.m_source, suspended, result);
  102           suspended = NONE;
  103           break;
  104 
  105       case SYN_ALARM:           /* shouldn't happen */
  106       case SYS_SIG:             /* ignore system events */
  107           continue;             
  108 
  109       default:
  110           reply(TASK_REPLY, m.m_source, m.PROC_NR, EINVAL);
  111       } 
  112   }
  113 }
  114 
  115 /*===========================================================================*
  116  *                              reply                                        *
  117  *===========================================================================*/
  118 PRIVATE void reply(int code, int replyee, int process, int status)
  119 {
  120   message m;
  121   int s;
  122 
  123   m.m_type = code;              /* TASK_REPLY or REVIVE */
  124   m.REP_STATUS = status;        /* result of device operation */
  125   m.REP_PROC_NR = process;      /* which user made the request */
  126   if (OK != (s=send(replyee, &m)))
  127       panic("CMOS", "sending reply failed", s);
  128 }
  129 
  130 /*===========================================================================*
  131  *                              gettime                                      *
  132  *===========================================================================*/
  133 PRIVATE int gettime(int who, int y2kflag, vir_bytes dst_time)
  134 {
  135   unsigned char mach_id, cmos_state;
  136   struct tm time1;
  137   int i, s;
  138 
  139   /* First obtain the machine ID to see if we can read the CMOS clock. Only
  140    * for PS_386 and PC_AT this is possible. Otherwise, return an error.  
  141    */
  142   sys_vircopy(SELF, BIOS_SEG, (vir_bytes) MACHINE_ID_ADDR, 
  143         SELF, D, (vir_bytes) &mach_id, MACHINE_ID_SIZE);
  144   if (mach_id != PS_386_MACHINE && mach_id != PC_AT_MACHINE) {
  145         printf("IS: Machine ID unknown. ID byte = %02x.\n", mach_id);
  146         return(EFAULT);
  147   }
  148 
  149   /* Now check the CMOS' state to see if we can read a proper time from it.
  150    * If the state is crappy, return an error.
  151    */
  152   cmos_state = read_register(CMOS_STATUS);
  153   if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) {
  154         printf( "IS: CMOS RAM error(s) found. State = 0x%02x\n", cmos_state );
  155         if (cmos_state & CS_LOST_POWER)
  156             printf("IS: RTC lost power. Reset CMOS RAM with SETUP." );
  157         if (cmos_state & CS_BAD_CHKSUM)
  158             printf("IS: CMOS RAM checksum is bad. Run SETUP." );
  159         if (cmos_state & CS_BAD_TIME)
  160             printf("IS: Time invalid in CMOS RAM. Reset clock." );
  161         return(EFAULT);
  162   }
  163 
  164   /* Everything seems to be OK. Read the CMOS real time clock and copy the
  165    * result back to the caller.
  166    */
  167   if (get_cmostime(&time1, y2kflag) != 0)
  168         return(EFAULT);
  169   sys_datacopy(SELF, (vir_bytes) &time1, 
  170         who, dst_time, sizeof(struct tm));
  171 
  172   return(OK);
  173 }
  174 
  175 PRIVATE int get_cmostime(struct tm *t, int y2kflag)
  176 {
  177 /* Update the structure pointed to by time with the current time as read
  178  * from CMOS RAM of the RTC. If necessary, the time is converted into a
  179  * binary format before being stored in the structure.
  180  */
  181   int osec, n;
  182   unsigned long i;
  183   clock_t t0,t1;
  184 
  185   /* Start a timer to keep us from getting stuck on a dead clock. */
  186   getuptime(&t0);
  187   do {
  188         osec = -1;
  189         n = 0;
  190         do {
  191                 getuptime(&t1); 
  192                 if (t1-t0 > 5*HZ) {
  193                         printf("readclock: CMOS clock appears dead\n");
  194                         return(1);
  195                 }
  196 
  197                 /* Clock update in progress? */
  198                 if (read_register(RTC_REG_A) & RTC_A_UIP) continue;
  199 
  200                 t->tm_sec = read_register(RTC_SEC);
  201                 if (t->tm_sec != osec) {
  202                         /* Seconds changed.  First from -1, then because the
  203                          * clock ticked, which is what we're waiting for to
  204                          * get a precise reading.
  205                          */
  206                         osec = t->tm_sec;
  207                         n++;
  208                 }
  209         } while (n < 2);
  210 
  211         /* Read the other registers. */
  212         t->tm_min = read_register(RTC_MIN);
  213         t->tm_hour = read_register(RTC_HOUR);
  214         t->tm_mday = read_register(RTC_MDAY);
  215         t->tm_mon = read_register(RTC_MONTH);
  216         t->tm_year = read_register(RTC_YEAR);
  217 
  218         /* Time stable? */
  219   } while (read_register(RTC_SEC) != t->tm_sec
  220         || read_register(RTC_MIN) != t->tm_min
  221         || read_register(RTC_HOUR) != t->tm_hour
  222         || read_register(RTC_MDAY) != t->tm_mday
  223         || read_register(RTC_MONTH) != t->tm_mon
  224         || read_register(RTC_YEAR) != t->tm_year);
  225 
  226   if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) {
  227         /* Convert BCD to binary (default RTC mode). */
  228         t->tm_year = bcd_to_dec(t->tm_year);
  229         t->tm_mon = bcd_to_dec(t->tm_mon);
  230         t->tm_mday = bcd_to_dec(t->tm_mday);
  231         t->tm_hour = bcd_to_dec(t->tm_hour);
  232         t->tm_min = bcd_to_dec(t->tm_min);
  233         t->tm_sec = bcd_to_dec(t->tm_sec);
  234   }
  235   t->tm_mon--;  /* Counts from 0. */
  236 
  237   /* Correct the year, good until 2080. */
  238   if (t->tm_year < 80) t->tm_year += 100;
  239 
  240   if (y2kflag) {
  241         /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
  242         if (t->tm_year < 100) t->tm_year += 20;
  243   }
  244   return 0;
  245 }
  246 
  247 PRIVATE int read_register(int reg_addr)
  248 {
  249 /* Read a single CMOS register value. */
  250   int r = 0;
  251   sys_outb(RTC_INDEX, reg_addr);
  252   sys_inb(RTC_IO, &r);
  253   return r;
  254 }
  255 
  256 PRIVATE int bcd_to_dec(int n)
  257 {
  258   return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
  259 }
  260 
  261 PRIVATE int dec_to_bcd(int n)
  262 {
  263   return ((n / 10) << 4) | (n % 10);
  264 }
  265 

Cache object: 4a087b2aed50272d65e1ba68d3f5733d


[ 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.