2015-03-24 04:32:09 +08:00
|
|
|
/*
|
|
|
|
* Real Time Clock Driver Test/Example Program
|
|
|
|
*
|
|
|
|
* Compile with:
|
|
|
|
* gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest
|
|
|
|
*
|
|
|
|
* Copyright (C) 1996, Paul Gortmaker.
|
|
|
|
*
|
|
|
|
* Released under the GNU General Public License, version 2,
|
|
|
|
* included herein by reference.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <linux/rtc.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This expects the new RTC class driver framework, working with
|
|
|
|
* clocks that will often not be clones of what the PC-AT had.
|
|
|
|
* Use the command line to specify another RTC if you need one.
|
|
|
|
*/
|
|
|
|
static const char default_rtc[] = "/dev/rtc0";
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i, fd, retval, irqcount = 0;
|
|
|
|
unsigned long tmp, data;
|
|
|
|
struct rtc_time rtc_tm;
|
|
|
|
const char *rtc = default_rtc;
|
2015-03-19 03:46:33 +08:00
|
|
|
struct timeval start, end, diff;
|
2015-03-24 04:32:09 +08:00
|
|
|
|
|
|
|
switch (argc) {
|
|
|
|
case 2:
|
|
|
|
rtc = argv[1];
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "usage: rtctest [rtcdev]\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(rtc, O_RDONLY);
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
perror(rtc);
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
|
|
|
|
|
|
|
|
/* Turn on update interrupts (one per second) */
|
|
|
|
retval = ioctl(fd, RTC_UIE_ON, 0);
|
|
|
|
if (retval == -1) {
|
2015-09-16 15:16:51 +08:00
|
|
|
if (errno == EINVAL) {
|
2015-03-24 04:32:09 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"\n...Update IRQs not supported.\n");
|
|
|
|
goto test_READ;
|
|
|
|
}
|
|
|
|
perror("RTC_UIE_ON ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:",
|
|
|
|
rtc);
|
|
|
|
fflush(stderr);
|
|
|
|
for (i=1; i<6; i++) {
|
|
|
|
/* This read will block */
|
|
|
|
retval = read(fd, &data, sizeof(unsigned long));
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("read");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
fprintf(stderr, " %d",i);
|
|
|
|
fflush(stderr);
|
|
|
|
irqcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
|
|
|
|
fflush(stderr);
|
|
|
|
for (i=1; i<6; i++) {
|
|
|
|
struct timeval tv = {5, 0}; /* 5 second timeout on select */
|
|
|
|
fd_set readfds;
|
|
|
|
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
FD_SET(fd, &readfds);
|
|
|
|
/* The select will wait until an RTC interrupt happens. */
|
|
|
|
retval = select(fd+1, &readfds, NULL, NULL, &tv);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("select");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
/* This read won't block unlike the select-less case above. */
|
|
|
|
retval = read(fd, &data, sizeof(unsigned long));
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("read");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
fprintf(stderr, " %d",i);
|
|
|
|
fflush(stderr);
|
|
|
|
irqcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Turn off update interrupts */
|
|
|
|
retval = ioctl(fd, RTC_UIE_OFF, 0);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_UIE_OFF ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
test_READ:
|
|
|
|
/* Read the RTC time/date */
|
|
|
|
retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_RD_TIME ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
|
|
|
|
rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
|
|
|
|
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
|
|
|
|
|
|
|
|
/* Set the alarm to 5 sec in the future, and check for rollover */
|
|
|
|
rtc_tm.tm_sec += 5;
|
|
|
|
if (rtc_tm.tm_sec >= 60) {
|
|
|
|
rtc_tm.tm_sec %= 60;
|
|
|
|
rtc_tm.tm_min++;
|
|
|
|
}
|
|
|
|
if (rtc_tm.tm_min == 60) {
|
|
|
|
rtc_tm.tm_min = 0;
|
|
|
|
rtc_tm.tm_hour++;
|
|
|
|
}
|
|
|
|
if (rtc_tm.tm_hour == 24)
|
|
|
|
rtc_tm.tm_hour = 0;
|
|
|
|
|
|
|
|
retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
|
|
|
|
if (retval == -1) {
|
rtc: rtctest: Change alarm IRQ support detection
For old style drivers, call a call to ioctl(..., RTC_ALM_SET, ...):
- char/ds1302.c will always return -EINVAL
- char/genrtc.c: will always return -EINVAL
- char/rtc.c will succeed regardless if IRQs are supported or not
- char/efirtc.c will always return -EINVAL
- input/misc/hp_sdc_rtc.c ... that ioctl code is a good lesson about
ifdefing code out and punting implementation ... and it will
always return -EINVAL
For new style rtc drivers, a call to ioctl(..., RTC_ALM_SET, ...) never
results in a call to __rtc_set_alarm, since struct rtc_wkalarm passed to
rtc_set_alarm has 'enabled' field set to 0. This means that
rtc->ops->set_alarm driver hook is never called in that ioctl. Since no
driver code interaction happens as a part of that call, using its
results to ascertain properties of the driver is not going to work. To
remedy this - use the result of RTC_AIE_ON to make the judgement.
This patch also changes ENOTTY to EINVAL as an error code value that
would tell us that IRQs are not supported. There are three reason for
this:
- As mentioned above old style driver never returns ENOTTY for this
ioctl
- In it's code __rtc_set_alarm() returns -EINVAL if rtc->ops->set_alarm
method is not provided by the driver, so one reason for change is to
be consistent with that code path.
- A call to ioctl(..., RTC_UIE_ON, ...) will result in a call to
rtc_update_irq_enable() and then __rtc_set_alarm(), which, if IRQs
are not supported by the driver, will result in a non-zero error
code. Returning ENOTTY in that case would:
a) Not be consistent with other codepaths of
rtc_update_irq_enable, for example the check of
rtc->uie_unsupported
b) Would break update IRQ emulation code since that codpath
expects EINVAL
c) Would break test's logic for feature support detection in
the case of RTC_UIE_ON ioctl
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2016-06-21 15:22:48 +08:00
|
|
|
if (errno == EINVAL) {
|
2015-03-24 04:32:09 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"\n...Alarm IRQs not supported.\n");
|
|
|
|
goto test_PIE;
|
|
|
|
}
|
rtc: rtctest: Change alarm IRQ support detection
For old style drivers, call a call to ioctl(..., RTC_ALM_SET, ...):
- char/ds1302.c will always return -EINVAL
- char/genrtc.c: will always return -EINVAL
- char/rtc.c will succeed regardless if IRQs are supported or not
- char/efirtc.c will always return -EINVAL
- input/misc/hp_sdc_rtc.c ... that ioctl code is a good lesson about
ifdefing code out and punting implementation ... and it will
always return -EINVAL
For new style rtc drivers, a call to ioctl(..., RTC_ALM_SET, ...) never
results in a call to __rtc_set_alarm, since struct rtc_wkalarm passed to
rtc_set_alarm has 'enabled' field set to 0. This means that
rtc->ops->set_alarm driver hook is never called in that ioctl. Since no
driver code interaction happens as a part of that call, using its
results to ascertain properties of the driver is not going to work. To
remedy this - use the result of RTC_AIE_ON to make the judgement.
This patch also changes ENOTTY to EINVAL as an error code value that
would tell us that IRQs are not supported. There are three reason for
this:
- As mentioned above old style driver never returns ENOTTY for this
ioctl
- In it's code __rtc_set_alarm() returns -EINVAL if rtc->ops->set_alarm
method is not provided by the driver, so one reason for change is to
be consistent with that code path.
- A call to ioctl(..., RTC_UIE_ON, ...) will result in a call to
rtc_update_irq_enable() and then __rtc_set_alarm(), which, if IRQs
are not supported by the driver, will result in a non-zero error
code. Returning ENOTTY in that case would:
a) Not be consistent with other codepaths of
rtc_update_irq_enable, for example the check of
rtc->uie_unsupported
b) Would break update IRQ emulation code since that codpath
expects EINVAL
c) Would break test's logic for feature support detection in
the case of RTC_UIE_ON ioctl
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2016-06-21 15:22:48 +08:00
|
|
|
|
2015-03-24 04:32:09 +08:00
|
|
|
perror("RTC_ALM_SET ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the current alarm settings */
|
|
|
|
retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_ALM_READ ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",
|
|
|
|
rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
|
|
|
|
|
|
|
|
/* Enable alarm interrupts */
|
|
|
|
retval = ioctl(fd, RTC_AIE_ON, 0);
|
|
|
|
if (retval == -1) {
|
rtc: rtctest: Change alarm IRQ support detection
For old style drivers, call a call to ioctl(..., RTC_ALM_SET, ...):
- char/ds1302.c will always return -EINVAL
- char/genrtc.c: will always return -EINVAL
- char/rtc.c will succeed regardless if IRQs are supported or not
- char/efirtc.c will always return -EINVAL
- input/misc/hp_sdc_rtc.c ... that ioctl code is a good lesson about
ifdefing code out and punting implementation ... and it will
always return -EINVAL
For new style rtc drivers, a call to ioctl(..., RTC_ALM_SET, ...) never
results in a call to __rtc_set_alarm, since struct rtc_wkalarm passed to
rtc_set_alarm has 'enabled' field set to 0. This means that
rtc->ops->set_alarm driver hook is never called in that ioctl. Since no
driver code interaction happens as a part of that call, using its
results to ascertain properties of the driver is not going to work. To
remedy this - use the result of RTC_AIE_ON to make the judgement.
This patch also changes ENOTTY to EINVAL as an error code value that
would tell us that IRQs are not supported. There are three reason for
this:
- As mentioned above old style driver never returns ENOTTY for this
ioctl
- In it's code __rtc_set_alarm() returns -EINVAL if rtc->ops->set_alarm
method is not provided by the driver, so one reason for change is to
be consistent with that code path.
- A call to ioctl(..., RTC_UIE_ON, ...) will result in a call to
rtc_update_irq_enable() and then __rtc_set_alarm(), which, if IRQs
are not supported by the driver, will result in a non-zero error
code. Returning ENOTTY in that case would:
a) Not be consistent with other codepaths of
rtc_update_irq_enable, for example the check of
rtc->uie_unsupported
b) Would break update IRQ emulation code since that codpath
expects EINVAL
c) Would break test's logic for feature support detection in
the case of RTC_UIE_ON ioctl
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
2016-06-21 15:22:48 +08:00
|
|
|
if (errno == EINVAL) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"\n...Alarm IRQs not supported.\n");
|
|
|
|
goto test_PIE;
|
|
|
|
}
|
|
|
|
|
2015-03-24 04:32:09 +08:00
|
|
|
perror("RTC_AIE_ON ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Waiting 5 seconds for alarm...");
|
|
|
|
fflush(stderr);
|
|
|
|
/* This blocks until the alarm ring causes an interrupt */
|
|
|
|
retval = read(fd, &data, sizeof(unsigned long));
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("read");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
irqcount++;
|
|
|
|
fprintf(stderr, " okay. Alarm rang.\n");
|
|
|
|
|
|
|
|
/* Disable alarm interrupts */
|
|
|
|
retval = ioctl(fd, RTC_AIE_OFF, 0);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_AIE_OFF ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
test_PIE:
|
|
|
|
/* Read periodic IRQ rate */
|
|
|
|
retval = ioctl(fd, RTC_IRQP_READ, &tmp);
|
|
|
|
if (retval == -1) {
|
|
|
|
/* not all RTCs support periodic IRQs */
|
2016-06-21 15:22:49 +08:00
|
|
|
if (errno == EINVAL) {
|
2015-03-24 04:32:09 +08:00
|
|
|
fprintf(stderr, "\nNo periodic IRQ support\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
perror("RTC_IRQP_READ ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp);
|
|
|
|
|
|
|
|
fprintf(stderr, "Counting 20 interrupts at:");
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
/* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
|
|
|
|
for (tmp=2; tmp<=64; tmp*=2) {
|
|
|
|
|
|
|
|
retval = ioctl(fd, RTC_IRQP_SET, tmp);
|
|
|
|
if (retval == -1) {
|
|
|
|
/* not all RTCs can change their periodic IRQ rate */
|
2016-06-21 15:22:50 +08:00
|
|
|
if (errno == EINVAL) {
|
2015-03-24 04:32:09 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"\n...Periodic IRQ rate is fixed\n");
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
perror("RTC_IRQP_SET ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "\n%ldHz:\t", tmp);
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
/* Enable periodic interrupts */
|
|
|
|
retval = ioctl(fd, RTC_PIE_ON, 0);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_PIE_ON ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=1; i<21; i++) {
|
2015-03-19 03:46:33 +08:00
|
|
|
gettimeofday(&start, NULL);
|
2015-03-24 04:32:09 +08:00
|
|
|
/* This blocks */
|
|
|
|
retval = read(fd, &data, sizeof(unsigned long));
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("read");
|
|
|
|
exit(errno);
|
|
|
|
}
|
2015-03-19 03:46:33 +08:00
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
timersub(&end, &start, &diff);
|
|
|
|
if (diff.tv_sec > 0 ||
|
|
|
|
diff.tv_usec > ((1000000L / tmp) * 1.10)) {
|
|
|
|
fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n",
|
|
|
|
diff.tv_sec, diff.tv_usec,
|
|
|
|
(1000000L / tmp));
|
|
|
|
fflush(stdout);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2015-03-24 04:32:09 +08:00
|
|
|
fprintf(stderr, " %d",i);
|
|
|
|
fflush(stderr);
|
|
|
|
irqcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable periodic interrupts */
|
|
|
|
retval = ioctl(fd, RTC_PIE_OFF, 0);
|
|
|
|
if (retval == -1) {
|
|
|
|
perror("RTC_PIE_OFF ioctl");
|
|
|
|
exit(errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|