1 /*
2  * Copyright (c) 2006-2024 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-01-30     armink       the first version
9  */
10 
11 #include <sys/time.h>
12 #include <string.h>
13 #include <rtthread.h>
14 #include <rtdevice.h>
15 
16 #ifdef RT_USING_KTIME
17 #include <ktime.h>
18 #endif
19 
20 #ifdef RT_USING_SOFT_RTC
21 
22 /* 2018-01-30 14:44:50 = RTC_TIME_INIT(2018, 1, 30, 14, 44, 50)  */
23 #define RTC_TIME_INIT(year, month, day, hour, minute, second)        \
24     {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second}
25 
26 #ifndef SOFT_RTC_TIME_DEFAULT
27 #define SOFT_RTC_TIME_DEFAULT                    RTC_TIME_INIT(2018, 1, 1, 0, 0 ,0)
28 #endif
29 
30 #ifndef RTC_AUTO_SYNC_FIRST_DELAY
31 #define RTC_AUTO_SYNC_FIRST_DELAY 25
32 #endif
33 #ifndef RTC_AUTO_SYNC_PERIOD
34 #define RTC_AUTO_SYNC_PERIOD 3600
35 #endif
36 
37 static struct rt_work rtc_sync_work;
38 
39 static struct rt_device soft_rtc_dev;
40 static rt_tick_t init_tick;
41 static time_t init_time;
42 static struct timeval init_tv = {0};
43 
44 #ifdef RT_USING_KTIME
45 static struct timespec init_ts = {0};
46 #endif
47 
48 #ifdef RT_USING_ALARM
49 
50 static struct rt_rtc_wkalarm wkalarm;
51 static struct rt_timer alarm_time;
52 
alarm_timeout(void * param)53 static void alarm_timeout(void *param)
54 {
55     rt_alarm_update(param, 1);
56 }
57 
soft_rtc_alarm_update(struct rt_rtc_wkalarm * palarm)58 static void soft_rtc_alarm_update(struct rt_rtc_wkalarm *palarm)
59 {
60     rt_tick_t next_tick;
61 
62     if (palarm->enable)
63     {
64         next_tick = RT_TICK_PER_SECOND;
65         rt_timer_control(&alarm_time, RT_TIMER_CTRL_SET_TIME, &next_tick);
66         rt_timer_start(&alarm_time);
67     }
68     else
69     {
70         rt_timer_stop(&alarm_time);
71     }
72 }
73 
74 #endif
75 
set_rtc_time(time_t t)76 static void set_rtc_time(time_t t)
77 {
78     init_time = t - (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
79 #ifdef RT_USING_ALARM
80     soft_rtc_alarm_update(&wkalarm);
81 #endif
82 }
83 
soft_rtc_control(rt_device_t dev,int cmd,void * args)84 static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
85 {
86     time_t *t;
87     struct tm time_temp;
88 
89     RT_ASSERT(dev != RT_NULL);
90     rt_memset(&time_temp, 0, sizeof(struct tm));
91 
92     switch (cmd)
93     {
94     case RT_DEVICE_CTRL_RTC_GET_TIME:
95     {
96         t = (time_t *) args;
97         *t = init_time + (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
98         break;
99     }
100     case RT_DEVICE_CTRL_RTC_SET_TIME:
101     {
102         t = (time_t *) args;
103         set_rtc_time(*t);
104         break;
105     }
106 #ifdef RT_USING_ALARM
107     case RT_DEVICE_CTRL_RTC_GET_ALARM:
108         *((struct rt_rtc_wkalarm *)args) = wkalarm;
109         break;
110     case RT_DEVICE_CTRL_RTC_SET_ALARM:
111         wkalarm = *((struct rt_rtc_wkalarm *)args);
112         soft_rtc_alarm_update(&wkalarm);
113         break;
114 #endif
115 #ifdef RT_USING_KTIME
116     case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
117     {
118         struct timeval _tv;
119         struct timeval *tv = (struct timeval *)args;
120         rt_ktime_boottime_get_us(&_tv);
121         tv->tv_sec  = init_time + _tv.tv_sec;
122         tv->tv_usec = init_tv.tv_usec + _tv.tv_usec;
123         break;
124     }
125     case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
126     {
127         struct timeval _tv;
128         struct timeval *tv = (struct timeval *)args;
129         rt_ktime_boottime_get_us(&_tv);
130         set_rtc_time(tv->tv_sec);
131         init_tv.tv_usec = tv->tv_usec - _tv.tv_usec;
132         break;
133     }
134     case RT_DEVICE_CTRL_RTC_GET_TIMESPEC:
135     {
136         struct timespec _ts;
137         struct timespec *ts = (struct timespec *)args;
138         rt_ktime_boottime_get_ns(&_ts);
139         ts->tv_sec  = init_time + _ts.tv_sec;
140         ts->tv_nsec = init_ts.tv_nsec + _ts.tv_nsec;
141         break;
142     }
143     case RT_DEVICE_CTRL_RTC_SET_TIMESPEC:
144     {
145         struct timespec _ts;
146         struct timespec *ts = (struct timespec *)args;
147         rt_ktime_boottime_get_ns(&_ts);
148         set_rtc_time(ts->tv_sec);
149         init_ts.tv_nsec = ts->tv_nsec - _ts.tv_nsec;
150         break;
151     }
152     case RT_DEVICE_CTRL_RTC_GET_TIMERES:
153     {
154         struct timespec *ts = (struct timespec *)args;
155         ts->tv_sec  = 0;
156         ts->tv_nsec = (rt_ktime_cputimer_getres() / RT_KTIME_RESMUL);
157         break;
158     }
159 #else
160     case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
161     {
162         struct timeval *tv = (struct timeval *)args;
163         rt_tick_t tick = rt_tick_get() - init_tick;
164         tv->tv_sec = init_time + tick / RT_TICK_PER_SECOND;
165         tv->tv_usec = init_tv.tv_usec + ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND));
166         break;
167     }
168     case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
169     {
170         struct timeval *tv = (struct timeval *)args;
171         rt_tick_t tick = rt_tick_get() - init_tick;
172         set_rtc_time(tv->tv_sec);
173         init_tv.tv_usec = tv->tv_usec - ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND));
174         break;
175     }
176     case RT_DEVICE_CTRL_RTC_GET_TIMERES:
177     {
178         struct timespec *ts = (struct timespec *)args;
179         ts->tv_sec  = 0;
180         ts->tv_nsec = (1000UL * 1000 * 1000) / RT_TICK_PER_SECOND;
181         break;
182     }
183 #endif /* RT_USING_KTIME */
184     default:
185         return -RT_EINVAL;
186     }
187 
188     return RT_EOK;
189 }
190 
191 #ifdef RT_USING_DEVICE_OPS
192 const static struct rt_device_ops soft_rtc_ops =
193 {
194     RT_NULL,
195     RT_NULL,
196     RT_NULL,
197     RT_NULL,
198     RT_NULL,
199     soft_rtc_control
200 };
201 #endif
202 
rt_soft_rtc_init(void)203 static int rt_soft_rtc_init(void)
204 {
205     static rt_bool_t init_ok = RT_FALSE;
206     struct tm time_new = SOFT_RTC_TIME_DEFAULT;
207 
208     if (init_ok)
209     {
210         return 0;
211     }
212     /* make sure only one 'rtc' device */
213 #if defined(RT_USING_SOFT_RTC) && defined(BSP_USING_ONCHIP_RTC)
214 #warning "Please note: Currently only one RTC device is allowed in the system, and the name is "rtc"."
215 #endif
216     RT_ASSERT(!rt_device_find("rtc"));
217 
218 #ifdef RT_USING_ALARM
219     rt_timer_init(&alarm_time,
220                   "alarm",
221                   alarm_timeout,
222                   &soft_rtc_dev,
223                   0,
224                   RT_TIMER_FLAG_SOFT_TIMER|RT_TIMER_FLAG_ONE_SHOT);
225 #endif
226 
227     init_tick = rt_tick_get();
228     init_time = timegm(&time_new);
229 
230     soft_rtc_dev.type    = RT_Device_Class_RTC;
231 
232     /* register rtc device */
233 #ifdef RT_USING_DEVICE_OPS
234     soft_rtc_dev.ops     = &soft_rtc_ops;
235 #else
236     soft_rtc_dev.init    = RT_NULL;
237     soft_rtc_dev.open    = RT_NULL;
238     soft_rtc_dev.close   = RT_NULL;
239     soft_rtc_dev.read    = RT_NULL;
240     soft_rtc_dev.write   = RT_NULL;
241     soft_rtc_dev.control = soft_rtc_control;
242 #endif
243 
244     /* no private */
245     soft_rtc_dev.user_data = RT_NULL;
246 
247     rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);
248 
249     init_ok = RT_TRUE;
250 
251     return 0;
252 }
253 INIT_DEVICE_EXPORT(rt_soft_rtc_init);
254 
255 #ifdef RT_USING_SYSTEM_WORKQUEUE
256 
rt_soft_rtc_sync(void)257 rt_err_t rt_soft_rtc_sync(void)
258 {
259     time_t time = 0;
260 
261     rt_device_control(&soft_rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &time);
262     set_rtc_time(time);
263     return RT_EOK;
264 }
265 
rtc_sync_work_func(struct rt_work * work,void * work_data)266 static void rtc_sync_work_func(struct rt_work *work, void *work_data)
267 {
268     rt_soft_rtc_sync();
269     rt_work_submit(work, rt_tick_from_millisecond(RTC_AUTO_SYNC_PERIOD * 1000));
270 }
271 
rt_soft_rtc_set_source(const char * name)272 rt_err_t rt_soft_rtc_set_source(const char *name)
273 {
274     RT_ASSERT(name != RT_NULL);
275     RT_ASSERT(rt_device_find(name));  /* make sure source is exist*/
276 
277     rt_work_init(&rtc_sync_work, rtc_sync_work_func, RT_NULL);
278     rt_work_submit(&rtc_sync_work, rt_tick_from_millisecond(RTC_AUTO_SYNC_FIRST_DELAY * 1000));
279 
280     return RT_EOK;
281 }
282 
283 #ifdef FINSH_USING_MSH
284 #include <finsh.h>
cmd_rtc_sync(int argc,char ** argv)285 static void cmd_rtc_sync(int argc, char **argv)
286 {
287     struct timeval  tv  = {0};
288     struct timezone tz  = {0};
289     time_t          now = (time_t)0;
290 
291     rt_soft_rtc_sync();
292 
293     gettimeofday(&tv, &tz);
294     now = tv.tv_sec;
295     /* output current time */
296     rt_kprintf("local time: %.*s", 25, ctime(&now));
297     rt_kprintf("timestamps: %ld\n", (long)tv.tv_sec);
298 }
299 MSH_CMD_EXPORT_ALIAS(cmd_rtc_sync, rtc_sync, Update time by soft rtc);
300 #endif
301 
302 #endif /* RT_USING_SYSTEM_WORKQUEUE */
303 
304 #endif /* RT_USING_SOFT_RTC */
305