The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/gpio/gpiodcf.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 ? "" : "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 }

Cache object: 71068db3de59f2363852272640a1c2d9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.