iPXE
rtc_time.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * RTC-based time source
00029  *
00030  */
00031 
00032 #include <stdint.h>
00033 #include <time.h>
00034 #include <rtc.h>
00035 #include <ipxe/time.h>
00036 
00037 /**
00038  * Read RTC register
00039  *
00040  * @v address           Register address
00041  * @ret data            Data
00042  */
00043 static unsigned int rtc_readb ( int address ) {
00044         outb ( address, CMOS_ADDRESS );
00045         return inb ( CMOS_DATA );
00046 }
00047 
00048 /**
00049  * Check if RTC update is in progress
00050  *
00051  * @ret is_busy         RTC update is in progress
00052  */
00053 static int rtc_is_busy ( void ) {
00054         return ( rtc_readb ( RTC_STATUS_A ) & RTC_STATUS_A_UPDATE_IN_PROGRESS );
00055 }
00056 
00057 /**
00058  * Read RTC BCD register
00059  *
00060  * @v address           Register address
00061  * @ret value           Value
00062  */
00063 static unsigned int rtc_readb_bcd ( int address ) {
00064         unsigned int bcd;
00065 
00066         bcd = rtc_readb ( address );
00067         return ( bcd - ( 6 * ( bcd >> 4 ) ) );
00068 }
00069 
00070 /**
00071  * Read RTC time
00072  *
00073  * @ret time            Time, in seconds
00074  */
00075 static time_t rtc_read_time ( void ) {
00076         unsigned int status_b;
00077         int is_binary;
00078         int is_24hour;
00079         unsigned int ( * read_component ) ( int address );
00080         struct tm tm;
00081         int is_pm;
00082         unsigned int hour;
00083         time_t time;
00084 
00085         /* Wait for any in-progress update to complete */
00086         while ( rtc_is_busy() ) {}
00087 
00088         /* Determine RTC mode */
00089         status_b = rtc_readb ( RTC_STATUS_B );
00090         is_binary = ( status_b & RTC_STATUS_B_BINARY );
00091         is_24hour = ( status_b & RTC_STATUS_B_24_HOUR );
00092         read_component = ( is_binary ? rtc_readb : rtc_readb_bcd );
00093 
00094         /* Read time values */
00095         tm.tm_sec = read_component ( RTC_SEC );
00096         tm.tm_min = read_component ( RTC_MIN );
00097         hour = read_component ( RTC_HOUR );
00098         if ( ! is_24hour ) {
00099                 is_pm = ( hour >= 80 );
00100                 hour = ( ( ( ( hour & 0x7f ) % 80 ) % 12 ) +
00101                          ( is_pm ? 12 : 0 ) );
00102         }
00103         tm.tm_hour = hour;
00104         tm.tm_mday = read_component ( RTC_MDAY );
00105         tm.tm_mon = ( read_component ( RTC_MON ) - 1 );
00106         tm.tm_year = ( read_component ( RTC_YEAR ) +
00107                        100 /* Assume we are in the 21st century, since
00108                             * this code was written in 2012 */ );
00109 
00110         DBGC ( RTC_STATUS_A, "RTCTIME is %04d-%02d-%02d %02d:%02d:%02d "
00111                "(%s,%d-hour)\n", ( tm.tm_year + 1900 ), ( tm.tm_mon + 1 ),
00112                tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
00113                ( is_binary ? "binary" : "BCD" ), ( is_24hour ? 24 : 12 ) );
00114 
00115         /* Convert to seconds since the Epoch */
00116         time = mktime ( &tm );
00117 
00118         return time;
00119 }
00120 
00121 /**
00122  * Get current time in seconds
00123  *
00124  * @ret time            Time, in seconds
00125  */
00126 static time_t rtc_now ( void ) {
00127         time_t time = 0;
00128         time_t last_time;
00129 
00130         /* Read time until we get two matching values in a row, in
00131          * case we end up reading a corrupted value in the middle of
00132          * an update.
00133          */
00134         do {
00135                 last_time = time;
00136                 time = rtc_read_time();
00137         } while ( time != last_time );
00138 
00139         return time;
00140 }
00141 
00142 PROVIDE_TIME ( rtc, time_now, rtc_now );