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