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/kern/tty_msts.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: 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 mstsattach(int dummy)
   80 {
   81 }
   82 
   83 int
   84 mstsopen(dev_t dev, struct tty *tp, struct proc *p)
   85 {
   86         struct msts *np;
   87         int error;
   88 
   89         DPRINTF(("mstsopen\n"));
   90         if (tp->t_line == MSTSDISC)
   91                 return ENODEV;
   92         if ((error = suser(p)) != 0)
   93                 return error;
   94         np = malloc(sizeof(struct msts), M_DEVBUF, M_WAITOK|M_ZERO);
   95         snprintf(np->timedev.xname, sizeof(np->timedev.xname), "msts%d",
   96             msts_nxid++);
   97         msts_count++;
   98         np->time.status = SENSOR_S_UNKNOWN;
   99         np->time.type = SENSOR_TIMEDELTA;
  100 #ifndef MSTS_DEBUG
  101         np->time.flags = SENSOR_FINVALID;
  102 #endif
  103         sensor_attach(&np->timedev, &np->time);
  104 
  105         np->signal.type = SENSOR_PERCENT;
  106         np->signal.status = SENSOR_S_UNKNOWN;
  107         np->signal.value = 100000LL;
  108         strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
  109         sensor_attach(&np->timedev, &np->signal);
  110 
  111         np->sync = 1;
  112         tp->t_sc = (caddr_t)np;
  113 
  114         error = linesw[TTYDISC].l_open(dev, tp, p);
  115         if (error) {
  116                 free(np, M_DEVBUF, sizeof(*np));
  117                 tp->t_sc = NULL;
  118         } else {
  119                 sensordev_install(&np->timedev);
  120                 timeout_set(&np->msts_tout, msts_timeout, np);
  121         }
  122 
  123         return error;
  124 }
  125 
  126 int
  127 mstsclose(struct tty *tp, int flags, struct proc *p)
  128 {
  129         struct msts *np = (struct msts *)tp->t_sc;
  130 
  131         tp->t_line = TTYDISC;   /* switch back to termios */
  132         timeout_del(&np->msts_tout);
  133         sensordev_deinstall(&np->timedev);
  134         free(np, M_DEVBUF, sizeof(*np));
  135         tp->t_sc = NULL;
  136         msts_count--;
  137         if (msts_count == 0)
  138                 msts_nxid = 0;
  139         return linesw[TTYDISC].l_close(tp, flags, p);
  140 }
  141 
  142 /* collect MSTS sentence from tty */
  143 int
  144 mstsinput(int c, struct tty *tp)
  145 {
  146         struct msts *np = (struct msts *)tp->t_sc;
  147         struct timespec ts;
  148         int64_t gap;
  149         long tmin, tmax;
  150 
  151         switch (c) {
  152         case 2:         /* ASCII <STX> */
  153                 nanotime(&ts);
  154                 np->pos = np->sync = 0;
  155                 gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
  156                     (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
  157 
  158                 np->lts.tv_sec = ts.tv_sec;
  159                 np->lts.tv_nsec = ts.tv_nsec;
  160 
  161                 if (gap <= np->gap)
  162                         break;
  163 
  164                 np->ts.tv_sec = ts.tv_sec;
  165                 np->ts.tv_nsec = ts.tv_nsec;
  166                 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                 if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
  177                     TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
  178                         tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
  179                         tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
  180                         if (tmax - tmin > 1)
  181                                 np->no_pps = 1;
  182                         else {
  183                                 np->ts.tv_sec = tp->t_tv.tv_sec;
  184                                 np->ts.tv_nsec = tp->t_tv.tv_usec *
  185                                     1000L;
  186                                 np->no_pps = 0;
  187                         }
  188                 }
  189                 break;
  190         case 3:         /* ASCII <ETX> */
  191                 if (!np->sync) {
  192                         np->cbuf[np->pos] = '\0';
  193                         msts_scan(np, tp);
  194                         np->sync = 1;
  195                 }
  196                 break;
  197         default:
  198                 if (!np->sync && np->pos < (MSTSMAX - 1))
  199                         np->cbuf[np->pos++] = c;
  200                 break;
  201         }
  202         /* pass data to termios */
  203         return linesw[TTYDISC].l_rint(c, tp);
  204 }
  205 
  206 /* Scan the MSTS sentence just received */
  207 void
  208 msts_scan(struct msts *np, struct tty *tp)
  209 {
  210         int fldcnt = 0, n;
  211         char *fld[MAXFLDS], *cs;
  212 
  213         /* split into fields */
  214         fld[fldcnt++] = &np->cbuf[0];
  215         for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
  216                 switch (np->cbuf[n]) {
  217                 case 3:         /* ASCII <ETX> */
  218                         np->cbuf[n] = '\0';
  219                         cs = &np->cbuf[n + 1];
  220                         break;
  221                 case ';':
  222                         if (fldcnt < MAXFLDS) {
  223                                 np->cbuf[n] = '\0';
  224                                 fld[fldcnt++] = &np->cbuf[n + 1];
  225                         } else {
  226                                 DPRINTF(("nr of fields in sentence exceeds "
  227                                     "maximum of %d\n", MAXFLDS));
  228                                 return;
  229                         }
  230                         break;
  231                 }
  232         }
  233         msts_decode(np, tp, fld, fldcnt);
  234 }
  235 
  236 /* Decode the time string */
  237 void
  238 msts_decode(struct msts *np, struct tty *tp, char *fld[], int fldcnt)
  239 {
  240         int64_t date_nano, time_nano, msts_now;
  241         int jumped = 0;
  242 
  243         if (fldcnt != MAXFLDS) {
  244                 DPRINTF(("msts: field count mismatch, %d\n", fldcnt));
  245                 return;
  246         }
  247         if (msts_time_to_nano(fld[2], &time_nano)) {
  248                 DPRINTF(("msts: illegal time, %s\n", fld[2]));
  249                 return;
  250         }
  251         if (msts_date_to_nano(fld[0], &date_nano)) {
  252                 DPRINTF(("msts: illegal date, %s\n", fld[0]));
  253                 return;
  254         }
  255         msts_now = date_nano + time_nano;
  256         if ( fld[3][2] == ' ' )         /* received time in CET */
  257                 msts_now = msts_now - 3600 * 1000000000LL;
  258         if ( fld[3][2] == 'S' )         /* received time in CEST */
  259                 msts_now = msts_now - 2 * 3600 * 1000000000LL;
  260         if (msts_now <= np->last) {
  261                 DPRINTF(("msts: time not monotonically increasing\n"));
  262                 jumped = 1;
  263         }
  264         np->last = msts_now;
  265         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         np->time.value = np->ts.tv_sec * 1000000000LL +
  274             np->ts.tv_nsec - msts_now;
  275         np->time.tv.tv_sec = np->ts.tv_sec;
  276         np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
  277         if (np->time.status == SENSOR_S_UNKNOWN) {
  278                 np->time.status = SENSOR_S_OK;
  279                 np->time.flags &= ~SENSOR_FINVALID;
  280                 strlcpy(np->time.desc, "MSTS", sizeof(np->time.desc));
  281         }
  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         if (fld[3][0] == ' ' && fld[3][1] == ' ') {
  290                 np->time.status = SENSOR_S_OK;
  291                 np->signal.status = SENSOR_S_OK;
  292         } else
  293                 np->signal.status = SENSOR_S_WARN;
  294 
  295         if (jumped)
  296                 np->time.status = SENSOR_S_WARN;
  297         if (np->time.status == SENSOR_S_OK)
  298                 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         if (np->no_pps)
  305                 np->time.status = SENSOR_S_CRIT;
  306 }
  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 msts_date_to_nano(char *s, int64_t *nano)
  315 {
  316         struct clock_ymdhms ymd;
  317         time_t secs;
  318         char *p;
  319         int n;
  320 
  321         if (s[0] != 'D' || s[1] != ':' || s[4] != '.' || s[7] != '.')
  322                 return -1;
  323 
  324         /* shift numbers to DDMMYY */
  325         s[0]=s[2];
  326         s[1]=s[3];
  327         s[2]=s[5];
  328         s[3]=s[6];
  329         s[4]=s[8];
  330         s[5]=s[9];
  331         s[6]='\0';
  332 
  333         /* make sure the input contains only numbers and is six digits long */
  334         for (n = 0, p = s; n < 6 && *p && *p >= '' && *p <= '9'; n++, p++)
  335                 ;
  336         if (n != 6 || (*p != '\0'))
  337                 return -1;
  338 
  339         ymd.dt_year = 2000 + (s[4] - '') * 10 + (s[5] - '');
  340         ymd.dt_mon = (s[2] - '') * 10 + (s[3] - '');
  341         ymd.dt_day = (s[0] - '') * 10 + (s[1] - '');
  342         ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
  343 
  344         secs = clock_ymdhms_to_secs(&ymd);
  345         *nano = secs * 1000000000LL;
  346         return 0;
  347 }
  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 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         if (s[0] != 'U' || s[1] != ':' || s[4] != '.' || s[7] != '.')
  362                 return -1;
  363 
  364         /* shift numbers to HHMMSS */
  365         s[0]=s[2];
  366         s[1]=s[3];
  367         s[2]=s[5];
  368         s[3]=s[6];
  369         s[4]=s[8];
  370         s[5]=s[9];
  371         s[6]='\0';
  372 
  373         for (n = 0, secs = 0; fac && *s && *s >= '' && *s <= ul; s++, n++) {
  374                 secs += (*s - '') * fac;
  375                 div = 16 - div;
  376                 fac /= div;
  377                 switch (n) {
  378                 case 0:
  379                         if (*s <= '1')
  380                                 ul = '9';
  381                         else
  382                                 ul = '3';
  383                         break;
  384                 case 1:
  385                 case 3:
  386                         ul = '5';
  387                         break;
  388                 case 2:
  389                 case 4:
  390                         ul = '9';
  391                         break;
  392                 }
  393         }
  394         if (fac)
  395                 return -1;
  396 
  397         if (*s != '\0')
  398                 return -1;
  399 
  400         *nano = secs * 1000000000LL;
  401         return 0;
  402 }
  403 
  404 /*
  405  * Degrade the sensor state if we received no MSTS string for more than
  406  * TRUSTTIME seconds.
  407  */
  408 void
  409 msts_timeout(void *xnp)
  410 {
  411         struct msts *np = xnp;
  412 
  413         if (np->time.status == SENSOR_S_OK) {
  414                 np->time.status = SENSOR_S_WARN;
  415                 /*
  416                  * further degrade in TRUSTTIME seconds if no new valid MSTS
  417                  * strings are received.
  418                  */
  419                 timeout_add_sec(&np->msts_tout, TRUSTTIME);
  420         } else
  421                 np->time.status = SENSOR_S_CRIT;
  422 }

Cache object: 487f13a98b7ed3f5475cae95a95e6aaf


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