LCOV - code coverage report
Current view: top level - kern - tty_msts.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 182 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: tty_msts.c,v 1.21 2018/02/19 08:59:52 mpi 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             : /*
      20             :  *  A tty line discipline to decode the Meinberg Standard Time String
      21             :  *  to get the time (http://www.meinberg.de/english/specs/timestr.htm).
      22             :  */
      23             : 
      24             : #include <sys/param.h>
      25             : #include <sys/systm.h>
      26             : #include <sys/malloc.h>
      27             : #include <sys/sensors.h>
      28             : #include <sys/tty.h>
      29             : #include <sys/conf.h>
      30             : #include <sys/time.h>
      31             : 
      32             : #ifdef MSTS_DEBUG
      33             : #define DPRINTFN(n, x)  do { if (mstsdebug > (n)) printf x; } while (0)
      34             : int mstsdebug = 0;
      35             : #else
      36             : #define DPRINTFN(n, x)
      37             : #endif
      38             : #define DPRINTF(x)      DPRINTFN(0, x)
      39             : 
      40             : void    mstsattach(int);
      41             : 
      42             : #define MSTSMAX 32
      43             : #define MAXFLDS 4
      44             : #ifdef MSTS_DEBUG
      45             : #define TRUSTTIME       30
      46             : #else
      47             : #define TRUSTTIME       (10 * 60)       /* 10 minutes */
      48             : #endif
      49             : 
      50             : int msts_count, msts_nxid;
      51             : 
      52             : struct msts {
      53             :         char                    cbuf[MSTSMAX];  /* receive buffer */
      54             :         struct ksensor          time;           /* the timedelta sensor */
      55             :         struct ksensor          signal;         /* signal status */
      56             :         struct ksensordev       timedev;
      57             :         struct timespec         ts;             /* current timestamp */
      58             :         struct timespec         lts;            /* timestamp of last <STX> */
      59             :         struct timeout          msts_tout;      /* invalidate sensor */
      60             :         int64_t                 gap;            /* gap between two sentences */
      61             :         int64_t                 last;           /* last time rcvd */
      62             :         int                     sync;           /* if 1, waiting for <STX> */
      63             :         int                     pos;            /* position in rcv buffer */
      64             :         int                     no_pps;         /* no PPS although requested */
      65             : };
      66             : 
      67             : /* MSTS decoding */
      68             : void    msts_scan(struct msts *, struct tty *);
      69             : void    msts_decode(struct msts *, struct tty *, char *fld[], int fldcnt);
      70             : 
      71             : /* date and time conversion */
      72             : int     msts_date_to_nano(char *s, int64_t *nano);
      73             : int     msts_time_to_nano(char *s, int64_t *nano);
      74             : 
      75             : /* degrade the timedelta sensor */
      76             : void    msts_timeout(void *);
      77             : 
      78             : void
      79           0 : mstsattach(int dummy)
      80             : {
      81           0 : }
      82             : 
      83             : int
      84           0 : mstsopen(dev_t dev, struct tty *tp, struct proc *p)
      85             : {
      86             :         struct msts *np;
      87             :         int error;
      88             : 
      89             :         DPRINTF(("mstsopen\n"));
      90           0 :         if (tp->t_line == MSTSDISC)
      91           0 :                 return ENODEV;
      92           0 :         if ((error = suser(p)) != 0)
      93           0 :                 return error;
      94           0 :         np = malloc(sizeof(struct msts), M_DEVBUF, M_WAITOK|M_ZERO);
      95           0 :         snprintf(np->timedev.xname, sizeof(np->timedev.xname), "msts%d",
      96           0 :             msts_nxid++);
      97           0 :         msts_count++;
      98           0 :         np->time.status = SENSOR_S_UNKNOWN;
      99           0 :         np->time.type = SENSOR_TIMEDELTA;
     100             : #ifndef MSTS_DEBUG
     101           0 :         np->time.flags = SENSOR_FINVALID;
     102             : #endif
     103           0 :         sensor_attach(&np->timedev, &np->time);
     104             : 
     105           0 :         np->signal.type = SENSOR_PERCENT;
     106           0 :         np->signal.status = SENSOR_S_UNKNOWN;
     107           0 :         np->signal.value = 100000LL;
     108           0 :         strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
     109           0 :         sensor_attach(&np->timedev, &np->signal);
     110             : 
     111           0 :         np->sync = 1;
     112           0 :         tp->t_sc = (caddr_t)np;
     113             : 
     114           0 :         error = linesw[TTYDISC].l_open(dev, tp, p);
     115           0 :         if (error) {
     116           0 :                 free(np, M_DEVBUF, sizeof(*np));
     117           0 :                 tp->t_sc = NULL;
     118           0 :         } else {
     119           0 :                 sensordev_install(&np->timedev);
     120           0 :                 timeout_set(&np->msts_tout, msts_timeout, np);
     121             :         }
     122             : 
     123           0 :         return error;
     124           0 : }
     125             : 
     126             : int
     127           0 : mstsclose(struct tty *tp, int flags, struct proc *p)
     128             : {
     129           0 :         struct msts *np = (struct msts *)tp->t_sc;
     130             : 
     131           0 :         tp->t_line = TTYDISC;        /* switch back to termios */
     132           0 :         timeout_del(&np->msts_tout);
     133           0 :         sensordev_deinstall(&np->timedev);
     134           0 :         free(np, M_DEVBUF, sizeof(*np));
     135           0 :         tp->t_sc = NULL;
     136           0 :         msts_count--;
     137           0 :         if (msts_count == 0)
     138           0 :                 msts_nxid = 0;
     139           0 :         return linesw[TTYDISC].l_close(tp, flags, p);
     140             : }
     141             : 
     142             : /* collect MSTS sentence from tty */
     143             : int
     144           0 : mstsinput(int c, struct tty *tp)
     145             : {
     146           0 :         struct msts *np = (struct msts *)tp->t_sc;
     147           0 :         struct timespec ts;
     148             :         int64_t gap;
     149             :         long tmin, tmax;
     150             : 
     151           0 :         switch (c) {
     152             :         case 2:         /* ASCII <STX> */
     153           0 :                 nanotime(&ts);
     154           0 :                 np->pos = np->sync = 0;
     155           0 :                 gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
     156           0 :                     (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
     157             : 
     158           0 :                 np->lts.tv_sec = ts.tv_sec;
     159           0 :                 np->lts.tv_nsec = ts.tv_nsec;
     160             : 
     161           0 :                 if (gap <= np->gap)
     162             :                         break;
     163             : 
     164           0 :                 np->ts.tv_sec = ts.tv_sec;
     165           0 :                 np->ts.tv_nsec = ts.tv_nsec;
     166           0 :                 np->gap = gap;
     167             : 
     168             :                 /*
     169             :                  * If a tty timestamp is available, make sure its value is
     170             :                  * reasonable by comparing against the timestamp just taken.
     171             :                  * If they differ by more than 2 seconds, assume no PPS signal
     172             :                  * is present, note the fact, and keep using the timestamp
     173             :                  * value.  When this happens, the sensor state is set to
     174             :                  * CRITICAL later when the MSTS sentence is decoded.
     175             :                  */
     176           0 :                 if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
     177             :                     TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
     178           0 :                         tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
     179           0 :                         tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
     180           0 :                         if (tmax - tmin > 1)
     181           0 :                                 np->no_pps = 1;
     182             :                         else {
     183           0 :                                 np->ts.tv_sec = tp->t_tv.tv_sec;
     184           0 :                                 np->ts.tv_nsec = tp->t_tv.tv_usec *
     185             :                                     1000L;
     186           0 :                                 np->no_pps = 0;
     187             :                         }
     188             :                 }
     189             :                 break;
     190             :         case 3:         /* ASCII <ETX> */
     191           0 :                 if (!np->sync) {
     192           0 :                         np->cbuf[np->pos] = '\0';
     193           0 :                         msts_scan(np, tp);
     194           0 :                         np->sync = 1;
     195           0 :                 }
     196             :                 break;
     197             :         default:
     198           0 :                 if (!np->sync && np->pos < (MSTSMAX - 1))
     199           0 :                         np->cbuf[np->pos++] = c;
     200             :                 break;
     201             :         }
     202             :         /* pass data to termios */
     203           0 :         return linesw[TTYDISC].l_rint(c, tp);
     204           0 : }
     205             : 
     206             : /* Scan the MSTS sentence just received */
     207             : void
     208           0 : msts_scan(struct msts *np, struct tty *tp)
     209             : {
     210             :         int fldcnt = 0, n;
     211           0 :         char *fld[MAXFLDS], *cs;
     212             : 
     213             :         /* split into fields */
     214           0 :         fld[fldcnt++] = &np->cbuf[0];
     215           0 :         for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
     216           0 :                 switch (np->cbuf[n]) {
     217             :                 case 3:         /* ASCII <ETX> */
     218           0 :                         np->cbuf[n] = '\0';
     219           0 :                         cs = &np->cbuf[n + 1];
     220           0 :                         break;
     221             :                 case ';':
     222           0 :                         if (fldcnt < MAXFLDS) {
     223           0 :                                 np->cbuf[n] = '\0';
     224           0 :                                 fld[fldcnt++] = &np->cbuf[n + 1];
     225             :                         } else {
     226             :                                 DPRINTF(("nr of fields in sentence exceeds "
     227             :                                     "maximum of %d\n", MAXFLDS));
     228           0 :                                 return;
     229             :                         }
     230           0 :                         break;
     231             :                 }
     232             :         }
     233           0 :         msts_decode(np, tp, fld, fldcnt);
     234           0 : }
     235             : 
     236             : /* Decode the time string */
     237             : void
     238           0 : msts_decode(struct msts *np, struct tty *tp, char *fld[], int fldcnt)
     239             : {
     240           0 :         int64_t date_nano, time_nano, msts_now;
     241             :         int jumped = 0;
     242             : 
     243           0 :         if (fldcnt != MAXFLDS) {
     244             :                 DPRINTF(("msts: field count mismatch, %d\n", fldcnt));
     245           0 :                 return;
     246             :         }
     247           0 :         if (msts_time_to_nano(fld[2], &time_nano)) {
     248             :                 DPRINTF(("msts: illegal time, %s\n", fld[2]));
     249           0 :                 return;
     250             :         }
     251           0 :         if (msts_date_to_nano(fld[0], &date_nano)) {
     252             :                 DPRINTF(("msts: illegal date, %s\n", fld[0]));
     253           0 :                 return;
     254             :         }
     255           0 :         msts_now = date_nano + time_nano;
     256           0 :         if ( fld[3][2] == ' ' )         /* received time in CET */
     257           0 :                 msts_now = msts_now - 3600 * 1000000000LL;
     258           0 :         if ( fld[3][2] == 'S' )         /* received time in CEST */
     259           0 :                 msts_now = msts_now - 2 * 3600 * 1000000000LL;
     260           0 :         if (msts_now <= np->last) {
     261             :                 DPRINTF(("msts: time not monotonically increasing\n"));
     262             :                 jumped = 1;
     263           0 :         }
     264           0 :         np->last = msts_now;
     265           0 :         np->gap = 0LL;
     266             : #ifdef MSTS_DEBUG
     267             :         if (np->time.status == SENSOR_S_UNKNOWN) {
     268             :                 np->time.status = SENSOR_S_OK;
     269             :                 timeout_add_sec(&np->msts_tout, TRUSTTIME);
     270             :         }
     271             : #endif
     272             : 
     273           0 :         np->time.value = np->ts.tv_sec * 1000000000LL +
     274           0 :             np->ts.tv_nsec - msts_now;
     275           0 :         np->time.tv.tv_sec = np->ts.tv_sec;
     276           0 :         np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
     277           0 :         if (np->time.status == SENSOR_S_UNKNOWN) {
     278           0 :                 np->time.status = SENSOR_S_OK;
     279           0 :                 np->time.flags &= ~SENSOR_FINVALID;
     280           0 :                 strlcpy(np->time.desc, "MSTS", sizeof(np->time.desc));
     281           0 :         }
     282             :         /*
     283             :          * only update the timeout if the clock reports the time a valid,
     284             :          * the status is reported in fld[3][0] and fld[3][1] as follows:
     285             :          * fld[3][0] == '#'                             critical
     286             :          * fld[3][0] == ' ' && fld[3][1] == '*'         warning
     287             :          * fld[3][0] == ' ' && fld[3][1] == ' '         ok
     288             :          */
     289           0 :         if (fld[3][0] == ' ' && fld[3][1] == ' ') {
     290           0 :                 np->time.status = SENSOR_S_OK;
     291           0 :                 np->signal.status = SENSOR_S_OK;
     292           0 :         } else
     293           0 :                 np->signal.status = SENSOR_S_WARN;
     294             : 
     295           0 :         if (jumped)
     296           0 :                 np->time.status = SENSOR_S_WARN;
     297           0 :         if (np->time.status == SENSOR_S_OK)
     298           0 :                 timeout_add_sec(&np->msts_tout, TRUSTTIME);
     299             : 
     300             :         /*
     301             :          * If tty timestamping is requested, but no PPS signal is present, set
     302             :          * the sensor state to CRITICAL.
     303             :          */
     304           0 :         if (np->no_pps)
     305           0 :                 np->time.status = SENSOR_S_CRIT;
     306           0 : }
     307             : 
     308             : /*
     309             :  * Convert date field from MSTS to nanoseconds since the epoch.
     310             :  * The string must be of the form D:DD.MM.YY .
     311             :  * Return 0 on success, -1 if illegal characters are encountered.
     312             :  */
     313             : int
     314           0 : msts_date_to_nano(char *s, int64_t *nano)
     315             : {
     316           0 :         struct clock_ymdhms ymd;
     317             :         time_t secs;
     318             :         char *p;
     319             :         int n;
     320             : 
     321           0 :         if (s[0] != 'D' || s[1] != ':' || s[4] != '.' || s[7] != '.')
     322           0 :                 return -1;
     323             : 
     324             :         /* shift numbers to DDMMYY */
     325           0 :         s[0]=s[2];
     326           0 :         s[1]=s[3];
     327           0 :         s[2]=s[5];
     328           0 :         s[3]=s[6];
     329           0 :         s[4]=s[8];
     330           0 :         s[5]=s[9];
     331           0 :         s[6]='\0';
     332             : 
     333             :         /* make sure the input contains only numbers and is six digits long */
     334           0 :         for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
     335             :                 ;
     336           0 :         if (n != 6 || (*p != '\0'))
     337           0 :                 return -1;
     338             : 
     339           0 :         ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
     340           0 :         ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
     341           0 :         ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
     342           0 :         ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
     343             : 
     344           0 :         secs = clock_ymdhms_to_secs(&ymd);
     345           0 :         *nano = secs * 1000000000LL;
     346           0 :         return 0;
     347           0 : }
     348             : 
     349             : /*
     350             :  * Convert time field from MSTS to nanoseconds since midnight.
     351             :  * The string must be of the form U:HH.MM.SS .
     352             :  * Return 0 on success, -1 if illegal characters are encountered.
     353             :  */
     354             : int
     355           0 : msts_time_to_nano(char *s, int64_t *nano)
     356             : {
     357             :         long fac = 36000L, div = 6L, secs = 0L;
     358             :         char ul = '2';
     359             :         int n;
     360             : 
     361           0 :         if (s[0] != 'U' || s[1] != ':' || s[4] != '.' || s[7] != '.')
     362           0 :                 return -1;
     363             : 
     364             :         /* shift numbers to HHMMSS */
     365           0 :         s[0]=s[2];
     366           0 :         s[1]=s[3];
     367           0 :         s[2]=s[5];
     368           0 :         s[3]=s[6];
     369           0 :         s[4]=s[8];
     370           0 :         s[5]=s[9];
     371           0 :         s[6]='\0';
     372             : 
     373           0 :         for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
     374           0 :                 secs += (*s - '0') * fac;
     375           0 :                 div = 16 - div;
     376           0 :                 fac /= div;
     377           0 :                 switch (n) {
     378             :                 case 0:
     379           0 :                         if (*s <= '1')
     380           0 :                                 ul = '9';
     381             :                         else
     382             :                                 ul = '3';
     383             :                         break;
     384             :                 case 1:
     385             :                 case 3:
     386             :                         ul = '5';
     387           0 :                         break;
     388             :                 case 2:
     389             :                 case 4:
     390             :                         ul = '9';
     391           0 :                         break;
     392             :                 }
     393             :         }
     394           0 :         if (fac)
     395           0 :                 return -1;
     396             : 
     397           0 :         if (*s != '\0')
     398           0 :                 return -1;
     399             : 
     400           0 :         *nano = secs * 1000000000LL;
     401           0 :         return 0;
     402           0 : }
     403             : 
     404             : /*
     405             :  * Degrade the sensor state if we received no MSTS string for more than
     406             :  * TRUSTTIME seconds.
     407             :  */
     408             : void
     409           0 : msts_timeout(void *xnp)
     410             : {
     411           0 :         struct msts *np = xnp;
     412             : 
     413           0 :         if (np->time.status == SENSOR_S_OK) {
     414           0 :                 np->time.status = SENSOR_S_WARN;
     415             :                 /*
     416             :                  * further degrade in TRUSTTIME seconds if no new valid MSTS
     417             :                  * strings are received.
     418             :                  */
     419           0 :                 timeout_add_sec(&np->msts_tout, TRUSTTIME);
     420           0 :         } else
     421           0 :                 np->time.status = SENSOR_S_CRIT;
     422           0 : }

Generated by: LCOV version 1.13