1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2020 Unisoc Communications Inc.
4f08c3bdfSopenharmony_ci */
5f08c3bdfSopenharmony_ci
6f08c3bdfSopenharmony_ci/*\
7f08c3bdfSopenharmony_ci * [Description]
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * RTC device set time function test.
10f08c3bdfSopenharmony_ci *
11f08c3bdfSopenharmony_ci * [Algorithm]
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * - Save RTC time
14f08c3bdfSopenharmony_ci * - Set RTC time
15f08c3bdfSopenharmony_ci * - Read the RTC time back
16f08c3bdfSopenharmony_ci * - Check if the set time and the read time are identical
17f08c3bdfSopenharmony_ci * - Restore RTC time
18f08c3bdfSopenharmony_ci */
19f08c3bdfSopenharmony_ci
20f08c3bdfSopenharmony_ci#include <stdio.h>
21f08c3bdfSopenharmony_ci#include "tst_rtctime.h"
22f08c3bdfSopenharmony_ci#include "tst_wallclock.h"
23f08c3bdfSopenharmony_ci#include "tst_test.h"
24f08c3bdfSopenharmony_ci
25f08c3bdfSopenharmony_cistatic char *rtc_dev = "/dev/rtc";
26f08c3bdfSopenharmony_ci
27f08c3bdfSopenharmony_cistatic char *rtctime_to_str(struct rtc_time *tm)
28f08c3bdfSopenharmony_ci{
29f08c3bdfSopenharmony_ci	static char rtctime_buf[128];
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci	sprintf(rtctime_buf, "%04d-%02d-%02d %02d:%02d:%02d",
32f08c3bdfSopenharmony_ci		tm->tm_year + 1900,
33f08c3bdfSopenharmony_ci		tm->tm_mon + 1,
34f08c3bdfSopenharmony_ci		tm->tm_mday,
35f08c3bdfSopenharmony_ci		tm->tm_hour,
36f08c3bdfSopenharmony_ci		tm->tm_min,
37f08c3bdfSopenharmony_ci		tm->tm_sec);
38f08c3bdfSopenharmony_ci	return rtctime_buf;
39f08c3bdfSopenharmony_ci}
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_cistatic int rtc_tm_cmp(struct rtc_time *set_tm, struct rtc_time *read_tm)
42f08c3bdfSopenharmony_ci{
43f08c3bdfSopenharmony_ci	long delta, seconds1, seconds2;
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci	if (set_tm->tm_year != read_tm->tm_year)
46f08c3bdfSopenharmony_ci		return 1;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci	if (set_tm->tm_mon != read_tm->tm_mon)
49f08c3bdfSopenharmony_ci		return 1;
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_ci	if (set_tm->tm_mday != read_tm->tm_mday)
52f08c3bdfSopenharmony_ci		return 1;
53f08c3bdfSopenharmony_ci
54f08c3bdfSopenharmony_ci	/*
55f08c3bdfSopenharmony_ci	 * Convert hour/min/sec into seconds to handle the normal
56f08c3bdfSopenharmony_ci	 * and special situations:
57f08c3bdfSopenharmony_ci	 * 1#
58f08c3bdfSopenharmony_ci	 *       set_tm:  2022-04-28 13:00:50
59f08c3bdfSopenharmony_ci	 *       read_tm: 2022-04-28 13:00:50
60f08c3bdfSopenharmony_ci	 * 2#
61f08c3bdfSopenharmony_ci	 *       set_tm:  2022-04-28 13:00:50
62f08c3bdfSopenharmony_ci	 *       read_tm: 2022-04-28 13:00:51
63f08c3bdfSopenharmony_ci	 * 3#
64f08c3bdfSopenharmony_ci	 *       set_tm:  2022-04-28 13:00:59
65f08c3bdfSopenharmony_ci	 *       read_tm: 2022-04-28 13:01:00
66f08c3bdfSopenharmony_ci	 * 4#
67f08c3bdfSopenharmony_ci	 *       set_tm:  2022-04-28 13:59:59
68f08c3bdfSopenharmony_ci	 *       read_tm: 2022-04-28 14:00:00
69f08c3bdfSopenharmony_ci	 *
70f08c3bdfSopenharmony_ci	 * Note: as we have avoided testing around the zero
71f08c3bdfSopenharmony_ci	 * clock, so it's impossible to hit situation 5#
72f08c3bdfSopenharmony_ci	 *       set_tm:  2022-04-28 23:59:59
73f08c3bdfSopenharmony_ci	 *       read_tm: 2022-04-29 00:00:00
74f08c3bdfSopenharmony_ci	 */
75f08c3bdfSopenharmony_ci	if ((set_tm->tm_hour != read_tm->tm_hour)
76f08c3bdfSopenharmony_ci		|| (set_tm->tm_min != read_tm->tm_min)
77f08c3bdfSopenharmony_ci		|| (set_tm->tm_sec != read_tm->tm_sec)) {
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci		seconds1 = (set_tm->tm_hour  * 3600) + (set_tm->tm_min  * 60) + set_tm->tm_sec;
80f08c3bdfSopenharmony_ci		seconds2 = (read_tm->tm_hour * 3600) + (read_tm->tm_min * 60) + read_tm->tm_sec;
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_ci		delta = seconds2 - seconds1;
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci		if (delta < 0 || delta > 3) {
85f08c3bdfSopenharmony_ci			tst_res(TFAIL, "seconds1 is %ld, seconds2 is %ld", seconds1, seconds2);
86f08c3bdfSopenharmony_ci			return 1;
87f08c3bdfSopenharmony_ci		}
88f08c3bdfSopenharmony_ci	}
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci	return 0;
91f08c3bdfSopenharmony_ci}
92f08c3bdfSopenharmony_ci
93f08c3bdfSopenharmony_cistatic void set_rtc_test(void)
94f08c3bdfSopenharmony_ci{
95f08c3bdfSopenharmony_ci	struct rtc_time read_tm, set_tm;
96f08c3bdfSopenharmony_ci	int ret;
97f08c3bdfSopenharmony_ci
98f08c3bdfSopenharmony_ci	/* Read current RTC Time */
99f08c3bdfSopenharmony_ci	ret = tst_rtc_gettime(rtc_dev, &read_tm);
100f08c3bdfSopenharmony_ci	if (ret != 0) {
101f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "ioctl() RTC_RD_TIME");
102f08c3bdfSopenharmony_ci		return;
103f08c3bdfSopenharmony_ci	}
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci	/* set rtc to +/-1 hour */
106f08c3bdfSopenharmony_ci	set_tm = read_tm;
107f08c3bdfSopenharmony_ci	if (set_tm.tm_hour == 0)
108f08c3bdfSopenharmony_ci		set_tm.tm_hour += 1;
109f08c3bdfSopenharmony_ci	else
110f08c3bdfSopenharmony_ci		set_tm.tm_hour -= 1;
111f08c3bdfSopenharmony_ci
112f08c3bdfSopenharmony_ci	tst_res(TINFO, "To set RTC date/time is: %s", rtctime_to_str(&set_tm));
113f08c3bdfSopenharmony_ci
114f08c3bdfSopenharmony_ci	ret = tst_rtc_settime(rtc_dev, &set_tm);
115f08c3bdfSopenharmony_ci	if (ret != 0) {
116f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "ioctl() RTC_SET_TIME");
117f08c3bdfSopenharmony_ci		return;
118f08c3bdfSopenharmony_ci	}
119f08c3bdfSopenharmony_ci
120f08c3bdfSopenharmony_ci	/* Read new RTC Time */
121f08c3bdfSopenharmony_ci	ret = tst_rtc_gettime(rtc_dev, &read_tm);
122f08c3bdfSopenharmony_ci	if (ret != 0) {
123f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "ioctl() RTC_RD_TIME");
124f08c3bdfSopenharmony_ci		return;
125f08c3bdfSopenharmony_ci	}
126f08c3bdfSopenharmony_ci	tst_res(TINFO, "read RTC date/time is: %s", rtctime_to_str(&read_tm));
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	if (rtc_tm_cmp(&set_tm, &read_tm)) {
129f08c3bdfSopenharmony_ci		tst_res(TFAIL, "RTC SET TEST");
130f08c3bdfSopenharmony_ci		return;
131f08c3bdfSopenharmony_ci	}
132f08c3bdfSopenharmony_ci	tst_res(TPASS, "The read RTC time is consistent with set time");
133f08c3bdfSopenharmony_ci}
134f08c3bdfSopenharmony_ci
135f08c3bdfSopenharmony_cistatic void rtc_setup(void)
136f08c3bdfSopenharmony_ci{
137f08c3bdfSopenharmony_ci	int exists = access(rtc_dev, F_OK);
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci	if (exists < 0)
140f08c3bdfSopenharmony_ci		tst_brk(TCONF, "RTC device %s not available", rtc_dev);
141f08c3bdfSopenharmony_ci
142f08c3bdfSopenharmony_ci	tst_rtc_clock_save(rtc_dev);
143f08c3bdfSopenharmony_ci}
144f08c3bdfSopenharmony_ci
145f08c3bdfSopenharmony_cistatic void rtc_cleanup(void)
146f08c3bdfSopenharmony_ci{
147f08c3bdfSopenharmony_ci	tst_rtc_clock_restore(rtc_dev);
148f08c3bdfSopenharmony_ci}
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_cistatic struct tst_test test = {
151f08c3bdfSopenharmony_ci	.setup = rtc_setup,
152f08c3bdfSopenharmony_ci	.test_all = set_rtc_test,
153f08c3bdfSopenharmony_ci	.cleanup = rtc_cleanup,
154f08c3bdfSopenharmony_ci	.needs_root = 1,
155f08c3bdfSopenharmony_ci};
156