summaryrefslogtreecommitdiffstats
path: root/clockB/directio.c
diff options
context:
space:
mode:
Diffstat (limited to 'clockB/directio.c')
-rw-r--r--clockB/directio.c727
1 files changed, 727 insertions, 0 deletions
diff --git a/clockB/directio.c b/clockB/directio.c
new file mode 100644
index 000000000..d3a77eb19
--- /dev/null
+++ b/clockB/directio.c
@@ -0,0 +1,727 @@
+/**************************************************************************
+
+ This is a component of the hwclock program.
+
+ This file contains the code for accessing the hardware clock via
+ direct I/O (kernel-style input and output operations) as opposed
+ to via a device driver.
+
+
+ MAINTENANCE NOTES
+
+ Here is some information on how the Hardware Clock works, from
+ unknown source and authority. In theory, the specification for this
+ stuff is the specification of Motorola's MC146818A clock chip, used
+ in the early ISA machines. Subsequent machines should have copied
+ its function exactly. In reality, though, the copies are inexact
+ and the MC146818A itself may fail to implement its specifications,
+ and we have just have to work with whatever is there (actually,
+ anything that Windows works with, because that's what determines
+ whether broken hardware has to be fixed!).
+
+ i386 CMOS starts out with 14 bytes clock data
+ alpha has something similar, but with details
+ depending on the machine type.
+
+ byte 0: seconds (0-59)
+ byte 2: minutes (0-59)
+ byte 4: hours (0-23 in 24hr mode,
+ 1-12 in 12hr mode, with high bit unset/set if am/pm)
+ byte 6: weekday (1-7, Sunday=1)
+ byte 7: day of the month (1-31)
+ byte 8: month (1-12)
+ byte 9: year (0-99)
+
+
+ Numbers are stored in BCD/binary if bit 2 of byte 11 is unset/set
+ The clock is in 12hr/24hr mode if bit 1 of byte 11 is unset/set
+ The clock is undefined (being updated) if bit 7 of byte 10 is set.
+ The clock is frozen (to be updated) by setting bit 7 of byte 11
+ Bit 7 of byte 14 indicates whether the CMOS clock is reliable:
+ it is 1 if RTC power has been good since this bit was last read;
+ it is 0 when the battery is dead and system power has been off.
+
+ The century situation is messy:
+
+ Usually byte 50 (0x32) gives the century (in BCD, so 0x19 or 0x20 in
+ pure binary), but IBM PS/2 has (part of) a checksum there and uses
+ byte 55 (0x37). Sometimes byte 127 (0x7f) or Bank 1, byte 0x48
+ gives the century. The original RTC will not access any century
+ byte; some modern versions will. If a modern RTC or BIOS increments
+ the century byte it may go from 0x19 to 0x20, but in some buggy
+ cases 0x1a is produced.
+
+ CMOS byte 10 (clock status register A) has 3 bitfields:
+ bit 7: 1 if data invalid, update in progress (read-only bit)
+ (this is raised 224 us before the actual update starts)
+ 6-4 select base frequency
+ 010: 32768 Hz time base (default)
+ 111: reset
+ all other combinations are manufacturer-dependent
+ (e.g.: DS1287: 010 = start oscillator, anything else = stop)
+ 3-0 rate selection bits for interrupt
+ 0000 none
+ 0001, 0010 give same frequency as 1000, 1001
+ 0011 122 microseconds (minimum, 8192 Hz)
+ .... each increase by 1 halves the frequency, doubles the period
+ 1111 500 milliseconds (maximum, 2 Hz)
+ 0110 976.562 microseconds (default 1024 Hz)
+
+ Avoid setting the RTC clock within 2 seconds of the day rollover
+ that starts a new month or enters daylight saving time.
+
+****************************************************************************/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#if defined(__i386__) || defined(__alpha__)
+#include <asm/io.h> /* for inb, outb */
+#else
+void outb(int a, int b){}
+int inb(int c){ return 0; }
+#endif
+
+#include "hwclock.h"
+
+#define BCD_TO_BIN(val) (((val)&0x0f) + ((val)>>4)*10)
+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+
+
+/*----------------------------------------------------------------------------
+ ATOMIC_TOP and ATOMIC_BOTTOM are wierd macros that help us to do
+ atomic operations when we do ugly low level I/O.
+
+ You put ATOMIC_TOP above some code and ATOMIC_BOTTOM below it and
+ it makes sure all the enclosed code executes without interruption
+ by some other process (and, in some cases, even the kernel).
+
+ These work fundamentally differently depending on the machine
+ architecture. In the case of a x86, it simply turns interrupts off
+ at the top and turns them back on at the bottom.
+
+ For Alpha, we can't mess with interrupts (we shouldn't for x86
+ either, but at least it tends to work!), so instead we start a loop
+ at the top and close it at the bottom. This loop repeats the
+ enclosed code until the upper 32 bits of the cycle counter are the
+ same before and after. That means there was no context change
+ while the enclosed code was executing.
+
+ For other architectures, we do nothing, and the atomicity is only
+ feigned.
+
+-----------------------------------------------------------------------------*/
+
+#if defined(__i386__)
+#define ATOMIC_TOP \
+ { \
+ const bool interrupts_were_enabled = interrupts_enabled; \
+ __asm__ volatile ("cli"); \
+ interrupts_enabled = FALSE;
+
+#define ATOMIC_BOTTOM \
+ if (interrupts_were_enabled) { \
+ __asm__ volatile ("sti"); \
+ interrupts_enabled = TRUE; \
+ } \
+ }
+#elif defined(__alpha__)
+#define ATOMIC_TOP \
+ { \
+ unsigned long ts1, ts2, n; \
+ n = 0; \
+ do { \
+ asm volatile ("rpcc %0" : "r="(ts1));
+
+#define ATOMIC_BOTTOM \
+ asm volatile ("rpcc %0" : "r="(ts2)); \
+ n++; \
+ } while ((ts1 ^ ts2) >> 32 != 0); \
+ }
+#else
+#define ATOMIC_BOTTOM
+#define ATOMIC_TOP
+#endif
+
+
+#if defined(__i386__) || defined(__alpha__)
+/* The following are just constants. Oddly, this program will not
+ compile if the inb() and outb() functions use something even
+ slightly different from these variables. This is probably at least
+ partially related to the fact that __builtin_constant_p() doesn't
+ work (is never true) in an inline function. See comment to this
+ effect in asm/io.h.
+*/
+static unsigned short clock_ctl_addr = 0x70;
+static unsigned short clock_data_addr = 0x71;
+#endif
+
+
+static bool interrupts_enabled;
+ /* Interrupts are enabled as normal. We, unfortunately, turn interrupts
+ on the machine off in some places where we do the direct ISA accesses
+ to the Hardware Clock. It is in extremely poor form for a user space
+ program to do this, but that's the price we have to pay to run on an
+ ISA machine without the rtc driver in the kernel.
+
+ Code which turns interrupts off uses this value to determine if they
+ need to be turned back on.
+ */
+
+
+void
+assume_interrupts_enabled(void) {
+ interrupts_enabled = TRUE;
+}
+
+
+
+static int
+i386_iopl(const int level) {
+/*----------------------------------------------------------------------------
+ When compiled for an Intel target, this is just the iopl() kernel call.
+ When compiled for any other target, this is a dummy function.
+
+ We do it this way in order to keep the conditional compilation stuff
+ out of the way so it doesn't mess up readability of the code.
+-----------------------------------------------------------------------------*/
+#ifdef __i386__
+ extern int iopl(int level);
+ return iopl(level);
+#else
+ return -1;
+#endif
+}
+
+
+
+bool
+uf_bit_needed(const bool user_wants_uf) {
+/*----------------------------------------------------------------------------
+ Return true iff the UIP bit doesn't work on this hardware clock, so
+ we will need to use the UF bit to synchronize with the clock (if in
+ fact we synchronize using direct I/O to the clock).
+
+ To wit, we need to use the UF bit on a DEC Alpha PC164/LX164/SX164.
+ Or, of course, if the user told us to.
+-----------------------------------------------------------------------------*/
+ bool retval;
+
+ if (user_wants_uf) retval = TRUE;
+ else {
+ if (alpha_machine && (
+ is_in_cpuinfo("system variation", "PC164") ||
+ is_in_cpuinfo("system variation", "LX164") ||
+ is_in_cpuinfo("system variation", "SX164")))
+ retval = TRUE;
+ else retval = FALSE;
+ }
+ if (debug && retval)
+ printf("We will be using the UF bit instead of the usual "
+ "UIP bit to synchronize with the clock, as required on "
+ "certain models of DEC Alpha.\n");
+
+ return retval;
+}
+
+
+
+int
+zero_year(const bool arc_opt, const bool srm_opt) {
+/*----------------------------------------------------------------------------
+ Return the year of the century (e.g. 0) to which a zero value in
+ the year register of the hardware clock applies (or at least what
+ we are to assume -- nobody can say for sure!)
+
+ 'arc_opt' and 'srm_opt' are the true iff the user specified the
+ corresponding invocation option to instruct us that the machine is an
+ Alpha with ARC or SRM console time.
+
+ A note about hardware clocks:
+
+ ISA machines are simple: the year register is a year-of-century
+ register, so the zero year is zero. On Alphas, we may see 1980 or
+ 1952 (Digital Unix?) or 1958 (ALPHA_PRE_V1_2_SRM_CONSOLE)
+-----------------------------------------------------------------------------*/
+ int retval; /* our return value */
+
+ if (arc_opt || srm_opt) {
+ /* User is telling us what epoch his machine uses. Believe it. */
+ if (arc_opt) retval = 0;
+ else retval = 0;
+ } else {
+ unsigned long kernel_epoch;
+ char *reason; /* malloc'ed */
+
+ get_epoch(&kernel_epoch, &reason);
+ if (reason == NULL) retval = kernel_epoch;
+ else {
+ /* OK, the user doesn't know and the kernel doesn't know;
+ figure it out from the machine model
+ */
+ free(reason); /* Don't care about kernel's excuses */
+ /* See whether we are dealing with SRM or MILO, as they have
+ different "epoch" ideas. */
+ if (is_in_cpuinfo("system serial number", "MILO")) {
+ if (debug) printf("booted from MILO\n");
+ /* See whether we are dealing with a RUFFIAN aka UX, as they
+ have REALLY different TOY (TimeOfYear) format: BCD, and not
+ an ARC-style epoch. BCD is detected dynamically, but we
+ must NOT adjust like ARC.
+ */
+ if (is_in_cpuinfo("system type", "Ruffian")) {
+ if (debug) printf("Ruffian BCD clock\n");
+ retval = 0;
+ } else {
+ if (debug) printf("Not Ruffian BCD clock\n");
+ retval = 80;
+ }
+ } else {
+ if (debug) printf("Not booted from MILO\n");
+ retval = 0;
+ }
+ }
+ }
+ return retval;
+}
+
+
+
+static inline unsigned char
+hclock_read(const unsigned char reg, const int dev_port) {
+/*---------------------------------------------------------------------------
+ Relative byte 'reg' of the Hardware Clock value.
+
+ Get this with direct CPU I/O instructions. If 'dev_port' is not -1,
+ use the /dev/port device driver (via the 'dev_port' file descriptor)
+ to do this I/O. Otherwise, use the kernel's inb()/outb() facility.
+
+ On a system without the inb()/outb() facility, if 'dev_port' is -1,
+ just return 0.
+
+ Results undefined if 'reg' is out of range.
+---------------------------------------------------------------------------*/
+ unsigned char ret;
+
+ ATOMIC_TOP
+ if (dev_port >= 0) {
+ const unsigned char v = reg | 0x80;
+ lseek(dev_port, 0x170, 0);
+ write(dev_port, &v, 1);
+ lseek(dev_port, 0x171, 0);
+ read(dev_port, &ret, 1);
+ } else {
+#if defined(__i386__) || defined(__alpha__)
+ /* & 0x7f ensures that we are not disabling NMI while we read.
+ Setting on Bit 7 here would disable NMI
+
+ Various docs suggest that one should disable NMI while
+ reading/writing CMOS data, and enable it again afterwards.
+ This would yield the sequence
+ outb (reg | 0x80, 0x70);
+ val = inb(0x71);
+ outb (0x0d, 0x70); // 0x0d: random read-only location
+ Other docs state that "any write to 0x70 should be followed
+ by an action to 0x71 or the RTC wil be left in an unknown state".
+ Most docs say that it doesn't matter at all what one does.
+ */
+ outb(reg & 0x7f, clock_ctl_addr);
+ ret = inb(clock_data_addr);
+#else
+ ret = 0;
+#endif
+ }
+ ATOMIC_BOTTOM
+ return ret;
+}
+
+
+
+static inline void
+hclock_write(unsigned char reg, unsigned char val, const int dev_port) {
+/*----------------------------------------------------------------------------
+ Set relative byte 'reg' of the Hardware Clock value to 'val'.
+ Do this with the kernel's outb() function if 'dev_port' is -1, but
+ if not, use the /dev/port device (via the 'dev_port' file descriptor),
+ which is almost the same thing.
+
+ On a non-ISA, non-Alpha machine, if 'dev_port' is -1, do nothing.
+----------------------------------------------------------------------------*/
+ if (dev_port >= 0) {
+ unsigned char v;
+ v = reg | 0x80;
+ lseek(dev_port, 0x170, 0);
+ write(dev_port, &v, 1);
+ v = (val & 0xff);
+ lseek(dev_port, 0x171, 0);
+ write(dev_port, &v, 1);
+ } else {
+#if defined(__i386__) || defined(__alpha__)
+ /* & 0x7f ensures that we are not disabling NMI while we read.
+ Setting on Bit 7 here would disable NMI
+ */
+ outb(reg & 0x7f, clock_ctl_addr);
+ outb(val, clock_data_addr);
+#endif
+ }
+}
+
+
+
+static inline int
+hardware_clock_busy(const int dev_port, const bool use_uf_bit) {
+/*----------------------------------------------------------------------------
+ Return whether the hardware clock is in the middle of an update
+ (which happens once each second).
+
+ Use the clock's UIP bit (bit 7 of Control Register A) to tell
+ unless 'use_uf_bit' is true, in which case use the UF bit (bit 4 of
+ Control Register C).
+-----------------------------------------------------------------------------*/
+ return
+ use_uf_bit ? (hclock_read(12, dev_port) & 0x10) :
+ (hclock_read(10, dev_port) & 0x80);
+}
+
+
+
+void
+synchronize_to_clock_tick_ISA(int *retcode_p, const int dev_port,
+ const bool use_uf_bit) {
+/*----------------------------------------------------------------------------
+ Same as synchronize_to_clock_tick(), but just for ISA.
+-----------------------------------------------------------------------------*/
+ int i; /* local loop index */
+
+ /* Wait for rise. Should be within a second, but in case something
+ weird happens, we have a limit on this loop to reduce the impact
+ of this failure.
+ */
+ for (i = 0;
+ !hardware_clock_busy(dev_port, use_uf_bit) && (i < 10000000);
+ i++);
+ if (i >= 10000000) *retcode_p = 1;
+ else {
+ /* Wait for fall. Should be within 2.228 ms. */
+ for (i = 0;
+ hardware_clock_busy(dev_port, use_uf_bit) && (i < 1000000);
+ i++);
+ if (i >= 10000000) *retcode_p = 1;
+ else *retcode_p = 0;
+ }
+}
+
+
+
+void
+read_hardware_clock_isa(struct tm *tm, const int dev_port,
+ int hc_zero_year) {
+/*----------------------------------------------------------------------------
+ Read the hardware clock and return the current time via <tm> argument.
+ Assume we have an ISA machine and read the clock directly with CPU I/O
+ instructions. If 'dev_port' isn't -1, use the /dev/port facility to
+ do this I/O. Otherwise, use the kernel's inb()/outb() service.
+
+ This function is not totally reliable. It takes a finite and
+ unpredictable amount of time to execute the code below. During that
+ time, the clock may change and we may even read an invalid value in
+ the middle of an update. We do a few checks to minimize this
+ possibility, but only the kernel can actually read the clock
+ properly, since it can execute code in a short and predictable
+ amount of time (by turning off interrupts).
+
+ In practice, the chance of this function returning the wrong time is
+ extremely remote.
+
+-----------------------------------------------------------------------------*/
+ bool got_time;
+ /* We've successfully read a time from the Hardware Clock */
+ int attempts;
+ /* Number of times we've tried to read the clock. This only
+ matters because we will give up (and proceed with garbage in
+ variables) rather than hang if something is broken and we are
+ never able to read the clock
+ */
+ int hclock_sec = 0, hclock_min = 0, hclock_hour = 0, hclock_wday = 0,
+ hclock_mon = 0, hclock_mday = 0, hclock_year = 0;
+ /* The values we got from the Hardware Clock's registers, assuming
+ they are in pure binary.
+ */
+
+ int status = 0; /* Hardware Clock status register, as if pure binary */
+ int adjusted_year;
+ int ampmhour;
+ int pmbit;
+
+ got_time = FALSE;
+ attempts = 0; /* initial value */
+ while (!got_time && attempts++ < 1000000) {
+ /* Bit 7 of Byte 10 of the Hardware Clock value is the Update In Progress
+ (UIP) bit, which is on while and 244 uS before the Hardware Clock
+ updates itself. It updates the counters individually, so reading
+ them during an update would produce garbage. The update takes 2mS,
+ so we could be spinning here that long waiting for this bit to turn
+ off.
+
+ Furthermore, it is pathologically possible for us to be in this
+ code so long that even if the UIP bit is not on at first, the
+ clock has changed while we were running. We check for that too,
+ and if it happens, we start over.
+ */
+
+ if ((hclock_read(10, dev_port) & 0x80) == 0) {
+ /* No clock update in progress, go ahead and read */
+
+ status = hclock_read(11, dev_port);
+
+ hclock_sec = hclock_read(0, dev_port);
+ hclock_min = hclock_read(2, dev_port);
+ hclock_hour = hclock_read(4, dev_port);
+ hclock_wday = hclock_read(6, dev_port);
+ hclock_mday = hclock_read(7, dev_port);
+ hclock_mon = hclock_read(8, dev_port);
+ hclock_year = hclock_read(9, dev_port);
+ /* Unless the clock changed while we were reading, consider this
+ a good clock read .
+ */
+ if (hclock_sec == hclock_read(0, dev_port)) got_time = TRUE;
+ /* Yes, in theory we could have been running for 60 seconds and
+ the above test wouldn't work!
+ */
+ }
+ }
+
+ if (!(status & 0x04)) {
+ /* The hardware clock is in BCD mode. This is normal. */
+ tm->tm_sec = BCD_TO_BIN(hclock_sec);
+ tm->tm_min = BCD_TO_BIN(hclock_min);
+ ampmhour = BCD_TO_BIN(hclock_hour & 0x7f);
+ pmbit = hclock_hour & 0x80;
+ tm->tm_wday = BCD_TO_BIN(hclock_wday) - 1; /* Used to be 3. Why?? */
+ tm->tm_mday = BCD_TO_BIN(hclock_mday);
+ tm->tm_mon = BCD_TO_BIN(hclock_mon) - 1;
+ adjusted_year = BCD_TO_BIN(hclock_year);
+ } else {
+ /* The hardware clock registers are in pure binary format. */
+ tm->tm_sec = hclock_sec;
+ tm->tm_min = hclock_min;
+ ampmhour = hclock_hour & 0x7f;
+ pmbit = hclock_hour & 0x80;
+ tm->tm_wday = hclock_wday - 1; /* Used to be 3. Why?? */
+ tm->tm_mday = hclock_mday;
+ tm->tm_mon = hclock_mon - 1;
+ adjusted_year = hclock_year;
+ }
+
+ if (!(status & 0x02)) {
+ /* Clock is in 12 hour (am/pm) mode. This is unusual. */
+ if (pmbit == 0x80) {
+ if (ampmhour == 12) tm->tm_hour = 12;
+ else tm->tm_hour = 12 + ampmhour;
+ } else {
+ if (ampmhour ==12) tm->tm_hour = 0;
+ else tm->tm_hour = ampmhour;
+ }
+ } else {
+ /* Clock is in 24 hour mode. This is normal. */
+ tm->tm_hour = ampmhour;
+ }
+ /* We don't use the century byte (Byte 50) of the Hardware Clock.
+ Here's why: It didn't exist in the original ISA specification,
+ so old machines don't have it, and even some new ones don't.
+ Some machines, including the IBM Valuepoint 6387-X93, use that
+ byte for something else. Some machines have the century in
+ Byte 55.
+
+ Furthermore, the Linux standard time data structure doesn't
+ allow for times beyond about 2037 and no Linux systems were
+ running before 1937. Therefore, all the century byte could tell
+ us is that the clock is wrong or this whole program is obsolete!
+
+ So we just say if the year of century is less than 37, it's the
+ 2000's, otherwise it's the 1900's.
+
+ Alpha machines (some, anyway) don't have this ambiguity
+ because they do not have a year-of-century register. We
+ pretend they do anyway, for simplicity and to avoid
+ recognizing times that can't be represented in Linux standard
+ time. So even though we already have enough information to
+ know that the clock says 2050, we will render it as 1950.
+ */
+ {
+ const int year_of_century = (adjusted_year + hc_zero_year) % 100;
+ if (year_of_century >= 37) tm->tm_year = year_of_century;
+ else tm->tm_year = year_of_century + 100;
+ }
+ tm->tm_isdst = -1; /* don't know whether it's daylight */
+}
+
+
+
+void
+set_hardware_clock_isa(const struct tm new_tm,
+ const int hc_zero_year,
+ const int dev_port,
+ const bool testing) {
+/*----------------------------------------------------------------------------
+ Set the Hardware Clock to the time (in broken down format)
+ new_tm. Use direct I/O instructions to what we assume is
+ an ISA Hardware Clock.
+
+ Iff 'dev_port' is -1, use the kernel inb()/outb() service, otherwise
+ use the /dev/port device (via file descriptor 'dev_port')
+ to do those I/O instructions.
+----------------------------------------------------------------------------*/
+ unsigned char save_control, save_freq_select;
+
+ if (testing)
+ printf("Not setting Hardware Clock because running in test mode.\n");
+ else {
+ int ampmhour;
+ /* The hour number that goes into the hardware clock, taking into
+ consideration whether the clock is in 12 or 24 hour mode
+ */
+ int pmbit;
+ /* Value to OR into the hour register as the am/pm bit */
+ const int adjusted_year =
+ (new_tm.tm_year - hc_zero_year)%100;
+ /* The number that goes in the hardware clock's year register */
+
+ int hclock_sec, hclock_min, hclock_hour, hclock_wday, hclock_mon,
+ hclock_mday, hclock_year;
+ /* The values we will put, in pure binary, in the Hardware Clock's
+ registers.
+ */
+
+ ATOMIC_TOP
+
+ save_control = hclock_read(11, dev_port);
+ /* tell the clock it's being set */
+ hclock_write(11, (save_control | 0x80), dev_port);
+ save_freq_select = hclock_read(10, dev_port);
+ /* stop and reset prescaler */
+ hclock_write (10, (save_freq_select | 0x70), dev_port);
+
+
+ if (!(save_control & 0x02)) {
+ /* Clock is in 12 hour (am/pm) mode. This is unusual. */
+ if (new_tm.tm_hour == 0) {
+ ampmhour = 12;
+ pmbit = 0x00;
+ } else if (new_tm.tm_hour < 12) {
+ ampmhour = new_tm.tm_hour;
+ pmbit = 0x00;
+ } else if (new_tm.tm_hour == 12) {
+ ampmhour = 12;
+ pmbit = 0x80;
+ } else {
+ ampmhour = new_tm.tm_hour - 12;
+ pmbit = 0x80;
+ }
+ } else {
+ /* Clock is in 24 hour mode. This is normal. */
+ ampmhour = new_tm.tm_hour;
+ pmbit = 0x00;
+ }
+
+
+ if (!(save_control & 0x04)) {
+ /* Clock's registers are in BCD. This is normal. */
+ hclock_sec = BIN_TO_BCD(new_tm.tm_sec);
+ hclock_min = BIN_TO_BCD(new_tm.tm_min);
+ hclock_hour = pmbit | BIN_TO_BCD(ampmhour);
+ hclock_wday = BIN_TO_BCD(new_tm.tm_wday + 1); /* Used to be 3. Why??*/
+ hclock_mday = BIN_TO_BCD(new_tm.tm_mday);
+ hclock_mon = BIN_TO_BCD(new_tm.tm_mon + 1);
+ hclock_year = BIN_TO_BCD(adjusted_year);
+ } else {
+ /* Clock's registers are in pure binary. This is unusual. */
+ hclock_sec = new_tm.tm_sec;
+ hclock_min = new_tm.tm_min;
+ hclock_hour = pmbit | ampmhour;
+ hclock_wday = new_tm.tm_wday + 1; /* Used to be 3. Why?? */
+ hclock_mday = new_tm.tm_mday;
+ hclock_mon = new_tm.tm_mon + 1;
+ hclock_year = adjusted_year;
+ }
+
+ hclock_write(0, hclock_sec, dev_port);
+ hclock_write(2, hclock_min, dev_port);
+ hclock_write(4, hclock_hour, dev_port);
+ hclock_write(6, hclock_wday, dev_port);
+ hclock_write(7, hclock_mday, dev_port);
+ hclock_write(8, hclock_mon, dev_port);
+ hclock_write(9, hclock_year, dev_port);
+
+ /* We don't set the century byte (usually Byte 50) because it isn't
+ always there. (see further comments in read_hardware_clock_isa).
+ In previous releases, we did.
+ */
+
+ /* The kernel sources, linux/arch/i386/kernel/time.c, have the
+ following comment:
+
+ The following flags have to be released exactly in this order,
+ otherwise the DS12887 (popular MC146818A clone with integrated
+ battery and quartz) will not reset the oscillator and will not
+ update precisely 500 ms later. You won't find this mentioned
+ in the Dallas Semiconductor data sheets, but who believes data
+ sheets anyway ... -- Markus Kuhn
+
+ Hence, they will also be done in this order here.
+ faith@cs.unc.edu, Thu Nov 9 08:26:37 1995
+ */
+
+ hclock_write (11, save_control, dev_port);
+ hclock_write (10, save_freq_select, dev_port);
+
+ ATOMIC_BOTTOM
+ }
+}
+
+
+
+void
+get_inb_outb_privilege(const enum clock_access_method clock_access,
+ bool * const no_auth_p) {
+
+ if (clock_access == ISA) {
+ const int rc = i386_iopl(3);
+ if (rc != 0) {
+ fprintf(stderr, MYNAME " is unable to get I/O port access. "
+ "I.e. iopl(3) returned nonzero return code %d.\n"
+ "This is often because the program isn't running "
+ "with superuser privilege, which it needs.\n",
+ rc);
+ *no_auth_p = TRUE;
+ } else *no_auth_p = FALSE;
+ } else *no_auth_p = FALSE;
+}
+
+
+
+void
+get_dev_port_access(const enum clock_access_method clock_access,
+ int * dev_port_p) {
+
+ if (clock_access == DEV_PORT) {
+ /* Get the /dev/port file open */
+ *dev_port_p = open("/dev/port", O_RDWR);
+ if (*dev_port_p < 0) {
+ fprintf(stderr, MYNAME "is unable to open the /dev/port file. "
+ "I.e. open() of the file failed with errno = %s (%d).\n"
+ "Run with the --debug option and check documentation "
+ "to find out why we are trying "
+ "to use /dev/port instead of some other means to access "
+ "the Hardware Clock.",
+ strerror(errno), errno);
+ }
+ } else *dev_port_p = 0;
+}
+
+
+