bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC PATCH v2] Add rtc server, rtc-read and rtc-set operations


From: Zhaoming Luo
Subject: [RFC PATCH v2] Add rtc server, rtc-read and rtc-set operations
Date: Wed, 20 Nov 2024 20:06:19 +0800

Some coding style fixes. The rtc read operation can calculate day of year
based on month, day of month and year. Use only one thread for accessing
rtc to avoid data race.

* Makefile: add rtc server into the compile chain
* hurd/pioctl.defs: new file. Interfaces for rtc ioctl operations
* hurd/rtc.h: new file. Interfaces for rtc device
* rtc/Makefile: new file. Makefile for rtc server
* rtc/main.c: new file. Initialisation for rtc translator
* rtc/mig-mutate.h: new file. Type translation for rtc server
* rtc/pioctl-ops.c: new file. The rtc server side implementation
* sutils/MAKEDEV.sh: create rtc device at startup

---
 Makefile          |   3 +-
 hurd/pioctl.defs  |  52 +++++++++
 hurd/rtc.h        |  45 ++++++++
 rtc/Makefile      |  39 +++++++
 rtc/main.c        | 104 ++++++++++++++++++
 rtc/mig-mutate.h  |  24 +++++
 rtc/pioctl-ops.c  | 268 ++++++++++++++++++++++++++++++++++++++++++++++
 sutils/MAKEDEV.sh |   4 +-
 8 files changed, 537 insertions(+), 2 deletions(-)
 create mode 100644 hurd/pioctl.defs
 create mode 100644 hurd/rtc.h
 create mode 100644 rtc/Makefile
 create mode 100644 rtc/main.c
 create mode 100644 rtc/mig-mutate.h
 create mode 100644 rtc/pioctl-ops.c

diff --git a/Makefile b/Makefile
index 4d848221..9d9e33c3 100644
--- a/Makefile
+++ b/Makefile
@@ -47,7 +47,8 @@ prog-subdirs = auth proc exec term \
               init \
               devnode \
               eth-multiplexer \
-              shutdown
+              shutdown \
+              rtc
 
 ifeq ($(HAVE_LIBRUMP),yes)
 prog-subdirs += rumpdisk
diff --git a/hurd/pioctl.defs b/hurd/pioctl.defs
new file mode 100644
index 00000000..307d9ee9
--- /dev/null
+++ b/hurd/pioctl.defs
@@ -0,0 +1,52 @@
+/* Definitions for /dev/rtc ioctls
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd 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, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+/* Ioctl group 'p'; the subsystem is derived from calculations in
+   hurd/ioctls.defs. */
+subsystem pioctl 140000;
+
+#include <hurd/ioctl_types.defs>
+
+import <hurd/rtc.h>;
+
+#ifdef PIOCTL_IMPORTS
+PIOCTL_IMPORTS
+#endif
+
+INTR_INTERFACE
+
+/* This is the arg for a struct rtc_time as specified by the
+   definition of _IOT_rtc_time in $(hurd)/hurd/rtc.h. */
+type rtc_time_t = struct[9] of int;
+
+/* TODO: adding other ioctl calls for /dev/rtc in mc146818rtc.h */
+skip; skip; skip; skip; /* 0 1 2 3 */
+skip; skip; skip; skip; /* 4 5 6 7 */
+skip; /* 8 */
+
+/* 9 RTC_RD_TIME */
+routine pioctl_rtc_rd_time (
+       reqport: io_t;
+       out tm: rtc_time_t);
+
+/* 10 RTC_SET_TIME */
+routine pioctl_rtc_set_time (
+       reqport: io_t;
+       tm: rtc_time_t);
diff --git a/hurd/rtc.h b/hurd/rtc.h
new file mode 100644
index 00000000..12ca7531
--- /dev/null
+++ b/hurd/rtc.h
@@ -0,0 +1,45 @@
+/* GNU Hurd RTC interface
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd 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, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#ifndef _RTC_H
+#define _RTC_H 1
+
+#include <hurd/ioctl.h>
+
+struct rtc_time
+{
+  int tm_sec;
+  int tm_min;
+  int tm_hour;
+  int tm_mday;
+  int tm_mon;
+  int tm_year;
+  int tm_wday;
+  int tm_yday;
+  int tm_isdst;
+};
+typedef struct rtc_time rtc_time_t;
+
+#define _IOT_rtc_time _IOT(_IOTS(int),9,0,0,0,0)
+
+#define RTC_RD_TIME    _IOR('p', 0x09, struct rtc_time) /* Read RTC time.  */
+#define RTC_SET_TIME   _IOW('p', 0x0a, struct rtc_time) /* Set RTC time.  */
+
+#endif /* rtc.h */
diff --git a/rtc/Makefile b/rtc/Makefile
new file mode 100644
index 00000000..e84d9c14
--- /dev/null
+++ b/rtc/Makefile
@@ -0,0 +1,39 @@
+#   Makefile for rtc server
+#
+#   Copyright (C) 2024 Free Software Foundation, Inc.
+#
+#   This file is part of the GNU Hurd.
+#
+#   The GNU Hurd 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, or (at
+#   your option) any later version.
+#
+#   The GNU Hurd is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+dir := rtc
+makemode := server
+
+SRCS = main.c pioctl-ops.c
+MIGSRCS = pioctlServer.c
+
+OBJS = main.o pioctlServer.o pioctl-ops.o
+
+HURDLIBS = trivfs shouldbeinlibc ports
+
+target = rtc
+
+include ../Makeconf
+
+MIGCOMSFLAGS += -prefix rtc_
+mig-sheader-prefix = rtc_
+pioctl-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h
+
+rtc_pioctl_S.h pioctlServer.c: mig-mutate.h
diff --git a/rtc/main.c b/rtc/main.c
new file mode 100644
index 00000000..19bf73b9
--- /dev/null
+++ b/rtc/main.c
@@ -0,0 +1,104 @@
+/* A translator for accessing rtc
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd 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, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#include <version.h>
+
+#include <error.h>
+#include <stdbool.h>
+#include <argp.h>
+#include <hurd/trivfs.h>
+#include <hurd/ports.h>
+#include <hurd/rtc.h>
+#include <sys/io.h>
+
+#include "rtc_pioctl_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (rtc);
+
+static struct trivfs_control *rtccntl;
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ | O_WRITE;
+
+static const struct argp rtc_argp =
+{ NULL, NULL, NULL, "Real-Time Clock device" };
+
+static int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+  mig_routine_t routine;
+  if ((routine = rtc_pioctl_server_routine (inp)) ||
+      (routine = NULL, trivfs_demuxer (inp, outp)))
+    {
+      if (routine)
+        (*routine) (inp, outp);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+  error_t err;
+  mach_port_t bootstrap;
+
+  argp_parse (&rtc_argp, argc, argv, 0, 0, 0);
+
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (1, 0, "Must be started as a translator");
+
+  /* Request for permission to do i/o on port numbers 0x70 and 0x71 for
+     accessing RTC registers.  Do this before replying to our parent, so
+     we don't end up saying "I'm ready!" and then immediately exit with
+     an error.  */
+  err = ioperm (0x70, 2, true);
+  if (err)
+    error (1, err, "Request IO permission failed");
+
+  /* Reply to our parent.  */
+  err = trivfs_startup (bootstrap, O_NORW, NULL, NULL, NULL, NULL, &rtccntl);
+  mach_port_deallocate (mach_task_self (), bootstrap);
+  if (err)
+    error (1, err, "trivfs_startup failed");
+
+  /* Launch.  */
+  ports_manage_port_operations_one_thread (rtccntl->pi.bucket, demuxer,
+                                          2 * 60 * 1000);
+
+  return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+  exit (EXIT_SUCCESS);
+}
diff --git a/rtc/mig-mutate.h b/rtc/mig-mutate.h
new file mode 100644
index 00000000..ddead5be
--- /dev/null
+++ b/rtc/mig-mutate.h
@@ -0,0 +1,24 @@
+/* Type translation for rtc operations
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd 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, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#define IO_INTRAN trivfs_protid_t trivfs_begin_using_protid (io_t)
+#define IO_INTRAN_PAYLOAD trivfs_protid_t trivfs_begin_using_protid_payload
+#define IO_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+#define PIOCTL_IMPORTS import "../libtrivfs/mig-decls.h";
diff --git a/rtc/pioctl-ops.c b/rtc/pioctl-ops.c
new file mode 100644
index 00000000..cb347db0
--- /dev/null
+++ b/rtc/pioctl-ops.c
@@ -0,0 +1,268 @@
+/* Server side implementation for rtc server
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd 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, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+/* This implementation is largely based on sys-utils/hwclock-cmos.c from
+   util-linux.  */
+
+/* A struct tm has int fields (it is defined in POSIX)
+   tm_sec      0-59, 60 or 61 only for leap seconds
+   tm_min      0-59
+   tm_hour     0-23
+   tm_mday     1-31
+   tm_mon      0-11
+   tm_year     number of years since 1900
+   tm_wday     0-6, 0=Sunday
+   tm_yday     0-365
+   tm_isdst    >0: yes, 0: no, <0: unknown  */
+
+#include "rtc_pioctl_S.h"
+#include <hurd/rtc.h>
+#include <hurd/hurd_types.h>
+#include <sys/io.h>
+#include <stdbool.h>
+
+/* Conversions to and from RTC internal format.  */
+#define BCD_TO_BIN(val) ((val)=((val)&15) + (((val)>>4)&15)*10 + \
+       ((val)>>8)*100)
+#define BIN_TO_BCD(val) ((val)=(((val)/100)<<8) + \
+       ((((val)/10)%10)<<4) + (val)%10)
+
+/* POSIX uses 1900 as epoch for a struct tm, and 1970 for a time_t.  */
+#define TM_EPOCH 1900
+
+#define CLOCK_CTL_ADDR 0x70
+#define CLOCK_DATA_ADDR 0x71
+
+static int month_to_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 
31};
+
+static inline unsigned char
+cmos_read (unsigned char reg)
+{
+  outb_p (reg, CLOCK_CTL_ADDR);
+  return inb_p (CLOCK_DATA_ADDR);
+}
+
+static inline void
+cmos_write (unsigned char reg, unsigned char val)
+{
+  outb_p (reg, CLOCK_CTL_ADDR);
+  outb_p (val, CLOCK_DATA_ADDR);
+}
+
+static inline int
+cmos_clock_busy (void)
+{
+  /* Poll bit 7 (UIP) of Control Register A.  */
+  return (cmos_read (10) & 0x80);
+}
+
+/* Calculate day of year based on month, day of month, and year.  The value
+   it returns is in binary format.  The values of mon, mday, and year are in
+   binary format as well.  */
+static int
+calculate_yday (int mon, int mday, int year)
+{
+  int yday = 0;
+  int i;
+
+  for (i = 0; i < mon; ++i)
+    yday += month_to_days[i];
+  yday += mday;
+
+  /* If the year is a leap year and February is added in yday, one more day
+     is added because February day number is 29 in a leap year.  */
+  if ((((TM_EPOCH + year) % 4) == 0) && mon > 1)
+    yday++;
+
+  /* Convert the range of yday from [1,366] to [0,365].  */
+  yday--;
+
+  return yday;
+}
+
+/* 9 RTC_RD_TIME -- Read RTC time.  */
+kern_return_t
+rtc_S_pioctl_rtc_rd_time (struct trivfs_protid *cred, struct rtc_time *tm)
+{
+  unsigned char status = 0;
+  unsigned char pmbit = 0;
+  int time_passed_in_milliseconds = 0;
+  bool read_rtc_successfully = false;
+
+  if (!cred)
+    return EOPNOTSUPP;
+  if (!(cred->po->openmodes & O_READ))
+    return EBADF;
+
+  /* When we wait for 100 ms (it takes too long), we exit with error.  */
+  while (time_passed_in_milliseconds < 100)
+    {
+      if (!cmos_clock_busy ())
+       {
+         tm->tm_sec = cmos_read (0);
+         tm->tm_min = cmos_read (2);
+         tm->tm_hour = cmos_read (4);
+         tm->tm_wday = cmos_read (6);
+         tm->tm_mday = cmos_read (7);
+         tm->tm_mon = cmos_read (8);
+         tm->tm_year = cmos_read (9);
+         status = cmos_read (11);
+         /* Unless the clock changed while we were reading, consider this
+            a good clock read.  */
+         if (tm->tm_sec == cmos_read (0))
+           {
+             read_rtc_successfully = true;
+             break;
+           }
+       }
+      usleep (1000);
+      time_passed_in_milliseconds++;
+    }
+
+  if (!read_rtc_successfully)
+    return EBUSY;
+
+  /* If the data we just read is in BCD format, convert it to binary
+     format.  */
+  if (!(status & 0x04))
+    {
+      BCD_TO_BIN (tm->tm_sec);
+      BCD_TO_BIN (tm->tm_min);
+      pmbit = (tm->tm_hour & 0x80);
+      tm->tm_hour &= 0x7f;
+      BCD_TO_BIN (tm->tm_hour);
+      BCD_TO_BIN (tm->tm_wday);
+      BCD_TO_BIN (tm->tm_mday);
+      BCD_TO_BIN (tm->tm_mon);
+      BCD_TO_BIN (tm->tm_year);
+    }
+
+  /* We don't use the century byte of the Hardware Clock since we
+     don't know its address (usually 50 or 55).  Here, we follow the
+     advice of the X/Open Base Working Group: "if century is not
+     specified, then values in the range [69-99] refer to years in the
+     twentieth century (1969 to 1999 inclusive), and values in the
+     range [00-68] refer to years in the twenty-first century (2000 to
+     2068 inclusive)".  */
+  tm->tm_wday -= 1;
+  tm->tm_mon -= 1;
+  if (tm->tm_year < 69)
+    tm->tm_year += 100;
+
+  /* Calculate day of year.  */
+  tm->tm_yday = calculate_yday (tm->tm_mon, tm->tm_mday, tm->tm_year);
+
+  if (pmbit)
+    {
+      tm->tm_hour += 12;
+      if (tm->tm_hour == 24)
+        tm->tm_hour = 0;
+    }
+
+  /* We don't know whether it's daylight.  */
+  tm->tm_isdst = -1;
+
+  return KERN_SUCCESS;
+}
+
+/* 10 RTC_SET_TIME -- Set RTC time.  */
+kern_return_t
+rtc_S_pioctl_rtc_set_time (struct trivfs_protid *cred, struct rtc_time tm)
+{
+  unsigned char save_control, save_freq_select, pmbit = 0;
+
+  if (!cred)
+    return EOPNOTSUPP;
+  if (!(cred->po->openmodes & O_WRITE))
+    return EBADF;
+
+  /* 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 (may stop RTC)
+            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).  */
+
+  /* Tell the clock it's being set.  */
+  save_control = cmos_read (11);
+  cmos_write (11, (save_control | 0x80));
+  /* Stop and reset prescaler.  */
+  save_freq_select = cmos_read (10);
+  cmos_write (10, (save_freq_select | 0x70));
+
+  tm.tm_year %= 100;
+  tm.tm_mon += 1;
+  tm.tm_wday += 1;
+
+  /* 12hr mode; the default is 24hr mode.  */
+  if (!(save_control & 0x02))
+    {
+      if (tm.tm_hour == 0)
+       tm.tm_hour = 24;
+      if (tm.tm_hour > 12)
+       {
+         tm.tm_hour -= 12;
+         pmbit = 0x80;
+       }
+    }
+
+  /* BCD mode - the default.  */
+  if (!(save_control & 0x04))
+    {
+      BIN_TO_BCD (tm.tm_sec);
+      BIN_TO_BCD (tm.tm_min);
+      BIN_TO_BCD (tm.tm_hour);
+      BIN_TO_BCD (tm.tm_wday);
+      BIN_TO_BCD (tm.tm_mday);
+      BIN_TO_BCD (tm.tm_mon);
+      BIN_TO_BCD (tm.tm_year);
+    }
+
+  cmos_write (0, tm.tm_sec);
+  cmos_write (2, tm.tm_min);
+  cmos_write (4, tm.tm_hour | pmbit);
+  cmos_write (6, tm.tm_wday);
+  cmos_write (7, tm.tm_mday);
+  cmos_write (8, tm.tm_mon);
+  cmos_write (9, tm.tm_year);
+
+  /* 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.  */
+  cmos_write (11, save_control);
+  cmos_write (10, save_freq_select);
+
+  return KERN_SUCCESS;
+}
diff --git a/sutils/MAKEDEV.sh b/sutils/MAKEDEV.sh
index c3d7d112..c1ef4f92 100644
--- a/sutils/MAKEDEV.sh
+++ b/sutils/MAKEDEV.sh
@@ -120,10 +120,12 @@ mkdev() {
        ;;
 
       std)
-       mkdev console tty random urandom null zero full fd time mem klog shm
+       mkdev console tty random urandom null zero full fd time mem klog shm rtc
        ;;
       console|com[0-9])
        st $I root 600 c /hurd/term ${DEVDIR}/$I device $I;;
+      rtc)
+       st $I root 644 c /hurd/rtc;;
       vcs)
         st $I root 600 d /hurd/console;;
       tty[1-9][0-9]|tty[1-9])
-- 
2.47.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]