On Sunday, February 3, 2008 at 23:44:18 +0100, Alain Guibert wrote:
> -1) Rewrite busy_wait() to make it work in whatever using_dev_rtc
> mode, decouple cmos_fd for rtc and port, and fix some other minor
> probs.
The attached patch implements this proposal. Klaus: Could you please
check if it fixes your problem? James: If it works, you can probably
commit this solution (1) patch. Any eventual future patch implementing
alternative solution (3), busywait_second_change(), would be done above
this one.
Alain.
move part of cmos_init() to new cmos_init_directisa()
new global var port_fd for /dev/port
rename busy_wait() to busywait_uip_fall()
make it work independant of selected access mode
--verbose shows what we are waiting for clock tick: interrupt or UIP
Reorganize cmos_read_time() to properly integrate the fallback case for
when /dev/rtc has no interrupts. Make this fallback actually work.
fixes partially(?) Debian bug #460065 as by proposed solution #1
Signed-off-by: Alain Guibert <[EMAIL PROTECTED]>
diff -prud adjtimex-1.23.orig/adjtimex.c adjtimex-1.23/adjtimex.c
--- adjtimex-1.23.orig/adjtimex.c Tue Feb 5 11:42:08 2008
+++ adjtimex-1.23/adjtimex.c Tue Feb 5 16:53:04 2008
@@ -58,6 +58,9 @@ static unsigned long epoch = 1900; /* ye
#define SECONDSPERDAY 86400
#define BUFLEN 128
+#ifndef USE_INLINE_ASM_IO
+static int port_fd = -1; /* used to access the RTC via /dev/port I/O */
+#endif
static int cmos_fd = -1;
static int using_dev_rtc = -1; /* 0 = not using /dev/rtc
1 = using /dev/rtc
@@ -149,9 +152,10 @@ static inline void outb (short port, cha
static inline void outb (short port, char val);
static inline unsigned char inb (short port);
static void cmos_init ();
+static void cmos_init_directisa ();
static inline int cmos_read_bcd (int addr);
static void cmos_read_time (time_t *cmos_timep, double *sysp);
-static void busy_wait(struct timeval *timestamp);
+static void busywait_uip_fall(struct timeval *timestamp);
static void compare(void);
static void failntpdate();
static void reset_time_status(void);
@@ -514,8 +518,8 @@ outb (short port, char val)
__asm__ volatile ("out%B0 %0,%1"::"a" (val), "d" (port));
#else
- lseek (cmos_fd, port, 0);
- write (cmos_fd, &val, 1);
+ lseek (port_fd, port, 0);
+ write (port_fd, &val, 1);
#endif
}
@@ -528,15 +532,18 @@ inb (short port)
__asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d" (port));
#else
- lseek (cmos_fd, port, 0);
- read (cmos_fd, &ret, 1);
+ lseek (port_fd, port, 0);
+ read (port_fd, &ret, 1);
#endif
return ret;
}
-/* set the global variable cmos_fd to a file descriptor for the CMOS
- clock */
-static
+/*
+ * Main initialisation of CMOS clock access methods, for all modes.
+ * Set the global variable cmos_fd to a file descriptor for /dev/rtc.
+ * Failing that, select and initialize direct I/O ports mode.
+ */
+static
void cmos_init ()
{
if (using_dev_rtc < 0)
@@ -556,27 +563,46 @@ void cmos_init ()
else if (using_dev_rtc > 0)
return;
- if (verbose)
- fprintf (stdout, "using /dev/port I/O\n");
+ /* otherwise do direct I/O */
+ cmos_init_directisa();
+}
+/*
+ * Initialise CMOS clock access method, only for direct I/O ports mode.
+ * Set the global variable port_fd to a file descriptor for /dev/port.
+ * This function can safely be called repeatedly (does nothing the
+ * second and following times).
+ */
+static
+void cmos_init_directisa ()
+{
#ifdef USE_INLINE_ASM_IO
+ if (verbose)
+ fprintf (stdout, "using I/O ports\n");
+
if (ioperm (0x70, 2, 1))
{
fprintf (stderr, "clock: unable to get I/O port access\n");
exit (1);
}
#else
- if (cmos_fd < 0)
- cmos_fd = open ("/dev/port", O_RDWR);
- if (cmos_fd < 0)
+ if (port_fd >= 0) /* already initialised */
+ return;
+
+ if (verbose)
+ fprintf (stdout, "using /dev/port I/O\n");
+
+ if (port_fd < 0)
+ port_fd = open ("/dev/port", O_RDWR);
+ if (port_fd < 0)
{
perror ("unable to open /dev/port read/write : ");
exit (1);
}
- if(verbose)
+ if (verbose)
fprintf (stdout, "opened /dev/port for reading\n");
- if (lseek (cmos_fd, 0x70, 0) < 0 || lseek (cmos_fd, 0x71, 0) < 0)
+ if (lseek (port_fd, 0x70, 0) < 0 || lseek (port_fd, 0x71, 0) < 0)
{
perror ("unable to seek port 0x70 in /dev/port : ");
exit (1);
@@ -607,54 +633,60 @@ cmos_read_time (time_t *cmos_timep, doub
time_t cmos_time;
struct timeval now;
- if (using_dev_rtc > 0)
+ if (using_dev_rtc > 0) /* access the CMOS clock thru /dev/rtc */
{
ioctl (cmos_fd, RTC_PIE_OFF, NULL); /* disable periodic interrupts */
rc = ioctl (cmos_fd, RTC_UIE_ON, NULL); /* enable update
complete interrupts */
- if (rc == -1)
+ if (rc == -1) /* busywait for update-in-progress fall */
{
if (verbose)
fprintf(stdout,
"/dev/rtc doesn't allow user access to update interrupts\n"
" - using busy wait instead\n");
- busy_wait(&now);
+ busywait_uip_fall(&now);
}
- {
- unsigned long interrupt_info;
- int type, count;
-
- do {
- rc = read(cmos_fd, &interrupt_info, sizeof(interrupt_info));
- gettimeofday(&now, NULL);
+ else /* wait for update-ended interrupt */
+ {
+ unsigned long interrupt_info;
+ int type, count;
- if (rc == -1)
- {
- perror("read() from /dev/rtc to wait for clock tick failed");
- exit(1);
- }
- type = (int)(interrupt_info & 0xff);
- count = (int)(interrupt_info >> 8);
- } while ((type==0)||(count>1)); /* The low-order byte holds
+ if (verbose)
+ fprintf (stdout, "waiting for CMOS update-ended interrupt\n");
+
+ do {
+ rc = read(cmos_fd, &interrupt_info, sizeof(interrupt_info));
+ gettimeofday(&now, NULL);
+
+ if (rc == -1)
+ {
+ perror("read() from /dev/rtc to wait for clock tick failed");
+ exit(1);
+ }
+ type = (int)(interrupt_info & 0xff);
+ count = (int)(interrupt_info >> 8);
+ } while ((type==0)||(count>1)); /* The low-order byte holds
the interrupt type. The first read may succeed
immediately, but in that case the byte is zero, so we
know to try again. If there has been more than one
interrupt, then presumably periodic interrupts were
enabled. We need to try again for just the update
interrupt. */
-
- ioctl (cmos_fd, RTC_RD_TIME, &tm);
- ioctl (cmos_fd, RTC_UIE_OFF, NULL); /* disable update complete
+
+ } /* the CMOS clock tick just happened, and has been timestamped */
+
+ /* now get this just beginning RTC second */
+ ioctl (cmos_fd, RTC_RD_TIME, &tm);
+ ioctl (cmos_fd, RTC_UIE_OFF, NULL); /* disable update complete
interrupts */
- }
}
- else
+ else /* access the CMOS clock thru I/O ports */
{
/* The "do" loop is "low-risk programming" */
/* In theory it should never run more than once */
do
{
- busy_wait(&now);
+ busywait_uip_fall(&now);
tm.tm_sec = cmos_read_bcd (0);
tm.tm_min = cmos_read_bcd (2);
tm.tm_hour = cmos_read_bcd (4);
@@ -754,11 +786,23 @@ cmos_read_time (time_t *cmos_timep, doub
/* busywait for UIP fall and timestamp this event */
static void
-busy_wait(struct timeval *timestamp)
+busywait_uip_fall(struct timeval *timestamp)
{
long i;
-
- /* read RTC exactly on falling edge of update flag */
+
+ /*
+ * Initialise direct I/O access mode.
+ * This may have been already done previously by the main
+ * initialisation cmos_init(), if the CMOS access mode is direct I/O.
+ * We init it here again, to make busywait_uip_fall() usable in any
+ * other CMOS access modes (/dev/rtc).
+ */
+ cmos_init_directisa();
+
+ if (verbose)
+ fprintf (stdout, "waiting for CMOS update-in-progress fall\n");
+
+ /* read RTC exactly on falling edge of update-in-progress flag */
/* Wait for rise.... (may take up to 1 second) */
for (i = 0; i < 10000000; i++)