1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-04-13     armink       the first version
9  * 2022-05-17     Meco Man     improve and beautify
10  */
11 
12 #include <sys/time.h>
13 #include <rtdevice.h>
14 
15 static struct rt_device rtc_dev;
16 
17 #ifdef BSP_USING_ALARM
18 static struct rt_rtc_wkalarm wkalarm;
19 static struct rt_timer alarm_time;
20 
alarm_timeout(void * param)21 static void alarm_timeout(void *param)
22 {
23     rt_alarm_update(param, 1);
24 }
25 
soft_rtc_alarm_update(struct rt_rtc_wkalarm * palarm)26 static void soft_rtc_alarm_update(struct rt_rtc_wkalarm *palarm)
27 {
28     rt_tick_t next_tick;
29 
30     if (palarm->enable)
31     {
32         next_tick = RT_TICK_PER_SECOND;
33         rt_timer_control(&alarm_time, RT_TIMER_CTRL_SET_TIME, &next_tick);
34         rt_timer_start(&alarm_time);
35     }
36     else
37     {
38         rt_timer_stop(&alarm_time);
39     }
40 }
41 #endif /* BSP_USING_ALARM */
42 
get_rtc_timeval(struct timeval * tv)43 static void get_rtc_timeval(struct timeval *tv)
44 {
45 #ifdef _WIN32
46     struct tm newtime = { 0 };
47     SYSTEMTIME sys_time;
48 
49     GetSystemTime(&sys_time); /* get RTC from Windows */
50 
51     newtime.tm_year = sys_time.wYear - 1900;
52     newtime.tm_mon = sys_time.wMonth - 1;
53     newtime.tm_mday = sys_time.wDay;
54     newtime.tm_hour = sys_time.wHour;
55     newtime.tm_min = sys_time.wMinute;
56     newtime.tm_sec = sys_time.wSecond;
57 
58     tv->tv_sec = timegm(&newtime);
59     tv->tv_usec = sys_time.wMilliseconds * 1000UL;
60 #else
61     tv->tv_sec = 0;
62     tv->tv_usec = 0;
63 #endif
64 }
65 
pc_rtc_control(rt_device_t dev,int cmd,void * args)66 static rt_err_t pc_rtc_control(rt_device_t dev, int cmd, void *args)
67 {
68     RT_ASSERT(dev != RT_NULL);
69 
70     switch (cmd)
71     {
72     case RT_DEVICE_CTRL_RTC_GET_TIME:
73     {
74         struct timeval tv;
75         get_rtc_timeval(&tv);
76         *(time_t*) args = tv.tv_sec;
77         break;
78     }
79     case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
80     {
81         get_rtc_timeval((struct timeval *) args);
82         break;
83     }
84 #ifdef BSP_USING_ALARM
85     case RT_DEVICE_CTRL_RTC_SET_TIME:
86     {
87         soft_rtc_alarm_update(&wkalarm);
88         break;
89     }
90     case RT_DEVICE_CTRL_RTC_GET_ALARM:
91     {
92         *((struct rt_rtc_wkalarm *)args) = wkalarm;
93         break;
94     }
95     case RT_DEVICE_CTRL_RTC_SET_ALARM:
96     {
97         wkalarm = *((struct rt_rtc_wkalarm *)args);
98         soft_rtc_alarm_update(&wkalarm);
99         break;
100     }
101 #endif /* BSP_USING_ALARM */
102     default:
103         return -RT_ERROR;
104     }
105 
106     return RT_EOK;
107 }
108 
109 #ifdef RT_USING_DEVICE_OPS
110 const static struct rt_device_ops pc_rtc_ops =
111 {
112     RT_NULL,
113     RT_NULL,
114     RT_NULL,
115     RT_NULL,
116     RT_NULL,
117     pc_rtc_control
118 };
119 #endif
120 
rt_pc_rtc_init(void)121 int rt_pc_rtc_init(void)
122 {
123     /* make sure only one 'rtc' device */
124     RT_ASSERT(!rt_device_find("rtc"));
125 
126 #ifdef BSP_USING_ALARM
127     rt_timer_init(&alarm_time,
128         "alarm",
129         alarm_timeout,
130         &rtc_dev,
131         0,
132         RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT);
133 #endif
134 
135     rtc_dev.type = RT_Device_Class_RTC;
136 
137     /* register rtc device */
138 #ifdef RT_USING_DEVICE_OPS
139     rtc_dev.ops = &pc_rtc_ops;
140 #else
141     rtc_dev.init = RT_NULL;
142     rtc_dev.open = RT_NULL;
143     rtc_dev.close = RT_NULL;
144     rtc_dev.read = RT_NULL;
145     rtc_dev.write = RT_NULL;
146     rtc_dev.control = pc_rtc_control;
147 #endif
148     rtc_dev.user_data = RT_NULL; /* no private */
149 
150     rt_device_register(&rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);
151     return 0;
152 }
153 INIT_BOARD_EXPORT(rt_pc_rtc_init);
154