summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMarc Balmer <mbalmer@cvs.openbsd.org>2008-11-28 17:42:44 +0000
committerMarc Balmer <mbalmer@cvs.openbsd.org>2008-11-28 17:42:44 +0000
commitfc69678ea9cdf784f55858fdeed1484b56cf560d (patch)
tree34a60c11cda16d530b6ac017a68c684ae5723df3 /sys
parent45cb44dba8434af5f6b2ee9dbd30cbde1bc8e31d (diff)
Welcome gpiodcf(4), a driver that attaches to a GPIO pin, decodes the
signal from an attached DCF77, HG, or TDF receiver and provides a timedelta.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/gpio/files.gpio8
-rw-r--r--sys/dev/gpio/gpiodcf.c612
2 files changed, 619 insertions, 1 deletions
diff --git a/sys/dev/gpio/files.gpio b/sys/dev/gpio/files.gpio
index 796dbc02e43..01d72f2ec50 100644
--- a/sys/dev/gpio/files.gpio
+++ b/sys/dev/gpio/files.gpio
@@ -1,4 +1,4 @@
-# $OpenBSD: files.gpio,v 1.7 2008/11/24 12:12:12 mbalmer Exp $
+# $OpenBSD: files.gpio,v 1.8 2008/11/28 17:42:43 mbalmer Exp $
define gpio {[offset = -1], [mask = 0]}
@@ -20,3 +20,9 @@ file dev/gpio/gpioiic.c gpioiic
device gpioow: onewirebus, onewire_bitbang
attach gpioow at gpio
file dev/gpio/gpioow.c gpioow
+
+# DCF77/HBG time signal station receivers
+device gpiodcf
+attach gpiodcf at gpio
+file dev/gpio/gpiodcf.c gpiodcf
+
diff --git a/sys/dev/gpio/gpiodcf.c b/sys/dev/gpio/gpiodcf.c
new file mode 100644
index 00000000000..58d5abc5ed7
--- /dev/null
+++ b/sys/dev/gpio/gpiodcf.c
@@ -0,0 +1,612 @@
+/* $OpenBSD: gpiodcf.c,v 1.1 2008/11/28 17:42:43 mbalmer Exp $ */
+
+/*
+ * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/device.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/sensors.h>
+#include <sys/gpio.h>
+
+#include <dev/gpio/gpiovar.h>
+
+#ifdef GPIODCF_DEBUG
+#define DPRINTFN(n, x) do { if (gpiodcfdebug > (n)) printf x; } while (0)
+int gpiodcfdebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define DPERIOD1 ((long) 5 * 60) /* degrade OK -> WARN */
+#define DPERIOD2 ((long) 15 * 60) /* degrade WARN -> CRIT */
+
+/* max. skew of received time diff vs. measured time diff in percent. */
+#define MAX_SKEW 5
+
+#define CLOCK_DCF77 0
+#define CLOCK_HBG 1
+
+#define GPIODCF_NPINS 1
+#define GPIODCF_PIN_DATA 0
+
+static const char *clockname[2] = {
+ "DCF77",
+ "HBG" };
+
+struct gpiodcf_softc {
+ struct device sc_dev; /* base device */
+ void *sc_gpio;
+ struct gpio_pinmap sc_map;
+ int __map[GPIODCF_NPINS];
+ u_char sc_dying; /* disconnecting */
+ int sc_data;
+
+ struct timeout sc_to;
+
+ struct timeout sc_bv_to; /* bit-value detect */
+ struct timeout sc_db_to; /* debounce */
+ struct timeout sc_mg_to; /* minute-gap detect */
+ struct timeout sc_sl_to; /* signal-loss detect */
+ struct timeout sc_it_to; /* invalidate time */
+ struct timeout sc_ct_to; /* detect clock type */
+
+ int sc_detect_ct; /* != 0: autodetect type */
+ int sc_clocktype; /* DCF77 or HBG */
+ int sc_sync; /* 1 during sync */
+ u_int64_t sc_mask; /* 64 bit mask */
+ u_int64_t sc_tbits; /* Time bits */
+ int sc_minute;
+ int sc_level;
+ time_t sc_last_mg;
+ time_t sc_current; /* current time */
+ time_t sc_next; /* time to become valid next */
+ time_t sc_last;
+ int sc_nrecv; /* consecutive valid times */
+ struct timeval sc_last_tv; /* uptime of last valid time */
+ struct ksensor sc_sensor;
+#ifdef GPIODCF_DEBUG
+ struct ksensor sc_skew; /* recv vs local skew */
+#endif
+ struct ksensordev sc_sensordev;
+};
+
+/*
+ * timeouts being used in hz:
+ * t_bv bit value detection (150ms)
+ * t_ct detect clocktype (250ms)
+ * t_sync sync (950ms)
+ * t_mg minute gap detection (1500ms)
+ * t_mgsync resync after a minute gap (450ms)
+ * t_sl detect signal loss (3sec)
+ * t_wait wait (5sec)
+ * t_warn degrade sensor status to warning (5min)
+ * t_crit degrade sensor status to critical (15min)
+ */
+static int t_bv, t_ct, t_sync, t_mg, t_sl, t_mgsync, t_wait, t_warn, t_crit;
+
+void gpiodcf_intr(void *);
+void gpiodcf_probe(void *);
+void gpiodcf_bv_probe(void *);
+void gpiodcf_mg_probe(void *);
+void gpiodcf_sl_probe(void *);
+void gpiodcf_ct_probe(void *);
+void gpiodcf_invalidate(void *);
+
+int gpiodcf_match(struct device *, void *, void *);
+void gpiodcf_attach(struct device *, struct device *, void *);
+int gpiodcf_detach(struct device *, int);
+int gpiodcf_activate(struct device *, enum devact);
+
+int gpiodcf_signal(struct gpiodcf_softc *);
+
+struct cfdriver gpiodcf_cd = {
+ NULL, "gpiodcf", DV_DULL
+};
+
+const struct cfattach gpiodcf_ca = {
+ sizeof(struct gpiodcf_softc),
+ gpiodcf_match,
+ gpiodcf_attach,
+ gpiodcf_detach,
+ gpiodcf_activate
+};
+
+int
+gpiodcf_match(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+ struct gpio_attach_args *ga = aux;
+
+ if (ga->ga_offset == -1)
+ return 0;
+
+ return (strcmp(cf->cf_driver->cd_name, "gpiodcf") == 0);
+}
+
+void
+gpiodcf_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
+ struct gpio_attach_args *ga = aux;
+ struct timeval t;
+ int caps;
+
+ if (gpio_npins(ga->ga_mask) != GPIODCF_NPINS) {
+ printf(": invalid pin mask\n");
+ return;
+ }
+ sc->sc_gpio = ga->ga_gpio;
+ sc->sc_map.pm_map = sc->__map;
+ if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
+ &sc->sc_map)) {
+ printf(": can't map pins\n");
+ return;
+ }
+
+ caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA);
+ if (!(caps & GPIO_PIN_INPUT)) {
+ printf(": data pin is unable to receive input\n");
+ goto fishy;
+ }
+ printf(": DATA[%d]", sc->sc_map.pm_map[GPIODCF_PIN_DATA]);
+ sc->sc_data = GPIO_PIN_INPUT;
+ gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA, sc->sc_data);
+ printf("\n");
+
+ sc->sc_detect_ct = 1;
+ strlcpy(sc->sc_sensor.desc, "Unknown",
+ sizeof(sc->sc_sensor.desc));
+
+ timeout_set(&sc->sc_to, gpiodcf_probe, sc);
+ timeout_set(&sc->sc_bv_to, gpiodcf_bv_probe, sc);
+ timeout_set(&sc->sc_mg_to, gpiodcf_mg_probe, sc);
+ timeout_set(&sc->sc_sl_to, gpiodcf_sl_probe, sc);
+ timeout_set(&sc->sc_it_to, gpiodcf_invalidate, sc);
+ timeout_set(&sc->sc_ct_to, gpiodcf_ct_probe, sc);
+
+ strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+
+ sc->sc_sensor.type = SENSOR_TIMEDELTA;
+ sc->sc_sensor.status = SENSOR_S_UNKNOWN;
+ sc->sc_sensor.value = 0LL;
+ sc->sc_sensor.flags = 0;
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
+
+#ifdef GPIODCF_DEBUG
+ sc->sc_skew.type = SENSOR_TIMEDELTA;
+ sc->sc_skew.status = SENSOR_S_UNKNOWN;
+ sc->sc_skew.value = 0LL;
+ sc->sc_skew.flags = 0;
+ strlcpy(sc->sc_skew.desc, "local clock skew",
+ sizeof(sc->sc_skew.desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_skew);
+#endif
+ sensordev_install(&sc->sc_sensordev);
+
+ sc->sc_clocktype = -1;
+ sc->sc_level = 0;
+ sc->sc_minute = 0;
+ sc->sc_last_mg = 0L;
+
+ sc->sc_sync = 1;
+
+ sc->sc_current = 0L;
+ sc->sc_next = 0L;
+ sc->sc_nrecv = 0;
+ sc->sc_last = 0L;
+ sc->sc_last_tv.tv_sec = 0L;
+
+ /* convert timevals to hz */
+ t.tv_sec = 0L;
+ t.tv_usec = 150000L;
+ t_bv = tvtohz(&t);
+
+ t.tv_usec = 450000L;
+ t_mgsync = tvtohz(&t);
+
+ t.tv_usec = 950000L;
+ t_sync = tvtohz(&t);
+
+ t.tv_sec = 1L;
+ t.tv_usec = 500000L;
+ t_mg = tvtohz(&t);
+
+ t.tv_sec = 3L;
+ t.tv_usec = 0L;
+ t_sl = tvtohz(&t);
+
+ t.tv_sec = 5L;
+ t_wait = tvtohz(&t);
+
+ t.tv_sec = DPERIOD1;
+ t_warn = tvtohz(&t);
+
+ t.tv_sec = DPERIOD2;
+ t_crit = tvtohz(&t);
+
+ if (sc->sc_detect_ct) {
+ t.tv_sec = 0L;
+ t.tv_usec = 250000L;
+ t_ct = tvtohz(&t);
+ }
+
+ /* Give the receiver some slack to stabilize */
+ timeout_add(&sc->sc_to, t_wait);
+
+ /* Detect signal loss */
+ timeout_add(&sc->sc_sl_to, t_wait + t_sl);
+
+ DPRINTF(("synchronizing\n"));
+ return;
+
+fishy:
+ DPRINTF(("gpiodcf_attach failed\n"));
+ gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+ sc->sc_dying = 1;
+}
+
+int
+gpiodcf_detach(struct device *self, int flags)
+{
+ struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
+
+ sc->sc_dying = 1;
+
+ timeout_del(&sc->sc_to);
+ timeout_del(&sc->sc_bv_to);
+ timeout_del(&sc->sc_mg_to);
+ timeout_del(&sc->sc_sl_to);
+ timeout_del(&sc->sc_it_to);
+ timeout_del(&sc->sc_ct_to);
+
+ /* Unregister the clock with the kernel */
+ sensordev_deinstall(&sc->sc_sensordev);
+
+ /* Finally unmap the GPIO pin */
+ gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+
+ return 0;
+}
+
+/*
+ * return 1 during high-power-, 0 during low-power-emission
+ * If bit 0 is set, the transmitter emits at full power.
+ * During the low-power emission we decode a zero bit.
+ */
+int
+gpiodcf_signal(struct gpiodcf_softc *sc)
+{
+ return (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA) ==
+ GPIO_PIN_HIGH ? 1 : 0);
+}
+
+/* gpiodcf_probe runs in a process context. */
+void
+gpiodcf_probe(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+ struct timespec now;
+ int data;
+
+ if (sc->sc_dying)
+ return;
+
+ data = gpiodcf_signal(sc);
+ if (data == -1)
+ return;
+
+ if (data) {
+ sc->sc_level = 1;
+ timeout_add(&sc->sc_to, 1);
+ return;
+ }
+
+ if (sc->sc_level == 0)
+ return;
+
+ /* the beginning of a second */
+ sc->sc_level = 0;
+ if (sc->sc_minute == 1) {
+ if (sc->sc_sync) {
+ DPRINTF(("start collecting bits\n"));
+ sc->sc_sync = 0;
+ if (sc->sc_sensor.status == SENSOR_S_UNKNOWN &&
+ sc->sc_detect_ct)
+ sc->sc_clocktype = -1;
+ } else {
+ /* provide the timedelta */
+ microtime(&sc->sc_sensor.tv);
+ nanotime(&now);
+ sc->sc_current = sc->sc_next;
+ sc->sc_sensor.value = (int64_t)(now.tv_sec -
+ sc->sc_current) * 1000000000LL + now.tv_nsec;
+
+ /* set the clocktype and make sensor valid */
+ if (sc->sc_sensor.status == SENSOR_S_UNKNOWN &&
+ sc->sc_detect_ct) {
+ strlcpy(sc->sc_sensor.desc, sc->sc_clocktype ?
+ clockname[CLOCK_HBG] :
+ clockname[CLOCK_DCF77],
+ sizeof(sc->sc_sensor.desc));
+ }
+ sc->sc_sensor.status = SENSOR_S_OK;
+
+ /*
+ * if no valid time information is received
+ * during the next 5 minutes, the sensor state
+ * will be degraded to SENSOR_S_WARN
+ */
+ timeout_add(&sc->sc_it_to, t_warn);
+ }
+ sc->sc_minute = 0;
+ }
+
+ timeout_add(&sc->sc_to, t_sync); /* resync in 950 ms */
+
+ /* no clock and bit detection during sync */
+ if (!sc->sc_sync) {
+ /* detect bit value */
+ timeout_add(&sc->sc_bv_to, t_bv);
+
+ /* detect clocktype */
+ if (sc->sc_detect_ct && sc->sc_clocktype == -1)
+ timeout_add(&sc->sc_ct_to, t_ct);
+ }
+ timeout_add(&sc->sc_mg_to, t_mg); /* detect minute gap */
+ timeout_add(&sc->sc_sl_to, t_sl); /* detect signal loss */
+}
+
+/* detect the bit value */
+void
+gpiodcf_bv_probe(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+ int data;
+
+ if (sc->sc_dying)
+ return;
+
+ data = gpiodcf_signal(sc);
+ if (data == -1) {
+ DPRINTF(("bit detection failed\n"));
+ return;
+ }
+
+ DPRINTFN(1, (data ? "0" : "1"));
+ if (!(data))
+ sc->sc_tbits |= sc->sc_mask;
+ sc->sc_mask <<= 1;
+}
+
+/* detect the minute gap */
+void
+gpiodcf_mg_probe(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+ struct clock_ymdhms ymdhm;
+ struct timeval monotime;
+ int tdiff_recv, tdiff_local;
+ int skew;
+ int minute_bits, hour_bits, day_bits;
+ int month_bits, year_bits, wday;
+ int p1, p2, p3;
+ int p1_bit, p2_bit, p3_bit;
+ int r_bit, a1_bit, a2_bit, z1_bit, z2_bit;
+ int s_bit, m_bit;
+ u_int32_t parity = 0x6996;
+
+ if (sc->sc_sync) {
+ sc->sc_minute = 1;
+ goto cleanbits;
+ }
+
+ if (time_second - sc->sc_last_mg < 57) {
+ DPRINTF(("\nunexpected gap, resync\n"));
+ sc->sc_sync = sc->sc_minute = 1;
+ goto cleanbits;
+ }
+
+ /* extract bits w/o parity */
+ m_bit = sc->sc_tbits & 1;
+ r_bit = sc->sc_tbits >> 15 & 1;
+ a1_bit = sc->sc_tbits >> 16 & 1;
+ z1_bit = sc->sc_tbits >> 17 & 1;
+ z2_bit = sc->sc_tbits >> 18 & 1;
+ a2_bit = sc->sc_tbits >> 19 & 1;
+ s_bit = sc->sc_tbits >> 20 & 1;
+ p1_bit = sc->sc_tbits >> 28 & 1;
+ p2_bit = sc->sc_tbits >> 35 & 1;
+ p3_bit = sc->sc_tbits >> 58 & 1;
+
+ minute_bits = sc->sc_tbits >> 21 & 0x7f;
+ hour_bits = sc->sc_tbits >> 29 & 0x3f;
+ day_bits = sc->sc_tbits >> 36 & 0x3f;
+ wday = (sc->sc_tbits >> 42) & 0x07;
+ month_bits = sc->sc_tbits >> 45 & 0x1f;
+ year_bits = sc->sc_tbits >> 50 & 0xff;
+
+ /* validate time information */
+ p1 = (parity >> (minute_bits & 0x0f) & 1) ^
+ (parity >> (minute_bits >> 4) & 1);
+
+ p2 = (parity >> (hour_bits & 0x0f) & 1) ^
+ (parity >> (hour_bits >> 4) & 1);
+
+ p3 = (parity >> (day_bits & 0x0f) & 1) ^
+ (parity >> (day_bits >> 4) & 1) ^
+ ((parity >> wday) & 1) ^ (parity >> (month_bits & 0x0f) & 1) ^
+ (parity >> (month_bits >> 4) & 1) ^
+ (parity >> (year_bits & 0x0f) & 1) ^
+ (parity >> (year_bits >> 4) & 1);
+
+ if (m_bit == 0 && s_bit == 1 && p1 == p1_bit && p2 == p2_bit &&
+ p3 == p3_bit && (z1_bit ^ z2_bit)) {
+
+ /* Decode time */
+ if ((ymdhm.dt_year = 2000 + FROMBCD(year_bits)) > 2037) {
+ DPRINTF(("year out of range, resync\n"));
+ sc->sc_sync = 1;
+ goto cleanbits;
+ }
+ ymdhm.dt_min = FROMBCD(minute_bits);
+ ymdhm.dt_hour = FROMBCD(hour_bits);
+ ymdhm.dt_day = FROMBCD(day_bits);
+ ymdhm.dt_mon = FROMBCD(month_bits);
+ ymdhm.dt_sec = 0;
+
+ sc->sc_next = clock_ymdhms_to_secs(&ymdhm);
+ getmicrouptime(&monotime);
+
+ /* convert to coordinated universal time */
+ sc->sc_next -= z1_bit ? 7200 : 3600;
+
+ DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s",
+ ymdhm.dt_day, ymdhm.dt_mon, ymdhm.dt_year,
+ ymdhm.dt_hour, ymdhm.dt_min, z1_bit ? "CEST" : "CET"));
+ DPRINTF((r_bit ? ", call bit" : ""));
+ DPRINTF((a1_bit ? ", dst chg ann." : ""));
+ DPRINTF((a2_bit ? ", leap sec ann." : ""));
+ DPRINTF(("\n"));
+
+ if (sc->sc_last) {
+ tdiff_recv = sc->sc_next - sc->sc_last;
+ tdiff_local = monotime.tv_sec - sc->sc_last_tv.tv_sec;
+ skew = abs(tdiff_local - tdiff_recv);
+#ifdef GPIODCF_DEBUG
+ if (sc->sc_skew.status == SENSOR_S_UNKNOWN)
+ sc->sc_skew.status = SENSOR_S_CRIT;
+ sc->sc_skew.value = skew * 1000000000LL;
+ getmicrotime(&sc->sc_skew.tv);
+#endif
+ DPRINTF(("local = %d, recv = %d, skew = %d\n",
+ tdiff_local, tdiff_recv, skew));
+
+ if (skew && skew * 100LL / tdiff_local > MAX_SKEW) {
+ DPRINTF(("skew out of tolerated range\n"));
+ goto cleanbits;
+ } else {
+ if (sc->sc_nrecv < 2) {
+ sc->sc_nrecv++;
+ DPRINTF(("got frame %d\n",
+ sc->sc_nrecv));
+ } else {
+ DPRINTF(("data is valid\n"));
+ sc->sc_minute = 1;
+ }
+ }
+ } else {
+ DPRINTF(("received the first frame\n"));
+ sc->sc_nrecv = 1;
+ }
+
+ /* record the time received and when it was received */
+ sc->sc_last = sc->sc_next;
+ sc->sc_last_tv.tv_sec = monotime.tv_sec;
+ } else {
+ DPRINTF(("\nparity error, resync\n"));
+ sc->sc_sync = sc->sc_minute = 1;
+ }
+
+cleanbits:
+ timeout_add(&sc->sc_to, t_mgsync); /* re-sync in 450 ms */
+ sc->sc_last_mg = time_second;
+ sc->sc_tbits = 0LL;
+ sc->sc_mask = 1LL;
+}
+
+/* detect signal loss */
+void
+gpiodcf_sl_probe(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+
+ if (sc->sc_dying)
+ return;
+
+ DPRINTF(("no signal\n"));
+ sc->sc_sync = 1;
+ timeout_add(&sc->sc_to, t_wait);
+ timeout_add(&sc->sc_sl_to, t_wait + t_sl);
+}
+
+/* invalidate timedelta (called in an interrupt context) */
+void
+gpiodcf_invalidate(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+
+ if (sc->sc_dying)
+ return;
+
+ if (sc->sc_sensor.status == SENSOR_S_OK) {
+ sc->sc_sensor.status = SENSOR_S_WARN;
+ /*
+ * further degrade in 15 minutes if we dont receive any new
+ * time information
+ */
+ timeout_add(&sc->sc_it_to, t_crit);
+ } else {
+ sc->sc_sensor.status = SENSOR_S_CRIT;
+ sc->sc_nrecv = 0;
+ }
+}
+
+/* detect clock type. used for older devices only. */
+void
+gpiodcf_ct_probe(void *xsc)
+{
+ struct gpiodcf_softc *sc = xsc;
+ int data;
+
+ if (sc->sc_dying)
+ return;
+
+ data = gpiodcf_signal(sc);
+ if (data == -1) {
+ DPRINTF(("clocktype detection failed\n"));
+ return;
+ }
+
+ sc->sc_clocktype = data ? 0 : 1;
+ DPRINTF(("\nclocktype is %s\n", sc->sc_clocktype ?
+ clockname[CLOCK_HBG] : clockname[CLOCK_DCF77]));
+}
+
+int
+gpiodcf_activate(struct device *self, enum devact act)
+{
+ struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ break;
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return 0;
+}