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