Hi, I have tested the read and set operations, they seemd fine. RTC has automatic leap year compensation, it's quite fun to see the effect. I still have a few questions:
- Day of year is not stored in RTC[0], though we can calculate it when we are reading RTC based on year, month, and day of month. Should we do it? - Is there an optimal value for ports_manage_port_operations_multithread()? [0]:http://bitsavers.trailing-edge.com/pdf/ibm/pc/at/1502494_PC_AT_Technical_Reference_Mar84.pdf 1-46 --- Makefile | 3 +- hurd/pioctl.defs | 52 +++++++++++ hurd/rtc.h | 44 +++++++++ rtc/Makefile | 39 ++++++++ rtc/main.c | 114 ++++++++++++++++++++++++ rtc/mig-mutate.h | 24 +++++ rtc/pioctl-ops.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++ sutils/MAKEDEV.sh | 4 +- 8 files changed, 501 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..6516e86c --- /dev/null +++ b/hurd/rtc.h @@ -0,0 +1,44 @@ +/* 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..daefb95d --- /dev/null +++ b/rtc/main.c @@ -0,0 +1,114 @@ +/* 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_option options[] = +{ + {0} +}; + +/* TODO: adding option */ +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + return ARGP_ERR_UNKNOWN; +} + +static const struct argp rtc_argp = +{ options, parse_opt, 0, "RTC 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"); + + /* 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"); + + /* Request for permission to do i/o on port numbers 0x70 and 0x71 for + accessing RTC registers. */ + err = ioperm (0x70, 2, true); + if (err) + error (1, err, "Request IO permission failed"); + + /* Launch. */ + ports_manage_port_operations_multithread (rtccntl->pi.bucket, demuxer, + 30 * 1000, 2 * 60 * 1000, 0); + + return 0; +} + +void +trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +{ +} + +error_t +trivfs_goaway (struct trivfs_control *fsys, int flags) +{ + exit (0); +} 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..c33403c8 --- /dev/null +++ b/rtc/pioctl-ops.c @@ -0,0 +1,223 @@ +/* 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)*10) +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) + +/* POSIX uses 1900 as epoch for a struct tm, and 1970 for a time_t. */ +#define TM_EPOCH 1900 + +static unsigned short clock_ctl_addr = 0x70; +static unsigned short clock_data_addr = 0x71; + +static inline unsigned long cmos_read(unsigned long reg) +{ + outb_p(reg, clock_ctl_addr); + return inb_p(clock_data_addr); +} + +static inline void cmos_write(unsigned long reg, unsigned long val) +{ + outb(reg, clock_ctl_addr); + outb(val, clock_data_addr); +} + + +static inline int cmos_clock_busy(void) +{ + return + /* poll bit 7 (UIP) of Control Register A */ + (cmos_read(10) & 0x80); +} + + +/* 9 RTC_RD_TIME -- Read RTC time */ +kern_return_t +rtc_S_pioctl_rtc_rd_time (struct trivfs_protid *cred, struct rtc_time *tm) +{ + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & O_READ)) + return EBADF; + + unsigned char status = 0; + unsigned char pmbit = 0; + + /* When we wait for 100 ms (it takes too long), we exit with error */ + int time_passed_in_milliseconds = 0; + bool read_rtc_successfully = false; + 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 (!(status & 0x04)) { /* BCD mode - the default */ + 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; + if (pmbit) { + tm->tm_hour += 12; + if (tm->tm_hour == 24) + tm->tm_hour = 0; + } + + /* 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) +{ + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openmodes & O_WRITE)) + return EBADF; + + unsigned char save_control, save_freq_select, pmbit = 0; + + /* 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) */ + + save_control = cmos_read(11); /* tell the clock it's being set */ + cmos_write(11, (save_control | 0x80)); + save_freq_select = cmos_read(10); /* stop and reset prescaler */ + cmos_write(10, (save_freq_select | 0x70)); + + tm.tm_year %= 100; + tm.tm_mon += 1; + tm.tm_wday += 1; + + if (!(save_control & 0x02)) { /* 12hr mode; the default is 24hr mode */ + if (tm.tm_hour == 0) + tm.tm_hour = 24; + if (tm.tm_hour > 12) { + tm.tm_hour -= 12; + pmbit = 0x80; + } + } + + if (!(save_control & 0x04)) { /* BCD mode - the default */ + 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 Linux 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