/src/barebox/commands/hwclock.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | |
3 | | #include <common.h> |
4 | | #include <command.h> |
5 | | #include <getopt.h> |
6 | | #include <linux/err.h> |
7 | | #include <linux/ctype.h> |
8 | | #include <rtc.h> |
9 | | #include <sntp.h> |
10 | | #include <linux/rtc.h> |
11 | | #include <string.h> |
12 | | #include <environment.h> |
13 | | |
14 | | static int sscanf_two_digits(char *s, int *res) |
15 | 0 | { |
16 | 0 | char buf[3]; |
17 | 0 | unsigned long t; |
18 | |
|
19 | 0 | if (!isdigit(s[0]) || !isdigit(s[1])) { |
20 | 0 | return -EINVAL; |
21 | 0 | } |
22 | | |
23 | 0 | buf[0] = s[0]; |
24 | 0 | buf[1] = s[1]; |
25 | 0 | buf[2] = '\0'; |
26 | |
|
27 | 0 | t = simple_strtoul(buf, NULL, 10); |
28 | 0 | *res = t; |
29 | |
|
30 | 0 | return 0; |
31 | 0 | } |
32 | | |
33 | | static int parse_datestr(char *date_str, struct rtc_time *ptm) |
34 | 0 | { |
35 | 0 | char end = '\0'; |
36 | 0 | int len = strchrnul(date_str, '.') - date_str; |
37 | 0 | int year; |
38 | | |
39 | | /* ccyymmddHHMM[.SS] */ |
40 | 0 | if (len != 12) { |
41 | 0 | return -EINVAL; |
42 | 0 | } |
43 | | |
44 | 0 | if (sscanf_two_digits(date_str, &year) || |
45 | 0 | sscanf_two_digits(&date_str[2], &ptm->tm_year)) { |
46 | 0 | return -EINVAL; |
47 | 0 | } |
48 | | |
49 | 0 | ptm->tm_year = year * 100 + ptm->tm_year; |
50 | | |
51 | | /* Adjust years */ |
52 | 0 | ptm->tm_year -= 1900; |
53 | |
|
54 | 0 | if (sscanf_two_digits(&date_str[4], &ptm->tm_mon) || |
55 | 0 | sscanf_two_digits(&date_str[6], &ptm->tm_mday) || |
56 | 0 | sscanf_two_digits(&date_str[8], &ptm->tm_hour) || |
57 | 0 | sscanf_two_digits(&date_str[10], &ptm->tm_min)) { |
58 | 0 | return -EINVAL; |
59 | 0 | } |
60 | | |
61 | | /* Adjust month from 1-12 to 0-11 */ |
62 | 0 | ptm->tm_mon -= 1; |
63 | |
|
64 | 0 | end = date_str[12]; |
65 | |
|
66 | 0 | if (end == '.') { |
67 | | /* xxx.SS */ |
68 | 0 | if (!sscanf_two_digits(&date_str[13], &ptm->tm_sec)) { |
69 | 0 | end = '\0'; |
70 | 0 | } |
71 | | /* else end != NUL and we error out */ |
72 | 0 | } |
73 | |
|
74 | 0 | if (end != '\0') { |
75 | 0 | return -EINVAL; |
76 | 0 | } |
77 | | |
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | | static int do_hwclock(int argc, char *argv[]) |
82 | 0 | { |
83 | 0 | struct rtc_device *r; |
84 | 0 | struct rtc_time tm; |
85 | 0 | struct rtc_time stm; |
86 | 0 | const char *rtc_name = "rtc0"; |
87 | 0 | char *env_name = NULL; |
88 | 0 | int opt; |
89 | 0 | int set = 0; |
90 | 0 | int ret; |
91 | 0 | int ntp_to_hw = 0; |
92 | 0 | char *ntpserver = NULL; |
93 | |
|
94 | 0 | while ((opt = getopt(argc, argv, "f:s:e:n:")) > 0) { |
95 | |
|
96 | 0 | switch (opt) { |
97 | 0 | case 'f': |
98 | 0 | rtc_name = optarg; |
99 | 0 | break; |
100 | 0 | case 's': |
101 | 0 | memset(&stm, 0, sizeof(stm)); |
102 | |
|
103 | 0 | ret = parse_datestr(optarg, &stm); |
104 | 0 | if (ret) |
105 | 0 | return ret; |
106 | | |
107 | 0 | ret = rtc_valid_tm(&stm); |
108 | 0 | if (ret) |
109 | 0 | return ret; |
110 | | |
111 | 0 | set = 1; |
112 | 0 | break; |
113 | 0 | case 'e': |
114 | 0 | env_name = optarg; |
115 | 0 | break; |
116 | 0 | case 'n': |
117 | 0 | ntp_to_hw = 1; |
118 | 0 | ntpserver = optarg; |
119 | 0 | break; |
120 | 0 | default: |
121 | 0 | return COMMAND_ERROR_USAGE; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | 0 | r = rtc_lookup(rtc_name); |
126 | 0 | if (IS_ERR(r)) |
127 | 0 | return PTR_ERR(r); |
128 | | |
129 | 0 | if (set) { |
130 | 0 | return rtc_set_time(r, &stm); |
131 | 0 | } |
132 | | |
133 | 0 | if (ntp_to_hw) { |
134 | 0 | s64 now; |
135 | |
|
136 | 0 | if (!IS_ENABLED(CONFIG_NET_SNTP)) { |
137 | 0 | printf("SNTP support is disabled\n"); |
138 | 0 | return 1; |
139 | 0 | } |
140 | | |
141 | 0 | now = sntp(ntpserver); |
142 | 0 | if (now < 0) |
143 | 0 | return now; |
144 | | |
145 | 0 | rtc_time_to_tm(now, &stm); |
146 | 0 | printf("%s\n", time_str(&stm)); |
147 | 0 | return rtc_set_time(r, &stm); |
148 | 0 | } |
149 | | |
150 | 0 | ret = rtc_read_time(r, &tm); |
151 | 0 | if (ret < 0) |
152 | 0 | return ret; |
153 | | |
154 | 0 | if (env_name) { |
155 | 0 | unsigned long time; |
156 | |
|
157 | 0 | rtc_tm_to_time(&tm, &time); |
158 | 0 | pr_setenv(env_name, "%lu", time); |
159 | 0 | } else { |
160 | 0 | printf("%s\n", time_str(&tm)); |
161 | 0 | } |
162 | |
|
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | | BAREBOX_CMD_HELP_START(hwclock) |
167 | | BAREBOX_CMD_HELP_TEXT("Options:") |
168 | | BAREBOX_CMD_HELP_OPT ("-f NAME\t\t\t", "RTC device name (default rtc0)") |
169 | | BAREBOX_CMD_HELP_OPT ("-e VARNAME\t\t", "store RTC readout into variable VARNAME") |
170 | | BAREBOX_CMD_HELP_OPT ("-n NTPSERVER\t\t", "set RTC from NTP server") |
171 | | BAREBOX_CMD_HELP_OPT ("-s ccyymmddHHMM[.SS]\t", "set time") |
172 | | BAREBOX_CMD_HELP_END |
173 | | |
174 | | BAREBOX_CMD_START(hwclock) |
175 | | .cmd = do_hwclock, |
176 | | BAREBOX_CMD_DESC("query or set the hardware clock (RTC)") |
177 | | BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) |
178 | | BAREBOX_CMD_HELP(cmd_hwclock_help) |
179 | | BAREBOX_CMD_END |