10f66f451Sopenharmony_ci/* hwclock.c - get and set the hwclock
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard, but see Documentation/rtc.txt in the linux kernel source..
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ciUSE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_SBIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig HWCLOCK
100f66f451Sopenharmony_ci  bool "hwclock"
110f66f451Sopenharmony_ci  default y
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: hwclock [-rswtluf]
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Get/set the hardware clock.
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ci    -f FILE	Use specified device file instead of /dev/rtc (--rtc)
180f66f451Sopenharmony_ci    -l	Hardware clock uses localtime (--localtime)
190f66f451Sopenharmony_ci    -r	Show hardware clock time (--show)
200f66f451Sopenharmony_ci    -s	Set system time from hardware clock (--hctosys)
210f66f451Sopenharmony_ci    -t	Set the system time based on the current timezone (--systz)
220f66f451Sopenharmony_ci    -u	Hardware clock uses UTC (--utc)
230f66f451Sopenharmony_ci    -w	Set hardware clock from system time (--systohc)
240f66f451Sopenharmony_ci*/
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci#define FOR_hwclock
270f66f451Sopenharmony_ci#include "toys.h"
280f66f451Sopenharmony_ci#include <linux/rtc.h>
290f66f451Sopenharmony_ci
300f66f451Sopenharmony_ciGLOBALS(
310f66f451Sopenharmony_ci  char *f;
320f66f451Sopenharmony_ci
330f66f451Sopenharmony_ci  int utc;
340f66f451Sopenharmony_ci)
350f66f451Sopenharmony_ci
360f66f451Sopenharmony_cistatic int rtc_find(struct dirtree* node)
370f66f451Sopenharmony_ci{
380f66f451Sopenharmony_ci  FILE *fp;
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_ci  if (!node->parent) return DIRTREE_RECURSE;
410f66f451Sopenharmony_ci
420f66f451Sopenharmony_ci  sprintf(toybuf, "/sys/class/rtc/%s/hctosys", node->name);
430f66f451Sopenharmony_ci  fp = fopen(toybuf, "r");
440f66f451Sopenharmony_ci  if (fp) {
450f66f451Sopenharmony_ci    int hctosys = 0, items = fscanf(fp, "%d", &hctosys);
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ci    fclose(fp);
480f66f451Sopenharmony_ci    if (items == 1 && hctosys == 1) {
490f66f451Sopenharmony_ci      sprintf(toybuf, "/dev/%s", node->name);
500f66f451Sopenharmony_ci      TT.f = toybuf;
510f66f451Sopenharmony_ci
520f66f451Sopenharmony_ci      return DIRTREE_ABORT;
530f66f451Sopenharmony_ci    }
540f66f451Sopenharmony_ci  }
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci  return 0;
570f66f451Sopenharmony_ci}
580f66f451Sopenharmony_ci
590f66f451Sopenharmony_civoid hwclock_main()
600f66f451Sopenharmony_ci{
610f66f451Sopenharmony_ci  struct timezone tzone;
620f66f451Sopenharmony_ci  struct timeval timeval;
630f66f451Sopenharmony_ci  struct tm tm;
640f66f451Sopenharmony_ci  time_t time;
650f66f451Sopenharmony_ci  int fd = -1;
660f66f451Sopenharmony_ci
670f66f451Sopenharmony_ci  // check for Grenich Mean Time
680f66f451Sopenharmony_ci  if (toys.optflags & FLAG_u) TT.utc = 1;
690f66f451Sopenharmony_ci  else {
700f66f451Sopenharmony_ci    FILE *fp;
710f66f451Sopenharmony_ci    char *s = 0;
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci    for (fp = fopen("/etc/adjtime", "r");
740f66f451Sopenharmony_ci         fp && getline(&s, (void *)toybuf, fp)>0;
750f66f451Sopenharmony_ci         free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3);
760f66f451Sopenharmony_ci    if (fp) fclose(fp);
770f66f451Sopenharmony_ci  }
780f66f451Sopenharmony_ci
790f66f451Sopenharmony_ci  if (!(toys.optflags&FLAG_t)) {
800f66f451Sopenharmony_ci    int w = toys.optflags & FLAG_w, flag = O_WRONLY*w;
810f66f451Sopenharmony_ci
820f66f451Sopenharmony_ci    // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it).
830f66f451Sopenharmony_ci    if (!TT.f && (fd = open("/dev/rtc", flag)) == -1) {
840f66f451Sopenharmony_ci      dirtree_read("/sys/class/rtc", rtc_find);
850f66f451Sopenharmony_ci      if (!TT.f) TT.f = "/dev/misc/rtc";
860f66f451Sopenharmony_ci    }
870f66f451Sopenharmony_ci    if (fd == -1) fd = xopen(TT.f, flag);
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_ci    // Get current time in seconds from rtc device. todo: get subsecond time
900f66f451Sopenharmony_ci    if (!w) {
910f66f451Sopenharmony_ci      char *s = s;
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci      xioctl(fd, RTC_RD_TIME, &tm);
940f66f451Sopenharmony_ci      if (TT.utc) s = xtzset("UTC0");
950f66f451Sopenharmony_ci      if ((time = mktime(&tm)) < 0) error_exit("mktime failed");
960f66f451Sopenharmony_ci      if (TT.utc) {
970f66f451Sopenharmony_ci        free(xtzset(s));
980f66f451Sopenharmony_ci        free(s);
990f66f451Sopenharmony_ci      }
1000f66f451Sopenharmony_ci    }
1010f66f451Sopenharmony_ci  }
1020f66f451Sopenharmony_ci
1030f66f451Sopenharmony_ci  if (toys.optflags & (FLAG_w|FLAG_t)) {
1040f66f451Sopenharmony_ci    if (gettimeofday(&timeval, 0)) perror_exit("gettimeofday failed");
1050f66f451Sopenharmony_ci    if (!(TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm))
1060f66f451Sopenharmony_ci      error_exit(TT.utc ? "gmtime_r failed" : "localtime_r failed");
1070f66f451Sopenharmony_ci  }
1080f66f451Sopenharmony_ci
1090f66f451Sopenharmony_ci  if (toys.optflags & FLAG_w) {
1100f66f451Sopenharmony_ci    /* The value of tm_isdst is positive if daylight saving time is in effect,
1110f66f451Sopenharmony_ci     * zero if it is not and negative if the information is not available.
1120f66f451Sopenharmony_ci     * todo: so why isn't this negative...? */
1130f66f451Sopenharmony_ci    tm.tm_isdst = 0;
1140f66f451Sopenharmony_ci    xioctl(fd, RTC_SET_TIME, &tm);
1150f66f451Sopenharmony_ci  } else if (toys.optflags & FLAG_s) {
1160f66f451Sopenharmony_ci    tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
1170f66f451Sopenharmony_ci    timeval.tv_sec = time;
1180f66f451Sopenharmony_ci    timeval.tv_usec = 0; // todo: fixit
1190f66f451Sopenharmony_ci  } else if (toys.optflags & FLAG_t) {
1200f66f451Sopenharmony_ci    // Adjust seconds for timezone and daylight saving time
1210f66f451Sopenharmony_ci    // extern long timezone is defined in header sys/time.h
1220f66f451Sopenharmony_ci    tzone.tz_minuteswest = timezone / 60;
1230f66f451Sopenharmony_ci    if (tm.tm_isdst) tzone.tz_minuteswest -= 60;
1240f66f451Sopenharmony_ci    if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
1250f66f451Sopenharmony_ci  } else {
1260f66f451Sopenharmony_ci    char *c = ctime(&time), *s = strrchr(c, '\n');
1270f66f451Sopenharmony_ci
1280f66f451Sopenharmony_ci    if (s) *s = '\0';
1290f66f451Sopenharmony_ci    // TODO: implement this.
1300f66f451Sopenharmony_ci    xprintf("%s  0.000000 seconds\n", c);
1310f66f451Sopenharmony_ci  }
1320f66f451Sopenharmony_ci  if (toys.optflags & (FLAG_t|FLAG_s)) {
1330f66f451Sopenharmony_ci    tzone.tz_dsttime = 0;
1340f66f451Sopenharmony_ci    if (settimeofday(&timeval, &tzone)) perror_exit("settimeofday failed");
1350f66f451Sopenharmony_ci  }
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_ci  if (fd != -1) close(fd);
1380f66f451Sopenharmony_ci}
139