1 /*
2  *      Real Time Clock Driver Test/Example Program
3  *
4  *      Compile with:
5  *      gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest
6  *
7  *      Copyright (C) 1996, Paul Gortmaker.
8  *      Copyright (C) 2010, Jason Wang <jasowang@redhat.com>
9  *
10  *      Released under the GNU General Public License, version 2,
11  *      included herein by reference.
12  *
13  */
14 
15 #include <stdio.h>
16 #include <linux/rtc.h>
17 #include <sys/ioctl.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 
25 
26 /*
27  * This expects the new RTC class driver framework, working with
28  * clocks that will often not be clones of what the PC-AT had.
29  * Use the command line to specify another RTC if you need one.
30  */
31 static const char default_rtc[] = "/dev/rtc0";
32 static int maxfreq = 64;
33 
main(int argc,char ** argv)34 int main(int argc, char **argv)
35 {
36 	int i, fd, retval, irqcount = 0;
37 	unsigned long tmp, data;
38 	struct rtc_time rtc_tm;
39 	const char *rtc = default_rtc;
40 
41 	switch (argc) {
42 	case 3:
43 		maxfreq = atoi(argv[2]);
44 	case 2:
45 		rtc = argv[1];
46 		/* FALLTHROUGH */
47 	case 1:
48 		break;
49 	default:
50 		fprintf(stderr, "usage:  rtctest [rtcdev] [maxfreq]\n");
51 		return 1;
52 	}
53 
54 	fd = open(rtc, O_RDONLY);
55 
56 	if (fd ==  -1) {
57 		perror(rtc);
58 		exit(errno);
59 	}
60 
61 	fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
62 
63 	/* Turn on update interrupts (one per second) */
64 	retval = ioctl(fd, RTC_UIE_ON, 0);
65 	if (retval == -1) {
66 		if (errno == ENOTTY) {
67 			fprintf(stderr,
68 				"\n...Update IRQs not supported.\n");
69 			goto test_READ;
70 		}
71 		perror("RTC_UIE_ON ioctl");
72 		exit(errno);
73 	}
74 
75 	fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:",
76 			rtc);
77 	fflush(stderr);
78 	for (i=1; i<6; i++) {
79 		/* This read will block */
80 		retval = read(fd, &data, sizeof(unsigned long));
81 		if (retval == -1) {
82 			perror("read");
83 			exit(errno);
84 		}
85 		fprintf(stderr, " %d", i);
86 		fflush(stderr);
87 		irqcount++;
88 	}
89 
90 	fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
91 	fflush(stderr);
92 	for (i=1; i<6; i++) {
93 		struct timeval tv = {5, 0};     /* 5 second timeout on select */
94 		fd_set readfds;
95 
96 		FD_ZERO(&readfds);
97 		FD_SET(fd, &readfds);
98 		/* The select will wait until an RTC interrupt happens. */
99 		retval = select(fd+1, &readfds, NULL, NULL, &tv);
100 		if (retval == -1) {
101 				perror("select");
102 				exit(errno);
103 		}
104 		/* This read won't block unlike the select-less case above. */
105 		retval = read(fd, &data, sizeof(unsigned long));
106 		if (retval == -1) {
107 				perror("read");
108 				exit(errno);
109 		}
110 		fprintf(stderr, " %d", i);
111 		fflush(stderr);
112 		irqcount++;
113 	}
114 
115 	/* Turn off update interrupts */
116 	retval = ioctl(fd, RTC_UIE_OFF, 0);
117 	if (retval == -1) {
118 		perror("RTC_UIE_OFF ioctl");
119 		exit(errno);
120 	}
121 
122 test_READ:
123 	/* Read the RTC time/date */
124 	retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
125 	if (retval == -1) {
126 		perror("RTC_RD_TIME ioctl");
127 		exit(errno);
128 	}
129 
130 	fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
131 		rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
132 		rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
133 
134 	/* Set the alarm to 5 sec in the future, and check for rollover */
135 	rtc_tm.tm_sec += 5;
136 	if (rtc_tm.tm_sec >= 60) {
137 		rtc_tm.tm_sec %= 60;
138 		rtc_tm.tm_min++;
139 	}
140 	if (rtc_tm.tm_min == 60) {
141 		rtc_tm.tm_min = 0;
142 		rtc_tm.tm_hour++;
143 	}
144 	if (rtc_tm.tm_hour == 24)
145 		rtc_tm.tm_hour = 0;
146 
147 	retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
148 	if (retval == -1) {
149 		if (errno == ENOTTY) {
150 			fprintf(stderr,
151 				"\n...Alarm IRQs not supported.\n");
152 			goto test_PIE;
153 		}
154 		perror("RTC_ALM_SET ioctl");
155 		exit(errno);
156 	}
157 
158 	/* Read the current alarm settings */
159 	retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
160 	if (retval == -1) {
161 		perror("RTC_ALM_READ ioctl");
162 		exit(errno);
163 	}
164 
165 	fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",
166 		rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
167 
168 	/* Enable alarm interrupts */
169 	retval = ioctl(fd, RTC_AIE_ON, 0);
170 	if (retval == -1) {
171 		perror("RTC_AIE_ON ioctl");
172 		exit(errno);
173 	}
174 
175 	fprintf(stderr, "Waiting 5 seconds for alarm...");
176 	fflush(stderr);
177 	/* This blocks until the alarm ring causes an interrupt */
178 	retval = read(fd, &data, sizeof(unsigned long));
179 	if (retval == -1) {
180 		perror("read");
181 		exit(errno);
182 	}
183 	irqcount++;
184 	fprintf(stderr, " okay. Alarm rang.\n");
185 
186 	/* Disable alarm interrupts */
187 	retval = ioctl(fd, RTC_AIE_OFF, 0);
188 	if (retval == -1) {
189 		perror("RTC_AIE_OFF ioctl");
190 		exit(errno);
191 	}
192 
193 test_PIE:
194 	/* Read periodic IRQ rate */
195 	retval = ioctl(fd, RTC_IRQP_READ, &tmp);
196 	if (retval == -1) {
197 		/* not all RTCs support periodic IRQs */
198 		if (errno == ENOTTY) {
199 			fprintf(stderr, "\nNo periodic IRQ support\n");
200 			goto done;
201 		}
202 		perror("RTC_IRQP_READ ioctl");
203 		exit(errno);
204 	}
205 	fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp);
206 
207 	fprintf(stderr, "Counting 20 interrupts at:");
208 	fflush(stderr);
209 
210 	/* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
211 	for (tmp=2; tmp<=maxfreq; tmp*=2) {
212 
213 		retval = ioctl(fd, RTC_IRQP_SET, tmp);
214 		if (retval == -1) {
215 			/* not all RTCs can change their periodic IRQ rate */
216 			if (errno == ENOTTY) {
217 				fprintf(stderr,
218 					"\n...Periodic IRQ rate is fixed\n");
219 				goto done;
220 			}
221 			perror("RTC_IRQP_SET ioctl");
222 			exit(errno);
223 		}
224 
225 		fprintf(stderr, "\n%ldHz:\t", tmp);
226 		fflush(stderr);
227 
228 		/* Enable periodic interrupts */
229 		retval = ioctl(fd, RTC_PIE_ON, 0);
230 		if (retval == -1) {
231 			perror("RTC_PIE_ON ioctl");
232 			exit(errno);
233 		}
234 
235 		for (i=1; i<21; i++) {
236 			/* This blocks */
237 			retval = read(fd, &data, sizeof(unsigned long));
238 			if (retval == -1) {
239 				perror("read");
240 				exit(errno);
241 			}
242 			fprintf(stderr, " %d",i);
243 			fflush(stderr);
244 			irqcount++;
245 		}
246 
247 		/* Disable periodic interrupts */
248 		retval = ioctl(fd, RTC_PIE_OFF, 0);
249 		if (retval == -1) {
250 			perror("RTC_PIE_OFF ioctl");
251 			exit(errno);
252 		}
253 	}
254 
255 done:
256 	fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
257 
258 	close(fd);
259 
260 	return 0;
261 }
262