/**************************************************************************

  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 <asm/io.h>
#include <errno.h>
#include <string.h>

#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



/* 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;


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.
-----------------------------------------------------------------------------*/
#if defined(__i386__) || defined(__alpha__)
  extern int iopl(const 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;
}



