lkml.org 
[lkml]   [1996]   [Mar]   [31]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
SubjectReal Time Clock driver for 1.3.81
Date

I wasn't expecting the mouse stuff on major 10 to be renamed to misc
so soon, but I am definitely happy to see it done (thanks Alan).

Anyway, here is my real time clock driver, updated to the misc vs.
mouse naming convention mentioned above. Patch is against 1.3.81.
Read the top of the patch for a description of what you can use it for,
if you are interested.

Regards,
Paul.

======================================================================

diff -ur /mnt/linux-1381/Documentation/Configure.help linux/Documentation/Configure.help
--- /mnt/linux-1381/Documentation/Configure.help Sun Mar 31 12:04:59 1996
+++ linux/Documentation/Configure.help Sun Mar 31 12:21:01 1996
@@ -2938,6 +2938,18 @@
from some situations that the hardware watchdog will recover
from. Equally it's a lot cheaper to install.

+Enhanced Real Time Clock Support
+CONFIG_RTC
+ If you enable this option and create a character special file
+ /dev/rtc with major number 10 and minor number 134 using mknod
+ ("man mknod"), you will get access to the real time clock built
+ 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
+ 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.
+
Do CPU IDLE calls
CONFIG_APM_CPU_IDLE
Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop.
diff -ur /mnt/linux-1381/Documentation/rtc.txt linux/Documentation/rtc.txt
--- /mnt/linux-1381/Documentation/rtc.txt Fri Jan 1 00:00:00 1971
+++ linux/Documentation/rtc.txt Sun Mar 31 21:22:09 1996
@@ -0,0 +1,270 @@
+
+ Real Time Clock Driver for Linux
+ ================================
+
+Each PC (even Alpha machines) have a Real Time Clock built into them.
+Usually they are built into the chipset of the computer, but some may
+actually have a Motorola MC146818 (or clone) on the board. This is the
+clock that keeps the date and time while your computer is turned off.
+
+However it can also be used to generate signals from a slow 2Hz to a
+relatively fast 8192Hz, in increments of powers of two. These signals
+are reported by interrupt number 8. (Oh! So *thats* what IRQ 8 is
+for...) It can also function as a 24hr alarm, raising IRQ 8 when the
+alarm goes off. The alarm can also be programmed to only check any
+subset of the three programmable values, meaning that it could be set to
+ring on the 30th second of the 30th minute of every hour, for example.
+The clock can also be set to generate an interrupt upon every clock
+update, thus generating a 1Hz signal.
+
+The interrupts are reported via /dev/rtc (major 10, minor 134, read only
+character device) in the form of a single byte. The low 4 bits contain
+the number of interrupts since the last read, and the high 4 bits
+contain the type of interrupt (update-done, alarm-rang, or periodic)
+that was raised. Status information is reported through the pseudo-file
+/proc/rtc if the /proc filesystem was enabled. The driver has built in
+locking so that only one process is allowed to have the /dev/rtc
+interface open at a time.
+
+A user process can monitor these interrupts by doing a read(2) or a
+select(2) on /dev/rtc -- either will block/stop the user process until
+the next interrupt is received. This is useful for things like
+reasonably high frequency data acquisition where one doesn't want to
+burn up 100% CPU by polling gettimeofday etc. etc.
+
+At high frequencies, or under high loads, the user process can check the
+low four bits of the byte read from /dev/rtc to determine if there has
+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
+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
+perhaps a bit conservative, but we don't want an evil user generating
+lots of IRQs on a slow 386sx-16, where it might have a negative impact
+on performance. Note that the interrupt handler is only four lines of
+code to minimize any possibility of this effect.
+
+The alarm and/or interrupt frequency are programmed into the RTC via
+various ioctl(2) calls as listed in ./include/linux/mc146818rtc.h
+Rather than write 50 pages describing the ioctl() and so on, it is
+perhaps more useful to include a small test program that demonstrates
+how to use them, and demonstrates the features of the driver. This is
+probably a lot more useful to people interested in writing applications
+that will be using this driver.
+
+ Paul Gortmaker
+
+-------------------- 8< ---------------- 8< -----------------------------
+
+/*
+ * Real Time Clock Driver Test/Example Program
+ *
+ * Compile with:
+ * gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest
+ *
+ * Copyright (C) 1996, Paul Gortmaker.
+ *
+ * Released under the GNU General Public License, version 2,
+ * included herein by reference.
+ *
+ */
+
+#include <stdio.h>
+#include <linux/mc146818rtc.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+
+void main(void) {
+
+int i, fd, retval, irqcount;
+unsigned char data;
+unsigned long tmp;
+struct tm rtc_tm;
+
+fd = open ("/dev/rtc", O_RDONLY);
+
+if (fd == -1) {
+ perror("/dev/rtc");
+ exit(errno);
+}
+
+fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
+
+/* Turn on update interrupts (one per second) */
+retval = ioctl(fd, RTC_UIE_ON);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading /dev/rtc:");
+fflush(stderr);
+for (i=1; i<6; i++) {
+ retval = read(fd, &data, 1); /* this blocks */
+ if (retval == -1) {
+ perror("read");
+ exit(errno);
+ }
+ fprintf(stderr, " %d",i);
+ fflush(stderr);
+ irqcount++;
+}
+
+fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
+fflush(stderr);
+for (i=1; i<6; i++) {
+ struct timeval tv = {5, 0}; /* 5 second timeout on select */
+ struct fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+ retval = select(fd+1, &readfds, NULL, NULL, &tv);
+ if (retval == -1) {
+ perror("select");
+ exit(errno);
+ }
+ /* This read won't block unlike the select-less case above. */
+ retval = read(fd, &data, 1);
+ if (retval == -1) {
+ perror("read");
+ exit(errno);
+ }
+ fprintf(stderr, " %d",i);
+ fflush(stderr);
+ irqcount++;
+}
+
+/* Turn off update interrupts */
+retval = ioctl(fd, RTC_UIE_OFF);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+/* Read the RTC time/date */
+retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+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_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+
+/* Set the alarm to 5 sec in the future, and check for rollover */
+rtc_tm.tm_sec += 5;
+if (rtc_tm.tm_sec >= 60) {
+ rtc_tm.tm_sec %= 60;
+ rtc_tm.tm_min++;
+}
+if (rtc_tm.tm_min == 60) {
+ rtc_tm.tm_min = 0;
+ rtc_tm.tm_hour++;
+}
+if (rtc_tm.tm_hour == 24)
+ rtc_tm.tm_hour = 0;
+
+retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+/* Read the current alarm settings */
+retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",
+ rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
+
+/* Enable alarm interrupts */
+retval = ioctl(fd, RTC_AIE_ON);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+fprintf(stderr, "Waiting 5 seconds for alarm...");
+fflush(stderr);
+retval = read(fd, &data, 1); /* blocks until ring */
+if (retval == -1) {
+ perror("read");
+ exit(errno);
+}
+irqcount++;
+fprintf(stderr, " okay. Alarm rang.\n");
+
+/* Disable alarm interrupts */
+retval = ioctl(fd, RTC_AIE_OFF);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+
+/* Read periodic IRQ rate */
+retval = ioctl(fd, RTC_IRQP_READ, &tmp);
+if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+}
+fprintf(stderr, "\nPeriodic IRQ rate was %ldHz.\n", tmp);
+
+fprintf(stderr, "Counting 20 interrupts at:");
+fflush(stderr);
+
+/* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
+for (tmp=2; tmp<=64; tmp*=2) {
+
+ retval = ioctl(fd, RTC_IRQP_SET, tmp);
+ if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+ }
+
+ fprintf(stderr, "\n%ldHz:\t", tmp);
+ fflush(stderr);
+
+ /* Enable periodic interrupts */
+ retval = ioctl(fd, RTC_PIE_ON);
+ if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+ }
+
+ for (i=1; i<21; i++) {
+ retval = read(fd, &data, 1); /* blocks */
+ if (retval == -1) {
+ perror("read");
+ exit(errno);
+ }
+ fprintf(stderr, " %d",i);
+ fflush(stderr);
+ irqcount++;
+ }
+
+ /* Disable periodic interrupts */
+ retval = ioctl(fd, RTC_PIE_OFF);
+ if (retval == -1) {
+ perror("ioctl");
+ exit(errno);
+ }
+}
+
+fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
+fprintf(stderr, "\nTyping \"cat /proc/interrupts\" will show %d more events on IRQ 8.\n\n",
+ irqcount);
+
+close(fd);
+
+} /* end main */
diff -ur /mnt/linux-1381/arch/alpha/defconfig linux/arch/alpha/defconfig
--- /mnt/linux-1381/arch/alpha/defconfig Mon Mar 25 19:56:11 1996
+++ linux/arch/alpha/defconfig Sun Mar 31 11:52:41 1996
@@ -188,6 +188,7 @@
# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set

#
# Sound
diff -ur /mnt/linux-1381/arch/i386/defconfig linux/arch/i386/defconfig
--- /mnt/linux-1381/arch/i386/defconfig Sun Mar 31 12:05:00 1996
+++ linux/arch/i386/defconfig Sun Mar 31 11:52:41 1996
@@ -152,6 +152,7 @@
# CONFIG_FTAPE is not set
# CONFIG_APM is not set
# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set

#
# Sound
diff -ur /mnt/linux-1381/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- /mnt/linux-1381/arch/i386/kernel/setup.c Mon Mar 25 21:34:39 1996
+++ linux/arch/i386/kernel/setup.c Sun Mar 31 11:52:41 1996
@@ -199,7 +199,6 @@
/* request io space for devices used on all i[345]86 PC'S */
request_region(0x00,0x20,"dma1");
request_region(0x40,0x20,"timer");
- request_region(0x70,0x10,"rtc");
request_region(0x80,0x20,"dma page reg");
request_region(0xc0,0x20,"dma2");
request_region(0xf0,0x10,"npu");
diff -ur /mnt/linux-1381/drivers/char/Config.in linux/drivers/char/Config.in
--- /mnt/linux-1381/drivers/char/Config.in Sun Mar 31 12:05:01 1996
+++ linux/drivers/char/Config.in Sun Mar 31 11:52:42 1996
@@ -58,4 +58,5 @@
bool ' Software Watchdog' CONFIG_SOFT_WATCHDOG
fi
fi
+bool 'Enhanced Real Time Clock Support' CONFIG_RTC
endmenu
diff -ur /mnt/linux-1381/drivers/char/Makefile linux/drivers/char/Makefile
--- /mnt/linux-1381/drivers/char/Makefile Sun Mar 31 12:05:01 1996
+++ linux/drivers/char/Makefile Sun Mar 31 11:57:06 1996
@@ -121,6 +121,12 @@
endif
endif

+ifeq ($(CONFIG_RTC),y)
+M = y
+# This is not modularized, so if configured then "misc.c" will be resident
+L_OBJS += rtc.o
+endif
+
ifdef CONFIG_QIC02_TAPE
L_OBJS += tpqic02.o
endif
diff -ur /mnt/linux-1381/drivers/char/mem.c linux/drivers/char/mem.c
--- /mnt/linux-1381/drivers/char/mem.c Sun Mar 31 12:05:02 1996
+++ linux/drivers/char/mem.c Sun Mar 31 11:55:00 1996
@@ -389,7 +389,8 @@
#endif
#if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \
defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
- defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG)
+ defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
+ defined (CONFIG_RTC)
misc_init();
#endif
#ifdef CONFIG_SOUND
diff -ur /mnt/linux-1381/drivers/char/misc.c linux/drivers/char/misc.c
--- /mnt/linux-1381/drivers/char/misc.c Sun Mar 31 12:05:02 1996
+++ linux/drivers/char/misc.c Sun Mar 31 11:56:46 1996
@@ -55,6 +55,7 @@
extern int ms_bus_mouse_init(void);
extern int atixl_busmouse_init(void);
extern void watchdog_init(void);
+extern int rtc_init(void);

#ifdef CONFIG_PROC_FS
static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
@@ -188,6 +189,9 @@
#ifdef CONFIG_SOFT_WATCHDOG
watchdog_init();
#endif
+#ifdef CONFIG_RTC
+ rtc_init();
+#endif
#endif /* !MODULE */
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
printk("unable to get major %d for misc devices\n",
diff -ur /mnt/linux-1381/drivers/char/rtc.c linux/drivers/char/rtc.c
--- /mnt/linux-1381/drivers/char/rtc.c Fri Jan 1 00:00:00 1971
+++ linux/drivers/char/rtc.c Sun Mar 31 13:47:27 1996
@@ -0,0 +1,656 @@
+/*
+ * Real Time Clock interface for Linux
+ *
+ * Copyright (C) 1996 Paul Gortmaker
+ *
+ * This driver allows use of the real time clock (built into
+ * nearly all computers) from user space. It exports the /dev/rtc
+ * interface supporting various ioctl() and also the /proc/rtc
+ * pseudo-file for status information.
+ *
+ * The ioctls can be used to set the interrupt behaviour and
+ * generation rate from the RTC via IRQ 8. Then the /dev/rtc
+ * interface can be used to make use of these timer interrupts,
+ * be they interval or alarm based.
+ *
+ * The /dev/rtc interface will block on reads until an interrupt
+ * has been received. If a RTC interrupt has already happened,
+ * it will output a byte and then block. The output byte contains
+ * the interrupt status in the high 4 bits and the number of
+ * interrupts since the last read in the low 4 bits. The /dev/rtc
+ * interface can also be used with the select(2) call.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on other minimal char device drivers, like Alan's
+ * watchdog, Ted's random, etc. etc.
+ *
+ */
+
+#define RTC_VERSION "1.01"
+
+#define RTC_IRQ 8 /* Can't see this changing soon. */
+#define RTC_IO_BASE 0x70 /* Or this... */
+#define RTC_IO_EXTENT 0x10 /* Only really 0x70 to 0x71, but... */
+
+/*
+ * Note that *all* calls to CMOS_READ and CMOS_WRITE are done with
+ * interrupts disabled. Due to the index-port/data-port (0x70/0x71)
+ * design of the RTC, we don't want two different things trying to
+ * get to it at once. (e.g. the periodic 11 min sync from time.c vs.
+ * this driver.)
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/mc146818rtc.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <time.h>
+
+/*
+ * We sponge a minor off of the misc major. No need slurping
+ * up another valuable major dev number for this.
+ */
+
+#define RTC_MINOR 134
+
+static struct wait_queue *rtc_wait;
+
+static int rtc_lseek(struct inode *inode, struct file *file, off_t offset,
+ int origin);
+
+static int rtc_read(struct inode *inode, struct file *file,
+ char *buf, int count);
+
+static int rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int rtc_select(struct inode *inode, struct file *file,
+ int sel_type, select_table *wait);
+
+void get_rtc_time (struct tm *rtc_tm);
+void get_rtc_alm_time (struct tm *alm_tm);
+
+inline void set_rtc_irq_bit(unsigned char bit);
+inline void mask_rtc_irq_bit(unsigned char bit);
+
+unsigned char rtc_is_updating(void);
+
+/*
+ * Bits in rtc_status. (7 bits of room for future expansion)
+ */
+
+#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+
+unsigned char rtc_status = 0; /* bitmapped status byte. */
+unsigned char rtc_irq_data = 0; /* our output to the world */
+
+/*
+ * A very tiny interrupt handler. It runs with SA_INTERRUPT set,
+ * so that there is no possibility of conflicting with the
+ * set_rtc_mmss() call that happens during some timer interrupts.
+ * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
+ */
+
+static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /*
+ * Can be an alarm interrupt, update complete interrupt,
+ * or a periodic interrupt. We store the status in the
+ * high nibble and the number of interrupts received since
+ * the last read in the remainder of rtc_irq_data.
+ */
+
+ rtc_irq_data++;
+ rtc_irq_data &= 0x0F;
+ rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
+ wake_up_interruptible(&rtc_wait);
+}
+
+/*
+ * Now all the various file operations that we export.
+ */
+
+static int rtc_lseek(struct inode *inode, struct file *file, off_t offset,
+ int origin)
+{
+ return -ESPIPE;
+}
+
+static int rtc_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+ struct wait_queue wait = { current, NULL };
+ int retval = 0;
+
+ if (count == 0)
+ return 0;
+
+ add_wait_queue(&rtc_wait, &wait);
+
+ current->state = TASK_INTERRUPTIBLE;
+
+ while (rtc_irq_data == 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ continue;
+ }
+
+ if (retval == 0) {
+ put_user(rtc_irq_data, buf);
+ rtc_irq_data = 0;
+ retval = 1; /* one byte of output */
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&rtc_wait, &wait);
+
+ return retval;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ unsigned long flags;
+
+ switch (cmd) {
+ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
+ {
+ mask_rtc_irq_bit(RTC_AIE);
+ return 0;
+ }
+ case RTC_AIE_ON: /* Allow alarm interrupts. */
+ {
+ set_rtc_irq_bit(RTC_AIE);
+ return 0;
+ }
+ case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
+ {
+ mask_rtc_irq_bit(RTC_PIE);
+ return 0;
+ }
+ case RTC_PIE_ON: /* Allow periodic ints */
+ {
+ unsigned int hz;
+ unsigned char tmp;
+
+ save_flags(flags);
+ cli();
+ tmp = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
+ restore_flags(flags);
+
+ hz = (tmp ? (65536/(1<<tmp)) : 0);
+
+ /*
+ * We don't really want Joe User enabling more
+ * than 64Hz of interrupts on a multi-user machine.
+ */
+ if ((hz >64) && (!suser()))
+ return -EPERM;
+
+ set_rtc_irq_bit(RTC_PIE);
+ return 0;
+ }
+ case RTC_UIE_OFF: /* Mask ints from RTC updates. */
+ {
+ mask_rtc_irq_bit(RTC_UIE);
+ return 0;
+ }
+ case RTC_UIE_ON: /* Allow ints for RTC updates. */
+ {
+ set_rtc_irq_bit(RTC_UIE);
+ return 0;
+ }
+ case RTC_ALM_READ: /* Read the present alarm time */
+ {
+ /*
+ * This returns a struct tm. Reading >= 0xc0 means
+ * "don't care" or "match all". Only the tm_hour,
+ * tm_min, and tm_sec values are filled in.
+ */
+ int retval;
+ struct tm alm_tm;
+
+ retval = verify_area(VERIFY_WRITE, (struct tm*)arg, sizeof(struct tm));
+ if (retval != 0 )
+ return retval;
+
+ get_rtc_alm_time(&alm_tm);
+
+ memcpy_tofs((struct tm*)arg, &alm_tm, sizeof(struct tm));
+
+ return 0;
+ }
+ case RTC_ALM_SET: /* Store a time into the alarm */
+ {
+ /*
+ * This expects a struct tm. Writing 0xff means
+ * "don't care" or "match all". Only the tm_hour,
+ * tm_min and tm_sec are used.
+ */
+ int retval;
+ unsigned char hrs, min, sec;
+ struct tm alm_tm;
+
+ retval = verify_area(VERIFY_READ, (struct tm*)arg, sizeof(struct tm));
+ if (retval != 0 )
+ return retval;
+
+ memcpy_fromfs(&alm_tm, (struct tm*)arg, sizeof(struct tm));
+
+ hrs = alm_tm.tm_hour;
+ min = alm_tm.tm_min;
+ sec = alm_tm.tm_sec;
+
+ if (hrs >= 24)
+ hrs = 0xff;
+
+ if (min >= 60)
+ min = 0xff;
+
+ if (sec >= 60)
+ sec = 0xff;
+
+ 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);
+ }
+ CMOS_WRITE(hrs, RTC_HOURS_ALARM);
+ CMOS_WRITE(min, RTC_MINUTES_ALARM);
+ CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+ restore_flags(flags);
+
+ return 0;
+ }
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ {
+ int retval;
+ struct tm rtc_tm;
+
+ retval = verify_area(VERIFY_WRITE, (struct tm*)arg, sizeof(struct tm));
+ if (retval !=0 )
+ return retval;
+
+ get_rtc_time(&rtc_tm);
+ memcpy_tofs((struct tm*)arg, &rtc_tm, sizeof(struct tm));
+ return 0;
+ }
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ /*
+ * No need for this at the moment. Use /sbin/clock.
+ */
+ return -ENOSYS;
+ }
+ case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
+ {
+ unsigned long hz;
+ int retval;
+
+ retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long));
+ if (retval != 0)
+ return retval;
+
+ save_flags(flags);
+ cli();
+ retval = CMOS_READ(RTC_FREQ_SELECT) & 0x0f;
+ restore_flags(flags);
+ hz = (retval ? (65536/(1<<retval)) : 0);
+ memcpy_tofs((unsigned long*)arg, &hz, sizeof(unsigned long));
+ return 0;
+ }
+ case RTC_IRQP_SET: /* Set periodic IRQ rate. */
+ {
+ int tmp = 0;
+ unsigned char val;
+
+ /*
+ * The max we can do is 8192Hz.
+ */
+ if (arg > 8192)
+ return -EINVAL;
+ /*
+ * We don't really want Joe User generating more
+ * than 64Hz of interrupts on a multi-user machine.
+ */
+ if ((arg >64) && (!suser()))
+ return -EPERM;
+
+ while (arg > (1<<tmp))
+ tmp++;
+
+ /*
+ * Check that the input was really a power of 2.
+ */
+ if ((arg != 0) && (arg != (1<<tmp)))
+ return -EINVAL;
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
+
+ if (arg == 0) {
+ CMOS_WRITE(val, RTC_FREQ_SELECT);
+ restore_flags(flags);
+ return 0;
+ }
+
+ val |= (16 - tmp);
+ CMOS_WRITE(val, RTC_FREQ_SELECT);
+ restore_flags(flags);
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * We enforce only one user at a time here with the open/close.
+ * Also clear the previous interrupt data on an open, and clean
+ * up things on a close.
+ */
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+
+ if(rtc_status & RTC_IS_OPEN)
+ return -EBUSY;
+
+ rtc_status |= RTC_IS_OPEN;
+ rtc_irq_data = 0;
+ return 0;
+}
+
+static void rtc_release(struct inode *inode, struct file *file)
+{
+
+ /*
+ * Turn off all interrupts once the device is no longer
+ * in use, and clear the data.
+ */
+
+ unsigned char tmp;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ tmp = CMOS_READ(RTC_CONTROL);
+ tmp &= ~RTC_PIE;
+ tmp &= ~RTC_AIE;
+ tmp &= ~RTC_UIE;
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
+ restore_flags(flags);
+ rtc_irq_data = 0;
+ rtc_status &= ~RTC_IS_OPEN;
+}
+
+static int rtc_select(struct inode *inode, struct file *file,
+ int sel_type, select_table *wait)
+{
+ if (sel_type == SEL_IN) {
+ if (rtc_irq_data != 0)
+ return 1;
+ select_wait(&rtc_wait, wait);
+ }
+ return 0;
+}
+
+/*
+ * The various file operations we support.
+ */
+
+static struct file_operations rtc_fops = {
+ rtc_lseek,
+ rtc_read,
+ NULL, /* No write */
+ NULL, /* No readdir */
+ rtc_select,
+ rtc_ioctl,
+ NULL, /* No mmap */
+ rtc_open,
+ rtc_release
+};
+
+static struct miscdevice rtc_dev=
+{
+ RTC_MINOR,
+ "rtc",
+ &rtc_fops
+};
+
+int rtc_init(void)
+{
+ printk("Real Time Clock Driver v%s\n", RTC_VERSION);
+ if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
+ {
+ /* Yeah right, seeing as irq 8 doesn't even hit the bus. */
+ printk("rtc: IRQ %d is not free.\n", RTC_IRQ);
+ return -EIO;
+ }
+ misc_register(&rtc_dev);
+ /* Check region ? Naaah! Just snarf it up. */
+ request_region(RTC_IO_BASE, RTC_IO_EXTENT, "rtc");
+ rtc_wait = NULL;
+ return 0;
+}
+
+/*
+ * Info exported via "/proc/rtc".
+ */
+
+int get_rtc_status(char *buf)
+{
+ char *p;
+ struct tm tm;
+ unsigned char freq, batt, ctrl;
+ unsigned long flags;
+
+ save_flags(flags);
+ freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+ batt = CMOS_READ(RTC_VALID) & RTC_VRT;
+ ctrl = CMOS_READ(RTC_CONTROL);
+ restore_flags(flags);
+
+ p = buf;
+ p += sprintf(p, "Real Time Clock Status:\n");
+
+ get_rtc_time(&tm);
+
+ /*
+ * There is no way to tell if the luser has the RTC set for local
+ * 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);
+
+ get_rtc_alm_time(&tm);
+
+ /*
+ * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will
+ * match any value for that particular field. Values that are
+ * greater than a valid time, but less than 0xc0 shouldn't appear.
+ */
+ p += sprintf(p, "\tAlarm set to match: ");
+ if (tm.tm_hour <= 24)
+ p += sprintf(p, "hour=%d, ", tm.tm_hour);
+ else
+ p += sprintf(p, "hour=any, ");
+ if (tm.tm_min <= 59)
+ p += sprintf(p, "min=%d, ", tm.tm_min);
+ else
+ p += sprintf(p, "min=any, ");
+ if (tm.tm_sec <= 59)
+ p += sprintf(p, "sec=%d.\n", tm.tm_sec);
+ else
+ p += sprintf(p, "sec=any.\n");
+
+ p += sprintf(p, "\tMisc. settings: daylight=%s; BCD=%s; 24hr=%s; Sq-Wave=%s.\n",
+ ((ctrl & RTC_DST_EN) ? "yes" : "no" ),
+ ((ctrl & RTC_DM_BINARY) ? "no" : "yes" ),
+ ((ctrl & RTC_24H) ? "yes" : "no" ),
+ ((ctrl & RTC_SQWE) ? "yes" : "no" ));
+
+ p += sprintf(p, "\tInterrupt for: alarm=%s; update=%s; periodic=%s.\n",
+ ((ctrl & RTC_AIE) ? "yes" : "no" ),
+ ((ctrl & RTC_UIE) ? "yes" : "no" ),
+ ((ctrl & RTC_PIE) ? "yes" : "no" ));
+
+ p += sprintf(p, "\tPeriodic interrupt rate set to %dHz.\n",
+ (freq ? (65536/(1<<freq)) : 0));
+
+ p += sprintf(p, "\tRTC reports that CMOS battery is %s.\n",
+ (batt ? "okay" : "dead"));
+
+ return p - buf;
+}
+
+/*
+ * Returns true if a clock update is in progress
+ */
+inline unsigned char rtc_is_updating(void)
+{
+ unsigned long flags;
+ unsigned char uip;
+
+ save_flags(flags);
+ uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
+ restore_flags(flags);
+ return uip;
+}
+
+void get_rtc_time(struct tm *rtc_tm)
+{
+
+ unsigned long flags, uip_watchdog = jiffies;
+ unsigned char ctrl;
+
+ /*
+ * read RTC once any update in progress is done. The update
+ * can take just over 2ms. We wait 20ms. The other alternative
+ * is to poll-wait for the falling edge of RTC_UIP which can
+ * mean a delay of over 1s, which is ugly.
+ */
+
+ if (rtc_is_updating() != 0)
+ while (jiffies - uip_watchdog < 2*HZ/100)
+ barrier();
+
+ /*
+ * Only the values that we read from the RTC are set. We leave
+ * tm_yday and tm_isdst untouched.
+ */
+ 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);
+ ctrl = CMOS_READ(RTC_CONTROL);
+ restore_flags(flags);
+
+ if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ {
+ 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)
+ rtc_tm->tm_year += 100;
+}
+
+void get_rtc_alm_time(struct tm *alm_tm)
+{
+ unsigned long flags;
+ unsigned char ctrl;
+
+ /*
+ * Only the values that we read from the RTC are set. That
+ * means only tm_hour, tm_min, and tm_sec.
+ */
+ save_flags(flags);
+ cli();
+ alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
+ alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM);
+ alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM);
+ ctrl = CMOS_READ(RTC_CONTROL);
+ restore_flags(flags);
+
+ if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ {
+ BCD_TO_BIN(alm_tm->tm_sec);
+ BCD_TO_BIN(alm_tm->tm_min);
+ BCD_TO_BIN(alm_tm->tm_hour);
+ }
+}
+
+/*
+ * Used to disable/enable interrupts for any one of UIE, AIE, PIE.
+ * Rumour has it that if you frob the interrupt enable/disable
+ * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to
+ * ensure you actually start getting interrupts. Probably for
+ * compatibility with older/broken chipset RTC implementations.
+ * We also clear out any old irq data after an ioctl() that
+ * meddles the interrupt enable/disable bits.
+ */
+inline void mask_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_CONTROL);
+ val &= ~bit;
+ CMOS_WRITE(val, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
+ restore_flags(flags);
+ rtc_irq_data = 0;
+}
+
+inline void set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_CONTROL);
+ val |= bit;
+ CMOS_WRITE(val, RTC_CONTROL);
+ CMOS_READ(RTC_INTR_FLAGS);
+ restore_flags(flags);
+ rtc_irq_data = 0;
+}
+
diff -ur /mnt/linux-1381/fs/proc/array.c linux/fs/proc/array.c
--- /mnt/linux-1381/fs/proc/array.c Sun Mar 31 12:05:09 1996
+++ linux/fs/proc/array.c Sun Mar 31 11:52:44 1996
@@ -962,6 +962,7 @@
extern int get_cpuinfo(char *);
extern int get_pci_list(char*);
extern int get_md_status (char *);
+extern int get_rtc_status (char *);
#ifdef __SMP_PROF__
extern int get_smp_prof_list(char *);
#endif
@@ -1036,6 +1037,10 @@

case PROC_MTAB:
return get_filesystem_info( page );
+#ifdef CONFIG_RTC
+ case PROC_RTC:
+ return get_rtc_status(page);
+#endif
}
return -EBADF;
}
diff -ur /mnt/linux-1381/fs/proc/root.c linux/fs/proc/root.c
--- /mnt/linux-1381/fs/proc/root.c Thu Feb 15 23:37:11 1996
+++ linux/fs/proc/root.c Sun Mar 31 11:52:44 1996
@@ -357,6 +357,12 @@
S_IFREG | S_IRUGO, 1, 0, 0,
});
#endif
+#ifdef CONFIG_RTC
+ proc_register(&proc_root, &(struct proc_dir_entry) {
+ PROC_RTC, 3, "rtc",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ });
+#endif
proc_register(&proc_root, &(struct proc_dir_entry) {
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
diff -ur /mnt/linux-1381/include/linux/mc146818rtc.h linux/include/linux/mc146818rtc.h
--- /mnt/linux-1381/include/linux/mc146818rtc.h Mon Oct 2 04:35:53 1995
+++ linux/include/linux/mc146818rtc.h Sun Mar 31 11:52:45 1996
@@ -106,4 +106,22 @@
#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
#endif

+/*
+ * ioctl calls that are permitted to the /dev/rtc interface, if
+ * CONFIG_RTC was enabled.
+ */
+
+#define RTC_AIE_ON 0x01 /* Alarm int. enable on */
+#define RTC_AIE_OFF 0x02 /* ... off */
+#define RTC_UIE_ON 0x03 /* Update int. enable on */
+#define RTC_UIE_OFF 0x04 /* ... off */
+#define RTC_PIE_ON 0x05 /* Periodic int. enable on */
+#define RTC_PIE_OFF 0x06 /* ... off */
+#define RTC_ALM_SET 0x07 /* Set alarm (struct tm) */
+#define RTC_ALM_READ 0x08 /* Read alarm (struct tm) */
+#define RTC_RD_TIME 0x09 /* Read RTC time (struct tm) */
+#define RTC_SET_TIME 0x0a /* Set time of RTC (not used) */
+#define RTC_IRQP_READ 0x0b /* Read periodic IRQ rate (Hz) */
+#define RTC_IRQP_SET 0x0c /* Set periodic IRQ rate (Hz) */
+
#endif /* _MC146818RTC_H */
diff -ur /mnt/linux-1381/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- /mnt/linux-1381/include/linux/proc_fs.h Sun Mar 31 12:05:11 1996
+++ linux/include/linux/proc_fs.h Sun Mar 31 11:52:45 1996
@@ -42,7 +42,8 @@
PROC_CMDLINE,
PROC_SYS,
PROC_MTAB,
- PROC_MD
+ PROC_MD,
+ PROC_RTC
};

enum pid_directory_inos {

\
 
 \ /
  Last update: 2005-03-22 13:36    [W:0.066 / U:0.292 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site