1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2012-01-29     aozima       first version.
9  * 2012-04-12     aozima       optimization: find rtc device only first.
10  * 2012-04-16     aozima       add scheduler lock for set_date and set_time.
11  * 2018-02-16     armink       add auto sync time by NTP
12  * 2021-05-09     Meco Man     remove NTP
13  * 2021-06-11     iysheng      implement RTC framework V2.0
14  * 2021-07-30     Meco Man     move rtc_core.c to rtc.c
15  */
16 
17 #include <string.h>
18 #include <stdlib.h>
19 #include <rtthread.h>
20 #include <drivers/dev_rtc.h>
21 
22 #ifdef RT_USING_RTC
23 
24 static rt_device_t _rtc_device;
25 /*
26  * This function initializes rtc_core
27  */
rt_rtc_init(struct rt_device * dev)28 static rt_err_t rt_rtc_init(struct rt_device *dev)
29 {
30     rt_rtc_dev_t *rtc_core;
31 
32     RT_ASSERT(dev != RT_NULL);
33     rtc_core = (rt_rtc_dev_t *)dev;
34     if (rtc_core->ops->init)
35     {
36         return (rtc_core->ops->init());
37     }
38 
39     return -RT_ENOSYS;
40 }
41 
rt_rtc_open(struct rt_device * dev,rt_uint16_t oflag)42 static rt_err_t rt_rtc_open(struct rt_device *dev, rt_uint16_t oflag)
43 {
44     return RT_EOK;
45 }
46 
rt_rtc_close(struct rt_device * dev)47 static rt_err_t rt_rtc_close(struct rt_device *dev)
48 {
49     /* Add close member function in rt_rtc_ops when need,
50      * then call that function here.
51      * */
52     return RT_EOK;
53 }
54 
rt_rtc_control(struct rt_device * dev,int cmd,void * args)55 static rt_err_t rt_rtc_control(struct rt_device *dev, int cmd, void *args)
56 {
57 #define TRY_DO_RTC_FUNC(rt_rtc_dev, func_name, args) \
58     rt_rtc_dev->ops->func_name ?  rt_rtc_dev->ops->func_name(args) : -RT_EINVAL;
59 
60     rt_rtc_dev_t *rtc_device;
61     rt_err_t ret = -RT_EINVAL;
62 
63     RT_ASSERT(dev != RT_NULL);
64     rtc_device = (rt_rtc_dev_t *)dev;
65 
66     switch (cmd)
67     {
68         case RT_DEVICE_CTRL_RTC_GET_TIME:
69             ret = TRY_DO_RTC_FUNC(rtc_device, get_secs, args);
70             break;
71         case RT_DEVICE_CTRL_RTC_SET_TIME:
72             ret = TRY_DO_RTC_FUNC(rtc_device, set_secs, args);
73             break;
74         case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
75             ret = TRY_DO_RTC_FUNC(rtc_device, get_timeval, args);
76             break;
77         case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
78             ret = TRY_DO_RTC_FUNC(rtc_device, set_timeval, args);
79             break;
80         case RT_DEVICE_CTRL_RTC_GET_ALARM:
81             ret = TRY_DO_RTC_FUNC(rtc_device, get_alarm, args);
82             break;
83         case RT_DEVICE_CTRL_RTC_SET_ALARM:
84             ret = TRY_DO_RTC_FUNC(rtc_device, set_alarm, args);
85             break;
86         default:
87             break;
88     }
89 
90     return ret;
91 
92 #undef TRY_DO_RTC_FUNC
93 }
94 
95 #ifdef RT_USING_DEVICE_OPS
96 const static struct rt_device_ops rtc_core_ops =
97 {
98     rt_rtc_init,
99     rt_rtc_open,
100     rt_rtc_close,
101     RT_NULL,
102     RT_NULL,
103     rt_rtc_control,
104 };
105 #endif /* RT_USING_DEVICE_OPS */
106 
rt_hw_rtc_register(rt_rtc_dev_t * rtc,const char * name,rt_uint32_t flag,void * data)107 rt_err_t rt_hw_rtc_register(rt_rtc_dev_t  *rtc,
108                             const char    *name,
109                             rt_uint32_t    flag,
110                             void          *data)
111 {
112     struct rt_device *device;
113     RT_ASSERT(rtc != RT_NULL);
114 
115     device = &(rtc->parent);
116 
117     device->type        = RT_Device_Class_RTC;
118     device->rx_indicate = RT_NULL;
119     device->tx_complete = RT_NULL;
120 
121 #ifdef RT_USING_DEVICE_OPS
122     device->ops         = &rtc_core_ops;
123 #else
124     device->init        = rt_rtc_init;
125     device->open        = rt_rtc_open;
126     device->close       = rt_rtc_close;
127     device->read        = RT_NULL;
128     device->write       = RT_NULL;
129     device->control     = rt_rtc_control;
130 #endif /* RT_USING_DEVICE_OPS */
131     device->user_data   = data;
132 
133     /* register a character device */
134     return rt_device_register(device, name, flag);
135 }
136 
137 /**
138  * Set system date(time not modify, local timezone).
139  *
140  * @param rt_uint32_t year  e.g: 2012.
141  * @param rt_uint32_t month e.g: 12 (1~12).
142  * @param rt_uint32_t day   e.g: 31.
143  *
144  * @return rt_err_t if set success, return RT_EOK.
145  */
set_date(rt_uint32_t year,rt_uint32_t month,rt_uint32_t day)146 rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)
147 {
148     time_t now, old_timestamp = 0;
149     struct tm tm_new = {0};
150     rt_err_t ret = -RT_ERROR;
151 
152     if (_rtc_device == RT_NULL)
153     {
154         _rtc_device = rt_device_find("rtc");
155         if (_rtc_device == RT_NULL)
156         {
157             return -RT_ERROR;
158         }
159     }
160 
161     /* get current time */
162     ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, &old_timestamp);
163     if (ret != RT_EOK)
164     {
165         return ret;
166     }
167 
168     /* converts calendar time into local time. */
169     localtime_r(&old_timestamp, &tm_new);
170 
171     /* update date. */
172     tm_new.tm_year = year - 1900;
173     tm_new.tm_mon  = month - 1; /* tm_mon: 0~11 */
174     tm_new.tm_mday = day;
175 
176     /* converts the local time into the calendar time. */
177     now = mktime(&tm_new);
178 
179     /* update to RTC device. */
180     ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);
181     return ret;
182 }
183 
184 /**
185  * Set system time(date not modify, local timezone).
186  *
187  * @param rt_uint32_t hour   e.g: 0~23.
188  * @param rt_uint32_t minute e.g: 0~59.
189  * @param rt_uint32_t second e.g: 0~59.
190  *
191  * @return rt_err_t if set success, return RT_EOK.
192  */
set_time(rt_uint32_t hour,rt_uint32_t minute,rt_uint32_t second)193 rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
194 {
195     time_t now, old_timestamp = 0;
196     struct tm tm_new = {0};
197     rt_err_t ret = -RT_ERROR;
198 
199     if (_rtc_device == RT_NULL)
200     {
201         _rtc_device = rt_device_find("rtc");
202         if (_rtc_device == RT_NULL)
203         {
204             return -RT_ERROR;
205         }
206     }
207 
208     /* get current time */
209     ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, &old_timestamp);
210     if (ret != RT_EOK)
211     {
212         return ret;
213     }
214 
215     /* converts calendar time into local time. */
216     localtime_r(&old_timestamp, &tm_new);
217 
218     /* update time. */
219     tm_new.tm_hour = hour;
220     tm_new.tm_min  = minute;
221     tm_new.tm_sec  = second;
222 
223     /* converts the local time into the calendar time. */
224     now = mktime(&tm_new);
225 
226     /* update to RTC device. */
227     ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);
228     return ret;
229 }
230 
231 /**
232  * Set timestamp(UTC).
233  *
234  * @param time_t timestamp
235  *
236  * @return rt_err_t if set success, return RT_EOK.
237  */
set_timestamp(time_t timestamp)238 rt_err_t set_timestamp(time_t timestamp)
239 {
240     if (_rtc_device == RT_NULL)
241     {
242         _rtc_device = rt_device_find("rtc");
243         if (_rtc_device == RT_NULL)
244         {
245             return -RT_ERROR;
246         }
247     }
248 
249     /* update to RTC device. */
250     return rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, &timestamp);
251 }
252 
253 /**
254  * Get timestamp(UTC).
255  *
256  * @param time_t* timestamp
257  *
258  * @return rt_err_t if set success, return RT_EOK.
259  */
get_timestamp(time_t * timestamp)260 rt_err_t get_timestamp(time_t *timestamp)
261 {
262     if (_rtc_device == RT_NULL)
263     {
264         _rtc_device = rt_device_find("rtc");
265         if (_rtc_device == RT_NULL)
266         {
267             return -RT_ERROR;
268         }
269     }
270 
271     /* Get timestamp from RTC device. */
272     return rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, timestamp);
273 }
274 
275 #ifdef RT_USING_FINSH
276 #include <finsh.h>
277 /**
278  * get date and time or set (local timezone) [year month day hour min sec]
279  */
date(int argc,char ** argv)280 static void date(int argc, char **argv)
281 {
282     time_t now = (time_t)0;
283 
284     if (argc == 1)
285     {
286         struct timeval tv = { 0 };
287         int32_t tz_offset_sec = 0;
288         uint32_t abs_tz_offset_sec = 0U;
289 
290 #if defined(RT_LIBC_USING_LIGHT_TZ_DST)
291         tz_offset_sec = rt_tz_get();
292 #endif /* RT_LIBC_USING_LIGHT_TZ_DST */
293 
294         gettimeofday(&tv, RT_NULL);
295         now = tv.tv_sec;
296 
297         abs_tz_offset_sec = tz_offset_sec > 0 ? tz_offset_sec : -tz_offset_sec;
298         /* output current time */
299         rt_kprintf("local time: %.*s", 25U, ctime(&now));
300         rt_kprintf("timestamps: %ld\n", (long)tv.tv_sec);
301         rt_kprintf("timezone: UTC%c%02d:%02d:%02d\n",
302             tz_offset_sec > 0 ? '+' : '-', abs_tz_offset_sec / 3600U, abs_tz_offset_sec % 3600U / 60U, abs_tz_offset_sec % 3600U % 60U);
303     }
304     else if (argc >= 7)
305     {
306         /* set time and date */
307         struct tm tm_new = {0};
308         time_t old = (time_t)0;
309         rt_err_t err;
310 
311         tm_new.tm_year = atoi(argv[1]) - 1900;
312         tm_new.tm_mon = atoi(argv[2]) - 1; /* .tm_min's range is [0-11] */
313         tm_new.tm_mday = atoi(argv[3]);
314         tm_new.tm_hour = atoi(argv[4]);
315         tm_new.tm_min = atoi(argv[5]);
316         tm_new.tm_sec = atoi(argv[6]);
317         if (tm_new.tm_year <= 0)
318         {
319             rt_kprintf("year is out of range [1900-]\n");
320             return;
321         }
322         if (tm_new.tm_mon > 11) /* .tm_min's range is [0-11] */
323         {
324             rt_kprintf("month is out of range [1-12]\n");
325             return;
326         }
327         if (tm_new.tm_mday == 0 || tm_new.tm_mday > 31)
328         {
329             rt_kprintf("day is out of range [1-31]\n");
330             return;
331         }
332         if (tm_new.tm_hour > 23)
333         {
334             rt_kprintf("hour is out of range [0-23]\n");
335             return;
336         }
337         if (tm_new.tm_min > 59)
338         {
339             rt_kprintf("minute is out of range [0-59]\n");
340             return;
341         }
342         if (tm_new.tm_sec > 60)
343         {
344             rt_kprintf("second is out of range [0-60]\n");
345             return;
346         }
347         /* save old timestamp */
348         err = get_timestamp(&old);
349         if (err != RT_EOK)
350         {
351             rt_kprintf("Get current timestamp failed. %d\n", err);
352             return;
353         }
354         /* converts the local time into the calendar time. */
355         now = mktime(&tm_new);
356         err = set_timestamp(now);
357         if (err != RT_EOK)
358         {
359             rt_kprintf("set date failed. %d\n", err);
360             return;
361         }
362         get_timestamp(&now); /* get new timestamp */
363         rt_kprintf("old: %.*s", 25, ctime(&old));
364         rt_kprintf("now: %.*s", 25, ctime(&now));
365     }
366     else
367     {
368         rt_kprintf("please input: date [year month day hour min sec] or date\n");
369         rt_kprintf("e.g: date 2018 01 01 23 59 59 or date\n");
370     }
371 }
372 MSH_CMD_EXPORT(date, get date and time or set (local timezone) [year month day hour min sec])
373 #endif /* RT_USING_FINSH */
374 
375 #endif /* RT_USING_RTC */
376