/src/ntp-dev/ntpd/refclock_pcf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * refclock_pcf - clock driver for the Conrad parallel port radio clock |
3 | | */ |
4 | | |
5 | | #ifdef HAVE_CONFIG_H |
6 | | # include <config.h> |
7 | | #endif |
8 | | |
9 | | #if defined(REFCLOCK) && defined(CLOCK_PCF) |
10 | | |
11 | | #include "ntpd.h" |
12 | | #include "ntp_io.h" |
13 | | #include "ntp_refclock.h" |
14 | | #include "ntp_calendar.h" |
15 | | #include "ntp_stdlib.h" |
16 | | |
17 | | /* |
18 | | * This driver supports the parallel port radio clock sold by Conrad |
19 | | * Electronic under order numbers 967602 and 642002. |
20 | | * |
21 | | * It requires that the local timezone be CET/CEST and that the pcfclock |
22 | | * device driver be installed. A device driver for Linux is available at |
23 | | * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD |
24 | | * driver is available at http://schumann.cx/pcfclock/. |
25 | | */ |
26 | | |
27 | | /* |
28 | | * Interface definitions |
29 | | */ |
30 | | #define DEVICE "/dev/pcfclocks/%d" |
31 | | #define OLDDEVICE "/dev/pcfclock%d" |
32 | 0 | #define PRECISION (-1) /* precision assumed (about 0.5 s) */ |
33 | 0 | #define REFID "PCF" |
34 | 0 | #define DESCRIPTION "Conrad parallel port radio clock" |
35 | | |
36 | | #define LENPCF 18 /* timecode length */ |
37 | | |
38 | | /* |
39 | | * Function prototypes |
40 | | */ |
41 | | static int pcf_start (int, struct peer *); |
42 | | static void pcf_shutdown (int, struct peer *); |
43 | | static void pcf_poll (int, struct peer *); |
44 | | |
45 | | /* |
46 | | * Transfer vector |
47 | | */ |
48 | | struct refclock refclock_pcf = { |
49 | | pcf_start, /* start up driver */ |
50 | | pcf_shutdown, /* shut down driver */ |
51 | | pcf_poll, /* transmit poll message */ |
52 | | noentry, /* not used */ |
53 | | noentry, /* initialize driver (not used) */ |
54 | | noentry, /* not used */ |
55 | | NOFLAGS /* not used */ |
56 | | }; |
57 | | |
58 | | |
59 | | /* |
60 | | * pcf_start - open the device and initialize data for processing |
61 | | */ |
62 | | static int |
63 | | pcf_start( |
64 | | int unit, |
65 | | struct peer *peer |
66 | | ) |
67 | 0 | { |
68 | 0 | struct refclockproc *pp; |
69 | 0 | int fd; |
70 | 0 | char device[128]; |
71 | | |
72 | | /* |
73 | | * Open device file for reading. |
74 | | */ |
75 | 0 | snprintf(device, sizeof(device), DEVICE, unit); |
76 | 0 | fd = open(device, O_RDONLY); |
77 | 0 | if (fd == -1) { |
78 | 0 | snprintf(device, sizeof(device), OLDDEVICE, unit); |
79 | 0 | fd = open(device, O_RDONLY); |
80 | 0 | } |
81 | 0 | #ifdef DEBUG |
82 | 0 | if (debug) |
83 | 0 | printf ("starting PCF with device %s\n",device); |
84 | 0 | #endif |
85 | 0 | if (fd == -1) { |
86 | 0 | return (0); |
87 | 0 | } |
88 | | |
89 | 0 | pp = peer->procptr; |
90 | 0 | pp->io.clock_recv = noentry; |
91 | 0 | pp->io.srcclock = peer; |
92 | 0 | pp->io.datalen = 0; |
93 | 0 | pp->io.fd = fd; |
94 | | |
95 | | /* |
96 | | * Initialize miscellaneous variables |
97 | | */ |
98 | 0 | peer->precision = PRECISION; |
99 | 0 | pp->clockdesc = DESCRIPTION; |
100 | | /* one transmission takes 172.5 milliseconds since the radio clock |
101 | | transmits 69 bits with a period of 2.5 milliseconds per bit */ |
102 | 0 | pp->fudgetime1 = 0.1725; |
103 | 0 | memcpy((char *)&pp->refid, REFID, 4); |
104 | |
|
105 | 0 | return (1); |
106 | 0 | } |
107 | | |
108 | | |
109 | | /* |
110 | | * pcf_shutdown - shut down the clock |
111 | | */ |
112 | | static void |
113 | | pcf_shutdown( |
114 | | int unit, |
115 | | struct peer *peer |
116 | | ) |
117 | 0 | { |
118 | 0 | struct refclockproc *pp; |
119 | | |
120 | 0 | pp = peer->procptr; |
121 | 0 | if (NULL != pp) |
122 | 0 | close(pp->io.fd); |
123 | 0 | } |
124 | | |
125 | | |
126 | | /* |
127 | | * pcf_poll - called by the transmit procedure |
128 | | */ |
129 | | static void |
130 | | pcf_poll( |
131 | | int unit, |
132 | | struct peer *peer |
133 | | ) |
134 | 0 | { |
135 | 0 | struct refclockproc *pp; |
136 | 0 | char buf[LENPCF]; |
137 | 0 | struct tm tm, *tp; |
138 | 0 | time_t t; |
139 | | |
140 | 0 | pp = peer->procptr; |
141 | |
|
142 | 0 | buf[0] = 0; |
143 | 0 | if (read(pp->io.fd, buf, sizeof(buf)) < (ssize_t)sizeof(buf) || buf[0] != 9) { |
144 | 0 | refclock_report(peer, CEVNT_FAULT); |
145 | 0 | return; |
146 | 0 | } |
147 | | |
148 | 0 | ZERO(tm); |
149 | |
|
150 | 0 | tm.tm_mday = buf[11] * 10 + buf[10]; |
151 | 0 | tm.tm_mon = buf[13] * 10 + buf[12] - 1; |
152 | 0 | tm.tm_year = buf[15] * 10 + buf[14]; |
153 | 0 | tm.tm_hour = buf[7] * 10 + buf[6]; |
154 | 0 | tm.tm_min = buf[5] * 10 + buf[4]; |
155 | 0 | tm.tm_sec = buf[3] * 10 + buf[2]; |
156 | 0 | tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1; |
157 | | |
158 | | /* |
159 | | * Y2K convert the 2-digit year |
160 | | */ |
161 | 0 | if (tm.tm_year < 99) |
162 | 0 | tm.tm_year += 100; |
163 | | |
164 | 0 | t = mktime(&tm); |
165 | 0 | if (t == (time_t) -1) { |
166 | 0 | refclock_report(peer, CEVNT_BADTIME); |
167 | 0 | return; |
168 | 0 | } |
169 | | |
170 | | #if defined(__GLIBC__) && defined(_BSD_SOURCE) |
171 | | if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200) |
172 | | || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600) |
173 | | || tm.tm_isdst < 0) { |
174 | | #ifdef DEBUG |
175 | | if (debug) |
176 | | printf ("local time zone not set to CET/CEST\n"); |
177 | | #endif |
178 | | refclock_report(peer, CEVNT_BADTIME); |
179 | | return; |
180 | | } |
181 | | #endif |
182 | | |
183 | 0 | pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm); |
184 | |
|
185 | 0 | #if defined(_REENTRANT) || defined(_THREAD_SAFE) |
186 | 0 | tp = gmtime_r(&t, &tm); |
187 | | #else |
188 | | tp = gmtime(&t); |
189 | | #endif |
190 | 0 | if (!tp) { |
191 | 0 | refclock_report(peer, CEVNT_FAULT); |
192 | 0 | return; |
193 | 0 | } |
194 | | |
195 | 0 | get_systime(&pp->lastrec); |
196 | 0 | pp->polls++; |
197 | 0 | pp->year = tp->tm_year + 1900; |
198 | 0 | pp->day = tp->tm_yday + 1; |
199 | 0 | pp->hour = tp->tm_hour; |
200 | 0 | pp->minute = tp->tm_min; |
201 | 0 | pp->second = tp->tm_sec; |
202 | 0 | pp->nsec = buf[16] * 31250000; |
203 | 0 | if (buf[17] & 1) |
204 | 0 | pp->nsec += 500000000; |
205 | |
|
206 | 0 | #ifdef DEBUG |
207 | 0 | if (debug) |
208 | 0 | printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", |
209 | 0 | unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour, |
210 | 0 | pp->minute, pp->second); |
211 | 0 | #endif |
212 | |
|
213 | 0 | if (!refclock_process(pp)) { |
214 | 0 | refclock_report(peer, CEVNT_BADTIME); |
215 | 0 | return; |
216 | 0 | } |
217 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
218 | 0 | if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2)) |
219 | 0 | pp->leap = LEAP_NOTINSYNC; |
220 | 0 | else |
221 | 0 | pp->leap = LEAP_NOWARNING; |
222 | 0 | pp->lastref = pp->lastrec; |
223 | 0 | refclock_receive(peer); |
224 | 0 | } |
225 | | #else |
226 | | int refclock_pcf_bs; |
227 | | #endif /* REFCLOCK */ |