Message ID | 20201223033517.1464263-2-gengcixi@gmail.com |
---|---|
State | Changes Requested |
Headers | show |
Series | add rtctime libs and rtc02 case | expand |
Hi! > get rtc time and set rtc time in default /dev/rtc; > Implemented a function that covert rtc time to time_t > this will be used in tst_rtc_save and tst_rtc_restore > > Signed-off-by: Cixi Geng <cixi.geng1@unisoc.com> > --- > include/tst_rtctime.h | 15 ++++ > lib/tst_rtctime.c | 161 ++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 176 insertions(+) > create mode 100644 include/tst_rtctime.h > create mode 100644 lib/tst_rtctime.c > > diff --git a/include/tst_rtctime.h b/include/tst_rtctime.h > new file mode 100644 > index 000000000..61ec6f0eb > --- /dev/null > +++ b/include/tst_rtctime.h > @@ -0,0 +1,15 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ This is not correct format for the identifiers, the comment has to start with // Also the default license for new LTP code i GPL-2.0-or-later, so please us that unless you have a reason to stick to GPL-2.0. > +/* > + * Copyright (C) 2020 Unisoc Inc. > + */ > + > +#ifndef TST_RTCTIME > +#define TST_RTCTIME > + > +#include <linux/rtc.h> > + > +int tst_rtc_gettime(struct rtc_time *rtc_tm); > + > +int tst_rtc_settime(struct rtc_time *rtc_tm); Should we add a path to the RTC device to these functions as well? Are there any boards that have more than one RTC where we would need to loop over all available devices during the test? > +#endif /* TST_RTCTIME */ > diff --git a/lib/tst_rtctime.c b/lib/tst_rtctime.c > new file mode 100644 > index 000000000..580ef0fdf > --- /dev/null > +++ b/lib/tst_rtctime.c > @@ -0,0 +1,161 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020 Unisoc Communications Inc. > + * > + * Filename : tst_rtctime.c > + * Abstract : This file is a implementation for rtc set read,covert to tm functions > + */ > + > +#include <linux/rtc.h> > +#include <stdbool.h> > +#include <limits.h> > +#define TST_NO_DEFAULT_MAIN > +#include "tst_test.h" > +#include "tst_rtctime.h" > + > +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) > + > +static const unsigned char rtc_days_in_month[] = { > + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 > +}; > + > +static inline bool is_leap_year(unsigned int year) > +{ > + return (!(year % 4) && (year % 100)) || !(year % 400); > +} > + > +static long long tst_mktime(const unsigned int year0, const unsigned int mon0, > + const unsigned int day, const unsigned int hour, > + const unsigned int min, const unsigned int sec) > +{ > + unsigned int mon = mon0, year = year0; > + > + /* 1..12 -> 11,12,1..10 */ > + mon -= 2; > + if (0 >= (int) (mon)) { > + mon += 12; /* Puts Feb last since it has leap day */ > + year -= 1; > + } > + > + return ((((long long) > + (year/4 - year/100 + year/400 + 367*mon/12 + day) + > + year*365 - 719499 > + )*24 + hour /* now have hours - midnight tomorrow handled here */ > + )*60 + min /* now have minutes */ > + )*60 + sec; /* finally seconds */ > +} > + > +/* > + * The number of days in the month. > + */ > +static int rtc_month_days(unsigned int month, unsigned int year) > +{ > + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); > +} > + > +/* > + * tst_rtc_time_to_tm - Converts time_t to rtc_time. > + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. > + */ > +void tst_rtc_time_to_tm(long long time, struct rtc_time *tm) > +{ > + unsigned int month, year, secs; > + int days; > + > + /* time must be positive */ > + days = time / 86400; > + secs = time % 86400; > + > + /* day of the week, 1970-01-01 was a Thursday */ > + tm->tm_wday = (days + 4) % 7; > + > + year = 1970 + days / 365; > + days -= (year - 1970) * 365 > + + LEAPS_THRU_END_OF(year - 1) > + - LEAPS_THRU_END_OF(1970 - 1); > + while (days < 0) { > + year -= 1; > + days += 365 + is_leap_year(year); > + } > + tm->tm_year = year - 1900; > + tm->tm_yday = days + 1; > + > + for (month = 0; month < 11; month++) { > + int newdays; > + > + newdays = days - rtc_month_days(month, year); > + if (newdays < 0) > + break; > + days = newdays; > + } > + tm->tm_mon = month; > + tm->tm_mday = days + 1; > + > + tm->tm_hour = secs / 3600; > + secs -= tm->tm_hour * 3600; > + tm->tm_min = secs / 60; > + tm->tm_sec = secs - tm->tm_min * 60; > + > + tm->tm_isdst = 0; > +} > + > +/* > + * tst_rtc_tm_to_time - Converts rtc_time to time_t. > + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. > + */ > +long long tst_rtc_tm_to_time(struct rtc_time *tm) > +{ > + return tst_mktime(((unsigned int)tm->tm_year + 1900), tm->tm_mon + 1, > + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); So I guess that the reason why we can't use libc mktime() and gmtime_r() is that time_t is 32bit on older 32bit architectures, right? > +} > + > +static int rtc_supported_by_kernel(const char *rtc_dev) > +{ > + int exists = access(rtc_dev, F_OK); > + > + if (exists < 0) > + tst_brk(TCONF, "RTC device %s not available", rtc_dev); > + return 0; > +} This function should be called by the tests rather than from the test library. > +static int tst_rtc_ioctl(unsigned long request, struct rtc_time *rtc_tm) > +{ > + int ret; > + int rtc_fd = -1; > + static const char *rtc_dev = "/dev/rtc"; > + > + if (!rtc_supported_by_kernel(rtc_dev)) > + rtc_fd = SAFE_OPEN(rtc_dev, O_RDONLY); > + > + ret = ioctl(rtc_fd, request, rtc_tm); > + > + if (ret != 0) > + return -1; > + > + if (rtc_fd > 0) > + SAFE_CLOSE(rtc_fd); > + > + return 0; > +} > + > +int tst_rtc_gettime(struct rtc_time *rtc_tm) > +{ > + int ret; > + > + ret = tst_rtc_ioctl(RTC_RD_TIME, rtc_tm); > + > + if (ret != 0) > + return -1; > + return 0; > +} > + > +int tst_rtc_settime(struct rtc_time *rtc_tm) > +{ > + int ret; > + > + ret = tst_rtc_ioctl(RTC_SET_TIME, rtc_tm); > + > + if (ret != 0) > + return -1; > + return 0; > +} These two functions can be static inline and declared in the header as: static inline int tst_rtc_settime(struct rtc_time *rtc_tm) { return tst_rtc_ioctl(RTC_SET_TIME, rtc_tm); }
Cyril Hrubis <chrubis@suse.cz> 于2021年1月7日周四 下午9:49写道: > > Hi! > > get rtc time and set rtc time in default /dev/rtc; > > Implemented a function that covert rtc time to time_t > > this will be used in tst_rtc_save and tst_rtc_restore > > > > Signed-off-by: Cixi Geng <cixi.geng1@unisoc.com> > > --- > > include/tst_rtctime.h | 15 ++++ > > lib/tst_rtctime.c | 161 ++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 176 insertions(+) > > create mode 100644 include/tst_rtctime.h > > create mode 100644 lib/tst_rtctime.c > > > > diff --git a/include/tst_rtctime.h b/include/tst_rtctime.h > > new file mode 100644 > > index 000000000..61ec6f0eb > > --- /dev/null > > +++ b/include/tst_rtctime.h > > @@ -0,0 +1,15 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > This is not correct format for the identifiers, the comment has to start > with // > > Also the default license for new LTP code i GPL-2.0-or-later, so please > us that unless you have a reason to stick to GPL-2.0. > > > +/* > > + * Copyright (C) 2020 Unisoc Inc. > > + */ > > + > > +#ifndef TST_RTCTIME > > +#define TST_RTCTIME > > + > > +#include <linux/rtc.h> > > + > > +int tst_rtc_gettime(struct rtc_time *rtc_tm); > > + > > +int tst_rtc_settime(struct rtc_time *rtc_tm); > > Should we add a path to the RTC device to these functions as well? > > Are there any boards that have more than one RTC where we would need to > loop over all available devices during the test? I test this case in x86_64 and arm64, in these machines, I check the /dev/rtc is a linked file which link to /dev/rtc0. so I use the /dev/rtc0 as default device. but I wiil take adopt your suggestion, add a device path parameter. > > > +#endif /* TST_RTCTIME */ > > diff --git a/lib/tst_rtctime.c b/lib/tst_rtctime.c > > new file mode 100644 > > index 000000000..580ef0fdf > > --- /dev/null > > +++ b/lib/tst_rtctime.c > > @@ -0,0 +1,161 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (C) 2020 Unisoc Communications Inc. > > + * > > + * Filename : tst_rtctime.c > > + * Abstract : This file is a implementation for rtc set read,covert to tm functions > > + */ > > + > > +#include <linux/rtc.h> > > +#include <stdbool.h> > > +#include <limits.h> > > +#define TST_NO_DEFAULT_MAIN > > +#include "tst_test.h" > > +#include "tst_rtctime.h" > > + > > +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) > > + > > +static const unsigned char rtc_days_in_month[] = { > > + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 > > +}; > > + > > +static inline bool is_leap_year(unsigned int year) > > +{ > > + return (!(year % 4) && (year % 100)) || !(year % 400); > > +} > > + > > +static long long tst_mktime(const unsigned int year0, const unsigned int mon0, > > + const unsigned int day, const unsigned int hour, > > + const unsigned int min, const unsigned int sec) > > +{ > > + unsigned int mon = mon0, year = year0; > > + > > + /* 1..12 -> 11,12,1..10 */ > > + mon -= 2; > > + if (0 >= (int) (mon)) { > > + mon += 12; /* Puts Feb last since it has leap day */ > > + year -= 1; > > + } > > + > > + return ((((long long) > > + (year/4 - year/100 + year/400 + 367*mon/12 + day) + > > + year*365 - 719499 > > + )*24 + hour /* now have hours - midnight tomorrow handled here */ > > + )*60 + min /* now have minutes */ > > + )*60 + sec; /* finally seconds */ > > +} > > + > > +/* > > + * The number of days in the month. > > + */ > > +static int rtc_month_days(unsigned int month, unsigned int year) > > +{ > > + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); > > +} > > + > > +/* > > + * tst_rtc_time_to_tm - Converts time_t to rtc_time. > > + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. > > + */ > > +void tst_rtc_time_to_tm(long long time, struct rtc_time *tm) > > +{ > > + unsigned int month, year, secs; > > + int days; > > + > > + /* time must be positive */ > > + days = time / 86400; > > + secs = time % 86400; > > + > > + /* day of the week, 1970-01-01 was a Thursday */ > > + tm->tm_wday = (days + 4) % 7; > > + > > + year = 1970 + days / 365; > > + days -= (year - 1970) * 365 > > + + LEAPS_THRU_END_OF(year - 1) > > + - LEAPS_THRU_END_OF(1970 - 1); > > + while (days < 0) { > > + year -= 1; > > + days += 365 + is_leap_year(year); > > + } > > + tm->tm_year = year - 1900; > > + tm->tm_yday = days + 1; > > + > > + for (month = 0; month < 11; month++) { > > + int newdays; > > + > > + newdays = days - rtc_month_days(month, year); > > + if (newdays < 0) > > + break; > > + days = newdays; > > + } > > + tm->tm_mon = month; > > + tm->tm_mday = days + 1; > > + > > + tm->tm_hour = secs / 3600; > > + secs -= tm->tm_hour * 3600; > > + tm->tm_min = secs / 60; > > + tm->tm_sec = secs - tm->tm_min * 60; > > + > > + tm->tm_isdst = 0; > > +} > > + > > +/* > > + * tst_rtc_tm_to_time - Converts rtc_time to time_t. > > + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. > > + */ > > +long long tst_rtc_tm_to_time(struct rtc_time *tm) > > +{ > > + return tst_mktime(((unsigned int)tm->tm_year + 1900), tm->tm_mon + 1, > > + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); > > So I guess that the reason why we can't use libc mktime() and gmtime_r() > is that time_t is 32bit on older 32bit architectures, right? these functions is porting int kernel rtc-lib.c rtc_tm_to_time64() which defaut retrun is time64_t, mktime64. so I convert time64_t to long long type > > > +} > > + > > +static int rtc_supported_by_kernel(const char *rtc_dev) > > +{ > > + int exists = access(rtc_dev, F_OK); > > + > > + if (exists < 0) > > + tst_brk(TCONF, "RTC device %s not available", rtc_dev); > > + return 0; > > +} > > This function should be called by the tests rather than from the test > library. next version modify it. > > > +static int tst_rtc_ioctl(unsigned long request, struct rtc_time *rtc_tm) > > +{ > > + int ret; > > + int rtc_fd = -1; > > + static const char *rtc_dev = "/dev/rtc"; > > + > > + if (!rtc_supported_by_kernel(rtc_dev)) > > + rtc_fd = SAFE_OPEN(rtc_dev, O_RDONLY); > > + > > + ret = ioctl(rtc_fd, request, rtc_tm); > > + > > + if (ret != 0) > > + return -1; > > + > > + if (rtc_fd > 0) > > + SAFE_CLOSE(rtc_fd); > > + > > + return 0; > > +} > > + > > +int tst_rtc_gettime(struct rtc_time *rtc_tm) > > +{ > > + int ret; > > + > > + ret = tst_rtc_ioctl(RTC_RD_TIME, rtc_tm); > > + > > + if (ret != 0) > > + return -1; > > + return 0; > > +} > > + > > +int tst_rtc_settime(struct rtc_time *rtc_tm) > > +{ > > + int ret; > > + > > + ret = tst_rtc_ioctl(RTC_SET_TIME, rtc_tm); > > + > > + if (ret != 0) > > + return -1; > > + return 0; > > +} > > These two functions can be static inline and declared in the header as: > > > static inline int tst_rtc_settime(struct rtc_time *rtc_tm) > { > return tst_rtc_ioctl(RTC_SET_TIME, rtc_tm); > } > > -- > Cyril Hrubis > chrubis@suse.cz
diff --git a/include/tst_rtctime.h b/include/tst_rtctime.h new file mode 100644 index 000000000..61ec6f0eb --- /dev/null +++ b/include/tst_rtctime.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Unisoc Inc. + */ + +#ifndef TST_RTCTIME +#define TST_RTCTIME + +#include <linux/rtc.h> + +int tst_rtc_gettime(struct rtc_time *rtc_tm); + +int tst_rtc_settime(struct rtc_time *rtc_tm); + +#endif /* TST_RTCTIME */ diff --git a/lib/tst_rtctime.c b/lib/tst_rtctime.c new file mode 100644 index 000000000..580ef0fdf --- /dev/null +++ b/lib/tst_rtctime.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Unisoc Communications Inc. + * + * Filename : tst_rtctime.c + * Abstract : This file is a implementation for rtc set read,covert to tm functions + */ + +#include <linux/rtc.h> +#include <stdbool.h> +#include <limits.h> +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_rtctime.h" + +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const unsigned char rtc_days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static inline bool is_leap_year(unsigned int year) +{ + return (!(year % 4) && (year % 100)) || !(year % 400); +} + +static long long tst_mktime(const unsigned int year0, const unsigned int mon0, + const unsigned int day, const unsigned int hour, + const unsigned int min, const unsigned int sec) +{ + unsigned int mon = mon0, year = year0; + + /* 1..12 -> 11,12,1..10 */ + mon -= 2; + if (0 >= (int) (mon)) { + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + + return ((((long long) + (year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours - midnight tomorrow handled here */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +/* + * The number of days in the month. + */ +static int rtc_month_days(unsigned int month, unsigned int year) +{ + return rtc_days_in_month[month] + (is_leap_year(year) && month == 1); +} + +/* + * tst_rtc_time_to_tm - Converts time_t to rtc_time. + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. + */ +void tst_rtc_time_to_tm(long long time, struct rtc_time *tm) +{ + unsigned int month, year, secs; + int days; + + /* time must be positive */ + days = time / 86400; + secs = time % 86400; + + /* day of the week, 1970-01-01 was a Thursday */ + tm->tm_wday = (days + 4) % 7; + + year = 1970 + days / 365; + days -= (year - 1970) * 365 + + LEAPS_THRU_END_OF(year - 1) + - LEAPS_THRU_END_OF(1970 - 1); + while (days < 0) { + year -= 1; + days += 365 + is_leap_year(year); + } + tm->tm_year = year - 1900; + tm->tm_yday = days + 1; + + for (month = 0; month < 11; month++) { + int newdays; + + newdays = days - rtc_month_days(month, year); + if (newdays < 0) + break; + days = newdays; + } + tm->tm_mon = month; + tm->tm_mday = days + 1; + + tm->tm_hour = secs / 3600; + secs -= tm->tm_hour * 3600; + tm->tm_min = secs / 60; + tm->tm_sec = secs - tm->tm_min * 60; + + tm->tm_isdst = 0; +} + +/* + * tst_rtc_tm_to_time - Converts rtc_time to time_t. + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +long long tst_rtc_tm_to_time(struct rtc_time *tm) +{ + return tst_mktime(((unsigned int)tm->tm_year + 1900), tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static int rtc_supported_by_kernel(const char *rtc_dev) +{ + int exists = access(rtc_dev, F_OK); + + if (exists < 0) + tst_brk(TCONF, "RTC device %s not available", rtc_dev); + return 0; +} + +static int tst_rtc_ioctl(unsigned long request, struct rtc_time *rtc_tm) +{ + int ret; + int rtc_fd = -1; + static const char *rtc_dev = "/dev/rtc"; + + if (!rtc_supported_by_kernel(rtc_dev)) + rtc_fd = SAFE_OPEN(rtc_dev, O_RDONLY); + + ret = ioctl(rtc_fd, request, rtc_tm); + + if (ret != 0) + return -1; + + if (rtc_fd > 0) + SAFE_CLOSE(rtc_fd); + + return 0; +} + +int tst_rtc_gettime(struct rtc_time *rtc_tm) +{ + int ret; + + ret = tst_rtc_ioctl(RTC_RD_TIME, rtc_tm); + + if (ret != 0) + return -1; + return 0; +} + +int tst_rtc_settime(struct rtc_time *rtc_tm) +{ + int ret; + + ret = tst_rtc_ioctl(RTC_SET_TIME, rtc_tm); + + if (ret != 0) + return -1; + return 0; +}