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 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 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 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 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 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 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 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 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 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 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