Re: Real Time Clock driver for 1.3.81

Paul Gortmaker (gpg109@rsphy1.anu.edu.au)
Tue, 2 Apr 1996 14:13:03 +1000 (EST)


If you installed the rtc driver (v1.01) from my previous post, here is a
minor update (to rtc v1.03) that you can apply on top of that patch. This
adds the RTC_SET_TIME ioctl() as requested by the Linux-SMP group, and fixes
a few typos in the documentation/demo. Also, months are zero based and years
are 1900 based in keeping with the struct tm definition from "man mktime".

If you want the diff against a clean 1.3.81 tree, then get it from:

http://rsphy1.anu.edu.au/~gpg109/rtc-1381

I also have patched /sbin/clock (not included here) to use ioctl()
via /dev/rtc if it is available, which should be of interest to
Linux-SMP people. The present /sbin/clock uses cli/sti from user
space which isn't SMP compatible.

Regards,
Paul.

diff -ur /mnt/linux-1381/Documentation/Configure.help linux/Documentation/Configure.help
--- /mnt/linux-1381/Documentation/Configure.help Tue Apr 2 13:03:50 1996
+++ linux/Documentation/Configure.help Tue Apr 2 12:22:40 1996
@@ -2946,7 +2946,9 @@
into your computer. It can be used to generate signals from as
low as 1Hz up to 8192Hz, and can also be used as a 24 hour alarm.
It reports status information via the file /proc/rtc and its
- behaviour is set by various ioctls on /dev/rtc. If you think you
+ behaviour is set by various ioctls on /dev/rtc. People running
+ SMP versions of Linux should enable this option to read and set
+ the RTC clock in a SMP compatible fashion. If you think you
have a use for such a device (such as periodic data sampling), then
say Y here, and go read the file Documentation/rtc.txt for details.

diff -ur /mnt/linux-1381/Documentation/rtc.txt linux/Documentation/rtc.txt
--- /mnt/linux-1381/Documentation/rtc.txt Tue Apr 2 13:03:51 1996
+++ linux/Documentation/rtc.txt Tue Apr 2 13:15:38 1996
@@ -37,7 +37,7 @@
been any interrupt "pileup" so to speak. Just for reference, a typical
486-33 running a tight read loop on /dev/rtc will start to suffer
occasional interrupt pileup (i.e. > 1 IRQ event since last read) for
-frequencies above 1024kHz. So you really should check the low four bits
+frequencies above 1024Hz. So you really should check the low four bits
of the value you read, especially at frequencies above that of the
normal timer interrupt, which is 100Hz. Programming and/or enabling
interrupt frequencies greater than 64Hz is only allowed by root. This is
@@ -83,7 +83,7 @@

void main(void) {

-int i, fd, retval, irqcount;
+int i, fd, retval, irqcount = 0;
unsigned char data;
unsigned long tmp;
struct tm rtc_tm;
@@ -98,7 +98,7 @@
fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");

/* Turn on update interrupts (one per second) */
-retval = ioctl(fd, RTC_UIE_ON);
+retval = ioctl(fd, RTC_UIE_ON, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
@@ -142,7 +142,7 @@
}

/* Turn off update interrupts */
-retval = ioctl(fd, RTC_UIE_OFF);
+retval = ioctl(fd, RTC_UIE_OFF, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
@@ -156,7 +156,7 @@
}

fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
- rtc_tm.tm_mday, rtc_tm.tm_mon, rtc_tm.tm_year,
+ rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);

/* Set the alarm to 5 sec in the future, and check for rollover */
@@ -189,7 +189,7 @@
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);

/* Enable alarm interrupts */
-retval = ioctl(fd, RTC_AIE_ON);
+retval = ioctl(fd, RTC_AIE_ON, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
@@ -206,7 +206,7 @@
fprintf(stderr, " okay. Alarm rang.\n");

/* Disable alarm interrupts */
-retval = ioctl(fd, RTC_AIE_OFF);
+retval = ioctl(fd, RTC_AIE_OFF, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
@@ -236,7 +236,7 @@
fflush(stderr);

/* Enable periodic interrupts */
- retval = ioctl(fd, RTC_PIE_ON);
+ retval = ioctl(fd, RTC_PIE_ON, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
@@ -254,7 +254,7 @@
}

/* Disable periodic interrupts */
- retval = ioctl(fd, RTC_PIE_OFF);
+ retval = ioctl(fd, RTC_PIE_OFF, 0);
if (retval == -1) {
perror("ioctl");
exit(errno);
diff -ur /mnt/linux-1381/drivers/char/rtc.c linux/drivers/char/rtc.c
--- /mnt/linux-1381/drivers/char/rtc.c Tue Apr 2 13:03:52 1996
+++ linux/drivers/char/rtc.c Tue Apr 2 13:27:07 1996
@@ -30,7 +30,7 @@
*
*/

-#define RTC_VERSION "1.01"
+#define RTC_VERSION "1.03"

#define RTC_IRQ 8 /* Can't see this changing soon. */
#define RTC_IO_BASE 0x70 /* Or this... */
@@ -96,6 +96,9 @@
unsigned char rtc_status = 0; /* bitmapped status byte. */
unsigned char rtc_irq_data = 0; /* our output to the world */

+unsigned char days_in_mo[] =
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
/*
* A very tiny interrupt handler. It runs with SA_INTERRUPT set,
* so that there is no possibility of conflicting with the
@@ -300,10 +303,78 @@
}
case RTC_SET_TIME: /* Set the RTC */
{
- /*
- * No need for this at the moment. Use /sbin/clock.
- */
- return -ENOSYS;
+ int retval;
+ struct tm rtc_tm;
+ unsigned char mon, day, hrs, min, sec, leap_yr;
+ unsigned char save_control, save_freq_select;
+ unsigned int yrs;
+ unsigned long flags;
+
+ if (!suser())
+ return -EPERM;
+
+ retval = verify_area(VERIFY_READ, (struct tm*)arg, sizeof(struct tm));
+ if (retval !=0 )
+ return retval;
+
+ memcpy_fromfs(&rtc_tm, (struct tm*)arg, sizeof(struct tm));
+
+ yrs = rtc_tm.tm_year + 1900;
+ mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
+ day = rtc_tm.tm_mday;
+ hrs = rtc_tm.tm_hour;
+ min = rtc_tm.tm_min;
+ sec = rtc_tm.tm_sec;
+
+ if ((yrs < 1970) || (yrs > 2069))
+ return -EINVAL;
+
+ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+
+ if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+ return -EINVAL;
+
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ if (yrs >= 2000)
+ yrs -= 2000; /* RTC (0, 1, ... 69) */
+ else
+ yrs -= 1900; /* RTC (70, 71, ... 99) */
+
+ save_flags(flags);
+ cli();
+ if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) ||
+ RTC_ALWAYS_BCD)
+ {
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hrs);
+ BIN_TO_BCD(day);
+ BIN_TO_BCD(mon);
+ BIN_TO_BCD(yrs);
+ }
+
+ save_control = CMOS_READ(RTC_CONTROL);
+ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
+ CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+ CMOS_WRITE(yrs, RTC_YEAR);
+ CMOS_WRITE(mon, RTC_MONTH);
+ CMOS_WRITE(day, RTC_DAY_OF_MONTH);
+ CMOS_WRITE(hrs, RTC_HOURS);
+ CMOS_WRITE(min, RTC_MINUTES);
+ CMOS_WRITE(sec, RTC_SECONDS);
+
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+
+ restore_flags(flags);
+ return 0;
}
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
@@ -486,7 +557,8 @@
* time or for Universal Standard Time (GMT). Probably local though.
*/
p += sprintf(p, "\tRTC reports %02d:%02d:%02d of %d-%d-%d.\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mday, tm.tm_mon, tm.tm_year);
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mday,
+ tm.tm_mon + 1, tm.tm_year + 1900);

get_rtc_alm_time(&tm);

@@ -562,14 +634,15 @@

/*
* Only the values that we read from the RTC are set. We leave
- * tm_yday and tm_isdst untouched.
+ * tm_wday, tm_yday and tm_isdst untouched. Even though the
+ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+ * by the RTC when initially set to a non-zero value.
*/
save_flags(flags);
cli();
rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
- rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK);
rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
@@ -581,13 +654,19 @@
BCD_TO_BIN(rtc_tm->tm_sec);
BCD_TO_BIN(rtc_tm->tm_min);
BCD_TO_BIN(rtc_tm->tm_hour);
- BCD_TO_BIN(rtc_tm->tm_wday);
BCD_TO_BIN(rtc_tm->tm_mday);
BCD_TO_BIN(rtc_tm->tm_mon);
BCD_TO_BIN(rtc_tm->tm_year);
}
- if ((rtc_tm->tm_year += 1900) < 1970)
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct tm;
+ */
+ if (rtc_tm->tm_year <= 69)
rtc_tm->tm_year += 100;
+
+ rtc_tm->tm_mon--;
}

void get_rtc_alm_time(struct tm *alm_tm)