14483eaadba149a5286a26a292f72631793b4826aAshwini Sharma/* hwclock.c - get and set the hwclock
24483eaadba149a5286a26a292f72631793b4826aAshwini Sharma *
34483eaadba149a5286a26a292f72631793b4826aAshwini Sharma * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com>
44483eaadba149a5286a26a292f72631793b4826aAshwini Sharma *
5c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley * No standard, but see Documentation/rtc.txt in the linux kernel source..
64483eaadba149a5286a26a292f72631793b4826aAshwini Sharma *
72c9b0e3009cf76e9d7fca772945711675678d181Rob LandleyUSE_HWCLOCK(NEWTOY(hwclock, ">0(fast)f(rtc):u(utc)l(localtime)t(systz)s(hctosys)r(show)w(systohc)[-ul][!rtsw]", TOYFLAG_USR|TOYFLAG_BIN))
84483eaadba149a5286a26a292f72631793b4826aAshwini Sharma
94483eaadba149a5286a26a292f72631793b4826aAshwini Sharmaconfig HWCLOCK
104483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  bool "hwclock"
114483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  default n
124483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  help
1372f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    usage: hwclock [-rswtluf]
1472f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley
15c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    -f FILE Use specified device file instead of /dev/rtc (--rtc)
1672f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -l      Hardware clock uses localtime (--localtime)
1772f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -r      Show hardware clock time (--show)
1872f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -s      Set system time from hardware clock (--hctosys)
1972f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -t      Set the system time based on the current timezone (--systz)
2072f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -u      Hardware clock uses UTC (--utc)
2172f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    -w      Set hardware clock from system time (--systohc)
224483eaadba149a5286a26a292f72631793b4826aAshwini Sharma*/
23c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
244483eaadba149a5286a26a292f72631793b4826aAshwini Sharma#define FOR_hwclock
254483eaadba149a5286a26a292f72631793b4826aAshwini Sharma#include "toys.h"
264483eaadba149a5286a26a292f72631793b4826aAshwini Sharma#include <linux/rtc.h>
274483eaadba149a5286a26a292f72631793b4826aAshwini Sharma
284483eaadba149a5286a26a292f72631793b4826aAshwini SharmaGLOBALS(
294483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  char *fname;
304483eaadba149a5286a26a292f72631793b4826aAshwini Sharma
314483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  int utc;
324483eaadba149a5286a26a292f72631793b4826aAshwini Sharma)
334483eaadba149a5286a26a292f72631793b4826aAshwini Sharma
34c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landleystatic int rtc_find(struct dirtree* node)
35ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes{
36ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  FILE *fp;
37ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes
38ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  if (!node->parent) return DIRTREE_RECURSE;
39ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes
40ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  snprintf(toybuf, sizeof(toybuf), "/sys/class/rtc/%s/hctosys", node->name);
41ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  fp = fopen(toybuf, "r");
42ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  if (fp) {
43c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    int hctosys = 0, items = fscanf(fp, "%d", &hctosys);
44c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
45ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes    fclose(fp);
46ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes    if (items == 1 && hctosys == 1) {
47ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes      snprintf(toybuf, sizeof(toybuf), "/dev/%s", node->name);
48ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes      TT.fname = toybuf;
49c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
50ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes      return DIRTREE_ABORT;
51ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes    }
52ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes  }
53ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes
54c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  return 0;
55ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes}
56ef0546d4f536f42a57af4c32bd37f7fd752d10c2Elliott Hughes
5772f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landleyvoid hwclock_main()
584483eaadba149a5286a26a292f72631793b4826aAshwini Sharma{
59c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  struct timezone tzone;
60c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  struct timeval timeval;
612c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  struct tm tm;
622c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  time_t time;
632c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  int fd = -1;
64c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
65c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  // check for Grenich Mean Time
66c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  if (toys.optflags & FLAG_u) TT.utc = 1;
67c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  else {
682c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    FILE *fp;
692c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    char *s = 0;
7072f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley
712c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    for (fp = fopen("/etc/adjtime", "r");
722c9b0e3009cf76e9d7fca772945711675678d181Rob Landley         fp && getline(&s, (void *)toybuf, fp)>0;
732c9b0e3009cf76e9d7fca772945711675678d181Rob Landley         free(s), s = 0) TT.utc += !strncmp(s, "UTC", 3);
742c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (fp) fclose(fp);
752c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  }
762c9b0e3009cf76e9d7fca772945711675678d181Rob Landley
772c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  if (!(toys.optflags&FLAG_t)) {
782c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    int w = toys.optflags & FLAG_w, flag = O_WRONLY*w;
79c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
802c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    // Open /dev/rtc (if your system has no /dev/rtc symlink, search for it).
812c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (!TT.fname && (fd = open("/dev/rtc", flag)) == -1) {
822c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      dirtree_read("/sys/class/rtc", rtc_find);
832c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      if (!TT.fname) TT.fname = "/dev/misc/rtc";
842c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    }
852c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (fd == -1) fd = xopen(TT.fname, flag);
862c9b0e3009cf76e9d7fca772945711675678d181Rob Landley
872c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    // Get current time in seconds from rtc device. todo: get subsecond time
882c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (!w) {
892c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      char *s = s;
902c9b0e3009cf76e9d7fca772945711675678d181Rob Landley
912c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      xioctl(fd, RTC_RD_TIME, &tm);
922c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      if (TT.utc) s = xtzset("UTC0");
932c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      if ((time = mktime(&tm)) < 0) goto bad;
942c9b0e3009cf76e9d7fca772945711675678d181Rob Landley      if (TT.utc) {
952c9b0e3009cf76e9d7fca772945711675678d181Rob Landley        free(xtzset(s));
962c9b0e3009cf76e9d7fca772945711675678d181Rob Landley        free(s);
974483eaadba149a5286a26a292f72631793b4826aAshwini Sharma      }
984483eaadba149a5286a26a292f72631793b4826aAshwini Sharma    }
99c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  }
100c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
101fb49bf09694439102959fbce896404ab819bdf99Rob Landley  if (toys.optflags & (FLAG_w|FLAG_t))
1022c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (gettimeofday(&timeval, 0)
1032c9b0e3009cf76e9d7fca772945711675678d181Rob Landley        || (TT.utc ? gmtime_r : localtime_r)(&timeval.tv_sec, &tm)) goto bad;
104c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
105fb49bf09694439102959fbce896404ab819bdf99Rob Landley  if (toys.optflags & FLAG_w) {
106c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    /* The value of tm_isdst will positive if daylight saving time is in effect,
107c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley     * zero if it is not and negative if the information is not available.
1082c9b0e3009cf76e9d7fca772945711675678d181Rob Landley     * todo: so why isn't this negative...? */
109c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    tm.tm_isdst = 0;
110c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    xioctl(fd, RTC_SET_TIME, &time);
111c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  } else if (toys.optflags & FLAG_s) {
112c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    tzone.tz_minuteswest = timezone / 60 - 60 * daylight;
1132c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    timeval.tv_sec = time;
114fb49bf09694439102959fbce896404ab819bdf99Rob Landley    timeval.tv_usec = 0; // todo: fixit
115c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  } else if (toys.optflags & FLAG_t) {
1162c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    // Adjust seconds for timezone and daylight saving time
117c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    // extern long timezone is defined in header sys/time.h
118c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    tzone.tz_minuteswest = timezone / 60;
1192c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    if (tm.tm_isdst) tzone.tz_minuteswest -= 60;
120c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    if (!TT.utc) timeval.tv_sec += tzone.tz_minuteswest * 60;
121c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley  } else {
1222c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    char *c = ctime(&time), *s = strrchr(c, '\n');
123c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley
124c277f347bba2417106b32212d9d40aceb4a88fb5Rob Landley    if (s) *s = '\0';
12572f8a5f7fedeb81c99044d3443872ae91b5ecfecRob Landley    // TODO: implement this.
1262c9b0e3009cf76e9d7fca772945711675678d181Rob Landley    xprintf("%s  0.000000 seconds\n", c);
1274483eaadba149a5286a26a292f72631793b4826aAshwini Sharma  }
128fb49bf09694439102959fbce896404ab819bdf99Rob Landley  if (toys.optflags & (FLAG_t|FLAG_s)) {
129fb49bf09694439102959fbce896404ab819bdf99Rob Landley    tzone.tz_dsttime = 0;
130fb49bf09694439102959fbce896404ab819bdf99Rob Landley    if (settimeofday(&timeval, &tzone)) goto bad;
131fb49bf09694439102959fbce896404ab819bdf99Rob Landley  }
1322c9b0e3009cf76e9d7fca772945711675678d181Rob Landley
1332c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  if (fd != -1) close(fd);
1342c9b0e3009cf76e9d7fca772945711675678d181Rob Landley
1352c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  return;
1362c9b0e3009cf76e9d7fca772945711675678d181Rob Landleybad:
1372c9b0e3009cf76e9d7fca772945711675678d181Rob Landley  perror_exit("failed");
1384483eaadba149a5286a26a292f72631793b4826aAshwini Sharma}
139