1 /* $OpenBSD: gpiodcf.c,v 1.11 2024/05/13 01:15:50 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/conf.h>
23 #include <sys/proc.h>
24 #include <sys/vnode.h>
25 #include <sys/device.h>
26 #include <sys/time.h>
27 #include <sys/sensors.h>
28 #include <sys/gpio.h>
29
30 #include <dev/gpio/gpiovar.h>
31
32 #ifdef GPIODCF_DEBUG
33 #define DPRINTFN(n, x) do { if (gpiodcfdebug > (n)) printf x; } while (0)
34 int gpiodcfdebug = 0;
35 #else
36 #define DPRINTFN(n, x)
37 #endif
38 #define DPRINTF(x) DPRINTFN(0, x)
39
40 /* max. skew of received time diff vs. measured time diff in percent. */
41 #define MAX_SKEW 5
42
43 #define GPIODCF_NPINS 1
44 #define GPIODCF_PIN_DATA 0
45
46 struct gpiodcf_softc {
47 struct device sc_dev; /* base device */
48 void *sc_gpio;
49 struct gpio_pinmap sc_map;
50 int __map[GPIODCF_NPINS];
51 u_char sc_dying; /* disconnecting */
52 int sc_data;
53
54 struct timeout sc_to;
55
56 struct timeout sc_bv_to; /* bit-value detect */
57 struct timeout sc_db_to; /* debounce */
58 struct timeout sc_mg_to; /* minute-gap detect */
59 struct timeout sc_sl_to; /* signal-loss detect */
60 struct timeout sc_it_to; /* invalidate time */
61
62 int sc_sync; /* 1 during sync */
63 u_int64_t sc_mask; /* 64 bit mask */
64 u_int64_t sc_tbits; /* Time bits */
65 int sc_minute;
66 int sc_level;
67 time_t sc_last_mg;
68 time_t sc_current; /* current time */
69 time_t sc_next; /* time to become valid next */
70 time_t sc_last;
71 int sc_nrecv; /* consecutive valid times */
72 struct timeval sc_last_tv; /* uptime of last valid time */
73 struct ksensor sc_sensor;
74 #ifdef GPIODCF_DEBUG
75 struct ksensor sc_skew; /* recv vs local skew */
76 #endif
77 struct ksensordev sc_sensordev;
78 };
79
80 /*
81 * timeouts used:
82 */
83 #define T_BV 150 /* bit value detection (150ms) */
84 #define T_SYNC 950 /* sync (950ms) */
85 #define T_MG 1500 /* minute gap detection (1500ms) */
86 #define T_MGSYNC 450 /* resync after a minute gap (450ms) */
87 #define T_SL 3000 /* detect signal loss (3sec) */
88 #define T_WAIT 5000 /* wait (5sec) */
89 #define T_WARN 300000 /* degrade sensor status to warning (5min) */
90 #define T_CRIT 900000 /* degrade sensor status to critical (15min) */
91
92 void gpiodcf_probe(void *);
93 void gpiodcf_bv_probe(void *);
94 void gpiodcf_mg_probe(void *);
95 void gpiodcf_sl_probe(void *);
96 void gpiodcf_invalidate(void *);
97
98 int gpiodcf_match(struct device *, void *, void *);
99 void gpiodcf_attach(struct device *, struct device *, void *);
100 int gpiodcf_detach(struct device *, int);
101 int gpiodcf_activate(struct device *, int);
102
103 int gpiodcf_signal(struct gpiodcf_softc *);
104
105 struct cfdriver gpiodcf_cd = {
106 NULL, "gpiodcf", DV_DULL
107 };
108
109 const struct cfattach gpiodcf_ca = {
110 sizeof(struct gpiodcf_softc),
111 gpiodcf_match,
112 gpiodcf_attach,
113 gpiodcf_detach,
114 gpiodcf_activate
115 };
116
117 int
gpiodcf_match(struct device * parent,void * match,void * aux)118 gpiodcf_match(struct device *parent, void *match, void *aux)
119 {
120 struct cfdata *cf = match;
121 struct gpio_attach_args *ga = aux;
122
123 if (ga->ga_offset == -1)
124 return 0;
125
126 return (strcmp(cf->cf_driver->cd_name, "gpiodcf") == 0);
127 }
128
129 void
gpiodcf_attach(struct device * parent,struct device * self,void * aux)130 gpiodcf_attach(struct device *parent, struct device *self, void *aux)
131 {
132 struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
133 struct gpio_attach_args *ga = aux;
134 int caps;
135
136 if (gpio_npins(ga->ga_mask) != GPIODCF_NPINS) {
137 printf(": invalid pin mask\n");
138 return;
139 }
140 sc->sc_gpio = ga->ga_gpio;
141 sc->sc_map.pm_map = sc->__map;
142 if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
143 &sc->sc_map)) {
144 printf(": can't map pins\n");
145 return;
146 }
147
148 caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA);
149 if (!(caps & GPIO_PIN_INPUT)) {
150 printf(": data pin is unable to receive input\n");
151 goto fishy;
152 }
153 printf(": DATA[%d]", sc->sc_map.pm_map[GPIODCF_PIN_DATA]);
154 sc->sc_data = GPIO_PIN_INPUT;
155 gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA, sc->sc_data);
156 printf("\n");
157
158 strlcpy(sc->sc_sensor.desc, "DCF77", sizeof(sc->sc_sensor.desc));
159
160 timeout_set(&sc->sc_to, gpiodcf_probe, sc);
161 timeout_set(&sc->sc_bv_to, gpiodcf_bv_probe, sc);
162 timeout_set(&sc->sc_mg_to, gpiodcf_mg_probe, sc);
163 timeout_set(&sc->sc_sl_to, gpiodcf_sl_probe, sc);
164 timeout_set(&sc->sc_it_to, gpiodcf_invalidate, sc);
165
166 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
167 sizeof(sc->sc_sensordev.xname));
168
169 sc->sc_sensor.type = SENSOR_TIMEDELTA;
170 sc->sc_sensor.status = SENSOR_S_UNKNOWN;
171 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
172
173 #ifdef GPIODCF_DEBUG
174 sc->sc_skew.type = SENSOR_TIMEDELTA;
175 sc->sc_skew.status = SENSOR_S_UNKNOWN;
176 strlcpy(sc->sc_skew.desc, "local clock skew",
177 sizeof(sc->sc_skew.desc));
178 sensor_attach(&sc->sc_sensordev, &sc->sc_skew);
179 #endif
180 sensordev_install(&sc->sc_sensordev);
181
182 sc->sc_level = 0;
183 sc->sc_minute = 0;
184 sc->sc_last_mg = 0L;
185
186 sc->sc_sync = 1;
187
188 sc->sc_current = 0L;
189 sc->sc_next = 0L;
190 sc->sc_nrecv = 0;
191 sc->sc_last = 0L;
192 sc->sc_last_tv.tv_sec = 0L;
193
194 /* Give the receiver some slack to stabilize */
195 timeout_add_msec(&sc->sc_to, T_WAIT);
196
197 /* Detect signal loss */
198 timeout_add_msec(&sc->sc_sl_to, T_WAIT + T_SL);
199
200 DPRINTF(("synchronizing\n"));
201 return;
202
203 fishy:
204 DPRINTF(("gpiodcf_attach failed\n"));
205 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
206 sc->sc_dying = 1;
207 }
208
209 int
gpiodcf_detach(struct device * self,int flags)210 gpiodcf_detach(struct device *self, int flags)
211 {
212 struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
213
214 sc->sc_dying = 1;
215
216 timeout_del(&sc->sc_to);
217 timeout_del(&sc->sc_bv_to);
218 timeout_del(&sc->sc_mg_to);
219 timeout_del(&sc->sc_sl_to);
220 timeout_del(&sc->sc_it_to);
221
222 /* Unregister the clock with the kernel */
223 sensordev_deinstall(&sc->sc_sensordev);
224
225 /* Finally unmap the GPIO pin */
226 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
227
228 return 0;
229 }
230
231 /*
232 * return 1 during high-power-, 0 during low-power-emission
233 * If bit 0 is set, the transmitter emits at full power.
234 * During the low-power emission we decode a zero bit.
235 */
236 int
gpiodcf_signal(struct gpiodcf_softc * sc)237 gpiodcf_signal(struct gpiodcf_softc *sc)
238 {
239 return (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA) ==
240 GPIO_PIN_HIGH ? 1 : 0);
241 }
242
243 /* gpiodcf_probe runs in a process context. */
244 void
gpiodcf_probe(void * xsc)245 gpiodcf_probe(void *xsc)
246 {
247 struct gpiodcf_softc *sc = xsc;
248 struct timespec now;
249 int data;
250
251 if (sc->sc_dying)
252 return;
253
254 data = gpiodcf_signal(sc);
255 if (data == -1)
256 return;
257
258 if (data) {
259 sc->sc_level = 1;
260 timeout_add(&sc->sc_to, 1);
261 return;
262 }
263
264 if (sc->sc_level == 0)
265 return;
266
267 /* the beginning of a second */
268 sc->sc_level = 0;
269 if (sc->sc_minute == 1) {
270 if (sc->sc_sync) {
271 DPRINTF(("start collecting bits\n"));
272 sc->sc_sync = 0;
273 } else {
274 /* provide the timedelta */
275 microtime(&sc->sc_sensor.tv);
276 nanotime(&now);
277 sc->sc_current = sc->sc_next;
278 sc->sc_sensor.value = (int64_t)(now.tv_sec -
279 sc->sc_current) * 1000000000LL + now.tv_nsec;
280
281 sc->sc_sensor.status = SENSOR_S_OK;
282
283 /*
284 * if no valid time information is received
285 * during the next 5 minutes, the sensor state
286 * will be degraded to SENSOR_S_WARN
287 */
288 timeout_add_msec(&sc->sc_it_to, T_WARN);
289 }
290 sc->sc_minute = 0;
291 }
292
293 timeout_add_msec(&sc->sc_to, T_SYNC); /* resync in 950 ms */
294
295 /* no clock and bit detection during sync */
296 if (!sc->sc_sync) {
297 /* detect bit value */
298 timeout_add_msec(&sc->sc_bv_to, T_BV);
299 }
300 timeout_add_msec(&sc->sc_mg_to, T_MG); /* detect minute gap */
301 timeout_add_msec(&sc->sc_sl_to, T_SL); /* detect signal loss */
302 }
303
304 /* detect the bit value */
305 void
gpiodcf_bv_probe(void * xsc)306 gpiodcf_bv_probe(void *xsc)
307 {
308 struct gpiodcf_softc *sc = xsc;
309 int data;
310
311 if (sc->sc_dying)
312 return;
313
314 data = gpiodcf_signal(sc);
315 if (data == -1) {
316 DPRINTF(("bit detection failed\n"));
317 return;
318 }
319
320 DPRINTFN(1, (data ? "0" : "1"));
321 if (!(data))
322 sc->sc_tbits |= sc->sc_mask;
323 sc->sc_mask <<= 1;
324 }
325
326 /* detect the minute gap */
327 void
gpiodcf_mg_probe(void * xsc)328 gpiodcf_mg_probe(void *xsc)
329 {
330 struct gpiodcf_softc *sc = xsc;
331 struct clock_ymdhms ymdhm;
332 struct timeval monotime;
333 int tdiff_recv, tdiff_local;
334 int skew;
335 int minute_bits, hour_bits, day_bits;
336 int month_bits, year_bits, wday;
337 int p1, p2, p3;
338 int p1_bit, p2_bit, p3_bit;
339 int r_bit, a1_bit, a2_bit, z1_bit, z2_bit;
340 int s_bit, m_bit;
341 u_int32_t parity = 0x6996;
342
343 if (sc->sc_sync) {
344 sc->sc_minute = 1;
345 goto cleanbits;
346 }
347
348 if (gettime() - sc->sc_last_mg < 57) {
349 DPRINTF(("\nunexpected gap, resync\n"));
350 sc->sc_sync = sc->sc_minute = 1;
351 goto cleanbits;
352 }
353
354 /* extract bits w/o parity */
355 m_bit = sc->sc_tbits & 1;
356 r_bit = sc->sc_tbits >> 15 & 1;
357 a1_bit = sc->sc_tbits >> 16 & 1;
358 z1_bit = sc->sc_tbits >> 17 & 1;
359 z2_bit = sc->sc_tbits >> 18 & 1;
360 a2_bit = sc->sc_tbits >> 19 & 1;
361 s_bit = sc->sc_tbits >> 20 & 1;
362 p1_bit = sc->sc_tbits >> 28 & 1;
363 p2_bit = sc->sc_tbits >> 35 & 1;
364 p3_bit = sc->sc_tbits >> 58 & 1;
365
366 minute_bits = sc->sc_tbits >> 21 & 0x7f;
367 hour_bits = sc->sc_tbits >> 29 & 0x3f;
368 day_bits = sc->sc_tbits >> 36 & 0x3f;
369 wday = (sc->sc_tbits >> 42) & 0x07;
370 month_bits = sc->sc_tbits >> 45 & 0x1f;
371 year_bits = sc->sc_tbits >> 50 & 0xff;
372
373 /* validate time information */
374 p1 = (parity >> (minute_bits & 0x0f) & 1) ^
375 (parity >> (minute_bits >> 4) & 1);
376
377 p2 = (parity >> (hour_bits & 0x0f) & 1) ^
378 (parity >> (hour_bits >> 4) & 1);
379
380 p3 = (parity >> (day_bits & 0x0f) & 1) ^
381 (parity >> (day_bits >> 4) & 1) ^
382 ((parity >> wday) & 1) ^ (parity >> (month_bits & 0x0f) & 1) ^
383 (parity >> (month_bits >> 4) & 1) ^
384 (parity >> (year_bits & 0x0f) & 1) ^
385 (parity >> (year_bits >> 4) & 1);
386
387 if (m_bit == 0 && s_bit == 1 && p1 == p1_bit && p2 == p2_bit &&
388 p3 == p3_bit && (z1_bit ^ z2_bit)) {
389
390 /* Decode time */
391 if ((ymdhm.dt_year = 2000 + FROMBCD(year_bits)) > 2037) {
392 DPRINTF(("year out of range, resync\n"));
393 sc->sc_sync = 1;
394 goto cleanbits;
395 }
396 ymdhm.dt_min = FROMBCD(minute_bits);
397 ymdhm.dt_hour = FROMBCD(hour_bits);
398 ymdhm.dt_day = FROMBCD(day_bits);
399 ymdhm.dt_mon = FROMBCD(month_bits);
400 ymdhm.dt_sec = 0;
401
402 sc->sc_next = clock_ymdhms_to_secs(&ymdhm);
403 getmicrouptime(&monotime);
404
405 /* convert to coordinated universal time */
406 sc->sc_next -= z1_bit ? 7200 : 3600;
407
408 DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s",
409 ymdhm.dt_day, ymdhm.dt_mon, ymdhm.dt_year,
410 ymdhm.dt_hour, ymdhm.dt_min, z1_bit ? "CEST" : "CET"));
411 DPRINTF((r_bit ? ", call bit" : ""));
412 DPRINTF((a1_bit ? ", dst chg ann." : ""));
413 DPRINTF((a2_bit ? ", leap sec ann." : ""));
414 DPRINTF(("\n"));
415
416 if (sc->sc_last) {
417 tdiff_recv = sc->sc_next - sc->sc_last;
418 tdiff_local = monotime.tv_sec - sc->sc_last_tv.tv_sec;
419 skew = abs(tdiff_local - tdiff_recv);
420 #ifdef GPIODCF_DEBUG
421 if (sc->sc_skew.status == SENSOR_S_UNKNOWN)
422 sc->sc_skew.status = SENSOR_S_CRIT;
423 sc->sc_skew.value = skew * 1000000000LL;
424 getmicrotime(&sc->sc_skew.tv);
425 #endif
426 DPRINTF(("local = %d, recv = %d, skew = %d\n",
427 tdiff_local, tdiff_recv, skew));
428
429 if (skew && skew * 100LL / tdiff_local > MAX_SKEW) {
430 DPRINTF(("skew out of tolerated range\n"));
431 goto cleanbits;
432 } else {
433 if (sc->sc_nrecv < 2) {
434 sc->sc_nrecv++;
435 DPRINTF(("got frame %d\n",
436 sc->sc_nrecv));
437 } else {
438 DPRINTF(("data is valid\n"));
439 sc->sc_minute = 1;
440 }
441 }
442 } else {
443 DPRINTF(("received the first frame\n"));
444 sc->sc_nrecv = 1;
445 }
446
447 /* record the time received and when it was received */
448 sc->sc_last = sc->sc_next;
449 sc->sc_last_tv.tv_sec = monotime.tv_sec;
450 } else {
451 DPRINTF(("\nparity error, resync\n"));
452 sc->sc_sync = sc->sc_minute = 1;
453 }
454
455 cleanbits:
456 timeout_add_msec(&sc->sc_to, T_MGSYNC); /* re-sync in 450 ms */
457 sc->sc_last_mg = gettime();
458 sc->sc_tbits = 0LL;
459 sc->sc_mask = 1LL;
460 }
461
462 /* detect signal loss */
463 void
gpiodcf_sl_probe(void * xsc)464 gpiodcf_sl_probe(void *xsc)
465 {
466 struct gpiodcf_softc *sc = xsc;
467
468 if (sc->sc_dying)
469 return;
470
471 DPRINTF(("no signal\n"));
472 sc->sc_sync = 1;
473 timeout_add_msec(&sc->sc_to, T_WAIT);
474 timeout_add_msec(&sc->sc_sl_to, T_WAIT + T_SL);
475 }
476
477 /* invalidate timedelta (called in an interrupt context) */
478 void
gpiodcf_invalidate(void * xsc)479 gpiodcf_invalidate(void *xsc)
480 {
481 struct gpiodcf_softc *sc = xsc;
482
483 if (sc->sc_dying)
484 return;
485
486 if (sc->sc_sensor.status == SENSOR_S_OK) {
487 sc->sc_sensor.status = SENSOR_S_WARN;
488 /*
489 * further degrade in 15 minutes if we dont receive any new
490 * time information
491 */
492 timeout_add_msec(&sc->sc_it_to, T_CRIT);
493 } else {
494 sc->sc_sensor.status = SENSOR_S_CRIT;
495 sc->sc_nrecv = 0;
496 }
497 }
498
499 int
gpiodcf_activate(struct device * self,int act)500 gpiodcf_activate(struct device *self, int act)
501 {
502 struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
503
504 switch (act) {
505 case DVACT_DEACTIVATE:
506 sc->sc_dying = 1;
507 break;
508 }
509 return 0;
510 }
511