From 5ca0fa556b24463bd52ce5e45dddefcd766ae9c3 Mon Sep 17 00:00:00 2001 From: Zhaoming Luo Date: Wed, 11 Dec 2024 08:54:15 +0800 Subject: [PATCH] Add rtc translator and RTC CMOS driver A /hurd/rtc translator will be created as, users can create a /dev/rtc device using the following command: ``` sudo settrans -c /dev/rtc /hurd/rtc ``` * Makefile: add rtc-cmos 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/rtc-cmos_pioctl-ops.c: new file. The rtc-cmos server-side implementation Signed-off-by: Zhaoming Luo Message-ID: <20241211005415.507656-2-zhmingluo@163.com> --- Makefile | 3 +- hurd/pioctl.defs | 59 ++++++++ hurd/rtc.h | 51 +++++++ rtc/Makefile | 39 ++++++ rtc/main.c | 104 ++++++++++++++ rtc/mig-mutate.h | 24 ++++ rtc/rtc-cmos_pioctl-ops.c | 276 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 555 insertions(+), 1 deletion(-) 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/rtc-cmos_pioctl-ops.c diff --git a/Makefile b/Makefile index 4d8482219..9d9e33c3e 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 000000000..77d830e70 --- /dev/null +++ b/hurd/pioctl.defs @@ -0,0 +1,59 @@ +/* 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 + +import ; + +#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; + +skip; skip; skip; /* 0 1 2 */ + +/* 3 RTC_UIE_ON */ +routine pioctl_rtc_uie_on ( + reqport: io_t); + +/* 4 RTC_UIE_OFF */ +routine pioctl_rtc_uie_off ( + reqport: io_t); + +skip; skip; skip; skip; /* 5 6 7 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 000000000..58bf5536b --- /dev/null +++ b/hurd/rtc.h @@ -0,0 +1,51 @@ +/* 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 + +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) + +/* ioctl calls that are permitted to the /dev/rtc interface, if + any of the RTC drivers are enabled. */ + +#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on. */ +#define RTC_UIE_OFF _IO('p', 0x04) /* ... off. */ + +#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 000000000..ce32de4cf --- /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 rtc-cmos_pioctl-ops.c +MIGSRCS = pioctlServer.c + +OBJS = main.o pioctlServer.o rtc-cmos_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 000000000..19bf73b9a --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#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 000000000..ddead5bea --- /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/rtc-cmos_pioctl-ops.c b/rtc/rtc-cmos_pioctl-ops.c new file mode 100644 index 000000000..8657cce1e --- /dev/null +++ b/rtc/rtc-cmos_pioctl-ops.c @@ -0,0 +1,276 @@ +/* Server side implementation for rtc-cmos + + 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 +#include +#include +#include + +/* 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 + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +static const int mon_yday[2][13] = +{ + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + + +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. */ +static int +calculate_yday (const struct rtc_time *tm) +{ + return mon_yday[is_leap (tm->tm_year)][tm->tm_mon] + tm->tm_mday - 1; +} + +/* 3 RTC_UIE_ON -- Enable update-ended interrupt. */ +kern_return_t +rtc_S_pioctl_rtc_uie_on (struct trivfs_protid *cred) +{ + return EOPNOTSUPP; +} + +/* 4 RTC_UIE_OFF -- Disable update-ended interrupt. */ +kern_return_t +rtc_S_pioctl_rtc_uie_off (struct trivfs_protid *cred) +{ + return EOPNOTSUPP; +} + +/* 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); + + 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; +}