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

          Line data    Source code
       1             : /*      $OpenBSD: tty_endrun.c,v 1.8 2018/02/19 08:59:52 mpi Exp $ */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
       5             :  * Copyright (c) 2009 Kevin Steves <stevesk@openbsd.org>
       6             :  *
       7             :  * Permission to use, copy, modify, and distribute this software for any
       8             :  * purpose with or without fee is hereby granted, provided that the above
       9             :  * copyright notice and this permission notice appear in all copies.
      10             :  *
      11             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      12             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      13             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      14             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      15             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      16             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      17             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      18             :  */
      19             : 
      20             : /*
      21             :  * A tty line discipline to decode the EndRun Technologies native
      22             :  * time-of-day message.
      23             :  * http://www.endruntechnologies.com/
      24             :  */
      25             : 
      26             : /*
      27             :  * EndRun Format:
      28             :  *
      29             :  * T YYYY DDD HH:MM:SS zZZ m<CR><LF>
      30             :  *
      31             :  * T is the Time Figure of Merit (TFOM) character (described below).
      32             :  * This is the on-time character, transmitted during the first
      33             :  * millisecond of each second.
      34             :  *
      35             :  * YYYY is the year
      36             :  * DDD is the day-of-year
      37             :  * : is the colon character (0x3A)
      38             :  * HH is the hour of the day
      39             :  * MM is the minute of the hour
      40             :  * SS is the second of the minute
      41             :  * z is the sign of the offset to UTC, + implies time is ahead of UTC.
      42             :  * ZZ is the magnitude of the offset to UTC in units of half-hours.
      43             :  * Non-zero only when the Timemode is Local.
      44             :  * m is the Timemode character and is one of:
      45             :  *   G = GPS
      46             :  *   L = Local
      47             :  *   U = UTC
      48             :  * <CR> is the ASCII carriage return character (0x0D)
      49             :  * <LF> is the ASCII line feed character (0x0A)
      50             :  */
      51             : 
      52             : #include <sys/param.h>
      53             : #include <sys/systm.h>
      54             : #include <sys/malloc.h>
      55             : #include <sys/sensors.h>
      56             : #include <sys/tty.h>
      57             : #include <sys/conf.h>
      58             : #include <sys/time.h>
      59             : 
      60             : #ifdef ENDRUN_DEBUG
      61             : #define DPRINTFN(n, x)  do { if (endrundebug > (n)) printf x; } while (0)
      62             : int endrundebug = 0;
      63             : #else
      64             : #define DPRINTFN(n, x)
      65             : #endif
      66             : #define DPRINTF(x)      DPRINTFN(0, x)
      67             : 
      68             : void    endrunattach(int);
      69             : 
      70             : #define ENDRUNLEN       27 /* strlen("6 2009 018 20:41:17 +00 U\r\n") */
      71             : #define NUMFLDS         6
      72             : #ifdef ENDRUN_DEBUG
      73             : #define TRUSTTIME       30
      74             : #else
      75             : #define TRUSTTIME       (10 * 60)       /* 10 minutes */
      76             : #endif
      77             : 
      78             : int endrun_count, endrun_nxid;
      79             : 
      80             : struct endrun {
      81             :         char                    cbuf[ENDRUNLEN];        /* receive buffer */
      82             :         struct ksensor          time;           /* the timedelta sensor */
      83             :         struct ksensor          signal;         /* signal status */
      84             :         struct ksensordev       timedev;
      85             :         struct timespec         ts;             /* current timestamp */
      86             :         struct timespec         lts;            /* timestamp of last TFOM */
      87             :         struct timeout          endrun_tout;    /* invalidate sensor */
      88             :         int64_t                 gap;            /* gap between two sentences */
      89             :         int64_t                 last;           /* last time rcvd */
      90             : #define SYNC_SCAN       1       /* scanning for '\n' */
      91             : #define SYNC_EOL        2       /* '\n' seen, next char TFOM */
      92             :         int                     sync;
      93             :         int                     pos;            /* position in rcv buffer */
      94             :         int                     no_pps;         /* no PPS although requested */
      95             : #ifdef ENDRUN_DEBUG
      96             :         char                    tfom;
      97             : #endif
      98             : };
      99             : 
     100             : /* EndRun decoding */
     101             : void    endrun_scan(struct endrun *, struct tty *);
     102             : void    endrun_decode(struct endrun *, struct tty *, char *fld[], int fldcnt);
     103             : 
     104             : /* date and time conversion */
     105             : int     endrun_atoi(char *s, int len);
     106             : int     endrun_date_to_nano(char *s1, char *s2, int64_t *nano);
     107             : int     endrun_time_to_nano(char *s, int64_t *nano);
     108             : int     endrun_offset_to_nano(char *s, int64_t *nano);
     109             : 
     110             : /* degrade the timedelta sensor */
     111             : void    endrun_timeout(void *);
     112             : 
     113             : void
     114           0 : endrunattach(int dummy)
     115             : {
     116           0 : }
     117             : 
     118             : int
     119           0 : endrunopen(dev_t dev, struct tty *tp, struct proc *p)
     120             : {
     121             :         struct endrun *np;
     122             :         int error;
     123             : 
     124             :         DPRINTF(("endrunopen\n"));
     125           0 :         if (tp->t_line == ENDRUNDISC)
     126           0 :                 return ENODEV;
     127           0 :         if ((error = suser(p)) != 0)
     128           0 :                 return error;
     129           0 :         np = malloc(sizeof(struct endrun), M_DEVBUF, M_WAITOK|M_ZERO);
     130           0 :         snprintf(np->timedev.xname, sizeof(np->timedev.xname), "endrun%d",
     131           0 :             endrun_nxid++);
     132           0 :         endrun_count++;
     133           0 :         np->time.status = SENSOR_S_UNKNOWN;
     134           0 :         np->time.type = SENSOR_TIMEDELTA;
     135             : #ifndef ENDRUN_DEBUG
     136           0 :         np->time.flags = SENSOR_FINVALID;
     137             : #endif
     138           0 :         sensor_attach(&np->timedev, &np->time);
     139             : 
     140           0 :         np->signal.type = SENSOR_PERCENT;
     141           0 :         np->signal.status = SENSOR_S_UNKNOWN;
     142           0 :         np->signal.value = 100000LL;
     143           0 :         strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
     144           0 :         sensor_attach(&np->timedev, &np->signal);
     145             : 
     146           0 :         np->sync = SYNC_SCAN;
     147             : #ifdef ENDRUN_DEBUG
     148             :         np->tfom = '0';
     149             : #endif
     150           0 :         tp->t_sc = (caddr_t)np;
     151             : 
     152           0 :         error = linesw[TTYDISC].l_open(dev, tp, p);
     153           0 :         if (error) {
     154           0 :                 free(np, M_DEVBUF, sizeof(*np));
     155           0 :                 tp->t_sc = NULL;
     156           0 :         } else {
     157           0 :                 sensordev_install(&np->timedev);
     158           0 :                 timeout_set(&np->endrun_tout, endrun_timeout, np);
     159             :         }
     160             : 
     161           0 :         return error;
     162           0 : }
     163             : 
     164             : int
     165           0 : endrunclose(struct tty *tp, int flags, struct proc *p)
     166             : {
     167           0 :         struct endrun *np = (struct endrun *)tp->t_sc;
     168             : 
     169             :         DPRINTF(("endrunclose\n"));
     170           0 :         tp->t_line = TTYDISC;        /* switch back to termios */
     171           0 :         timeout_del(&np->endrun_tout);
     172           0 :         sensordev_deinstall(&np->timedev);
     173           0 :         free(np, M_DEVBUF, sizeof(*np));
     174           0 :         tp->t_sc = NULL;
     175           0 :         endrun_count--;
     176           0 :         if (endrun_count == 0)
     177           0 :                 endrun_nxid = 0;
     178           0 :         return linesw[TTYDISC].l_close(tp, flags, p);
     179             : }
     180             : 
     181             : /* collect EndRun sentence from tty */
     182             : int
     183           0 : endruninput(int c, struct tty *tp)
     184             : {
     185           0 :         struct endrun *np = (struct endrun *)tp->t_sc;
     186           0 :         struct timespec ts;
     187             :         int64_t gap;
     188             :         long tmin, tmax;
     189             : 
     190           0 :         if (np->sync == SYNC_EOL) {
     191           0 :                 nanotime(&ts);
     192           0 :                 np->pos = 0;
     193           0 :                 np->sync = SYNC_SCAN;
     194           0 :                 np->cbuf[np->pos++] = c; /* TFOM char */
     195             : 
     196           0 :                 gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
     197           0 :                     (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
     198             : 
     199           0 :                 np->lts.tv_sec = ts.tv_sec;
     200           0 :                 np->lts.tv_nsec = ts.tv_nsec;
     201             : 
     202           0 :                 if (gap <= np->gap)
     203             :                         goto nogap;
     204             : 
     205           0 :                 np->ts.tv_sec = ts.tv_sec;
     206           0 :                 np->ts.tv_nsec = ts.tv_nsec;
     207           0 :                 np->gap = gap;
     208             : 
     209             :                 /*
     210             :                  * If a tty timestamp is available, make sure its value is
     211             :                  * reasonable by comparing against the timestamp just taken.
     212             :                  * If they differ by more than 2 seconds, assume no PPS signal
     213             :                  * is present, note the fact, and keep using the timestamp
     214             :                  * value.  When this happens, the sensor state is set to
     215             :                  * CRITICAL later when the EndRun sentence is decoded.
     216             :                  */
     217           0 :                 if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
     218             :                     TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
     219           0 :                         tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
     220           0 :                         tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
     221           0 :                         if (tmax - tmin > 1)
     222           0 :                                 np->no_pps = 1;
     223             :                         else {
     224           0 :                                 np->ts.tv_sec = tp->t_tv.tv_sec;
     225           0 :                                 np->ts.tv_nsec = tp->t_tv.tv_usec *
     226             :                                     1000L;
     227           0 :                                 np->no_pps = 0;
     228             :                         }
     229             :                 }
     230           0 :         } else if (c == '\n') {
     231           0 :                 if (np->pos == ENDRUNLEN - 1) {
     232             :                         /* don't copy '\n' into cbuf */
     233           0 :                         np->cbuf[np->pos] = '\0';
     234           0 :                         endrun_scan(np, tp);
     235           0 :                 }
     236           0 :                 np->sync = SYNC_EOL;
     237           0 :         } else {
     238           0 :                 if (np->pos < ENDRUNLEN - 1)
     239           0 :                         np->cbuf[np->pos++] = c;
     240             :         }
     241             : 
     242             : nogap:
     243             :         /* pass data to termios */
     244           0 :         return linesw[TTYDISC].l_rint(c, tp);
     245           0 : }
     246             : 
     247             : /* Scan the EndRun sentence just received */
     248             : void
     249           0 : endrun_scan(struct endrun *np, struct tty *tp)
     250             : {
     251             :         int fldcnt = 0, n;
     252           0 :         char *fld[NUMFLDS], *cs;
     253             : 
     254             :         DPRINTFN(1, ("%s\n", np->cbuf));
     255             :         /* split into fields */
     256           0 :         fld[fldcnt++] = &np->cbuf[0];
     257           0 :         for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
     258           0 :                 switch (np->cbuf[n]) {
     259             :                 case '\r':
     260           0 :                         np->cbuf[n] = '\0';
     261           0 :                         cs = &np->cbuf[n + 1];
     262           0 :                         break;
     263             :                 case ' ':
     264           0 :                         if (fldcnt < NUMFLDS) {
     265           0 :                                 np->cbuf[n] = '\0';
     266           0 :                                 fld[fldcnt++] = &np->cbuf[n + 1];
     267             :                         } else {
     268             :                                 DPRINTF(("endrun: nr of fields in sentence "
     269             :                                     "exceeds expected: %d\n", NUMFLDS));
     270           0 :                                 return;
     271             :                         }
     272           0 :                         break;
     273             :                 }
     274             :         }
     275           0 :         endrun_decode(np, tp, fld, fldcnt);
     276           0 : }
     277             : 
     278             : /* Decode the time string */
     279             : void
     280           0 : endrun_decode(struct endrun *np, struct tty *tp, char *fld[], int fldcnt)
     281             : {
     282           0 :         int64_t date_nano, time_nano, offset_nano, endrun_now;
     283             :         char tfom;
     284             :         int jumped = 0;
     285             : 
     286           0 :         if (fldcnt != NUMFLDS) {
     287             :                 DPRINTF(("endrun: field count mismatch, %d\n", fldcnt));
     288           0 :                 return;
     289             :         }
     290           0 :         if (endrun_time_to_nano(fld[3], &time_nano) == -1) {
     291             :                 DPRINTF(("endrun: illegal time, %s\n", fld[3]));
     292           0 :                 return;
     293             :         }
     294           0 :         if (endrun_date_to_nano(fld[1], fld[2], &date_nano) == -1) {
     295             :                 DPRINTF(("endrun: illegal date, %s %s\n", fld[1], fld[2]));
     296           0 :                 return;
     297             :         }
     298           0 :         offset_nano = 0;
     299             :         /* only parse offset when timemode is local */
     300           0 :         if (fld[5][0] == 'L' &&
     301           0 :             endrun_offset_to_nano(fld[4], &offset_nano) == -1) {
     302             :                 DPRINTF(("endrun: illegal offset, %s\n", fld[4]));
     303           0 :                 return;
     304             :         }
     305             : 
     306           0 :         endrun_now = date_nano + time_nano + offset_nano;
     307           0 :         if (endrun_now <= np->last) {
     308             :                 DPRINTF(("endrun: time not monotonically increasing "
     309             :                     "last %lld now %lld\n",
     310             :                     (long long)np->last, (long long)endrun_now));
     311             :                 jumped = 1;
     312           0 :         }
     313           0 :         np->last = endrun_now;
     314           0 :         np->gap = 0LL;
     315             : #ifdef ENDRUN_DEBUG
     316             :         if (np->time.status == SENSOR_S_UNKNOWN) {
     317             :                 np->time.status = SENSOR_S_OK;
     318             :                 timeout_add_sec(&np->endrun_tout, TRUSTTIME);
     319             :         }
     320             : #endif
     321             : 
     322           0 :         np->time.value = np->ts.tv_sec * 1000000000LL +
     323           0 :             np->ts.tv_nsec - endrun_now;
     324           0 :         np->time.tv.tv_sec = np->ts.tv_sec;
     325           0 :         np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
     326           0 :         if (np->time.status == SENSOR_S_UNKNOWN) {
     327           0 :                 np->time.status = SENSOR_S_OK;
     328           0 :                 np->time.flags &= ~SENSOR_FINVALID;
     329           0 :                 strlcpy(np->time.desc, "EndRun", sizeof(np->time.desc));
     330           0 :         }
     331             :         /*
     332             :          * Only update the timeout if the clock reports the time as valid.
     333             :          *
     334             :          * Time Figure Of Merit (TFOM) values:
     335             :          *
     336             :          * 6  - time error is < 100 us
     337             :          * 7  - time error is < 1 ms
     338             :          * 8  - time error is < 10 ms
     339             :          * 9  - time error is > 10 ms,
     340             :          *      unsynchronized state if never locked to CDMA
     341             :          */
     342             : 
     343           0 :         switch (tfom = fld[0][0]) {
     344             :         case '6':
     345             :         case '7':
     346             :         case '8':
     347           0 :                 np->time.status = SENSOR_S_OK;
     348           0 :                 np->signal.status = SENSOR_S_OK;
     349           0 :                 break;
     350             :         case '9':
     351           0 :                 np->signal.status = SENSOR_S_WARN;
     352           0 :                 break;
     353             :         default:
     354             :                 DPRINTF(("endrun: invalid TFOM: '%c'\n", tfom));
     355           0 :                 np->signal.status = SENSOR_S_CRIT;
     356           0 :                 break;
     357             :         }
     358             : 
     359             : #ifdef ENDRUN_DEBUG
     360             :         if (np->tfom != tfom) {
     361             :                 DPRINTF(("endrun: TFOM changed from %c to %c\n",
     362             :                     np->tfom, tfom));
     363             :                 np->tfom = tfom;
     364             :         }
     365             : #endif
     366           0 :         if (jumped)
     367           0 :                 np->time.status = SENSOR_S_WARN;
     368           0 :         if (np->time.status == SENSOR_S_OK)
     369           0 :                 timeout_add_sec(&np->endrun_tout, TRUSTTIME);
     370             : 
     371             :         /*
     372             :          * If tty timestamping is requested, but no PPS signal is present, set
     373             :          * the sensor state to CRITICAL.
     374             :          */
     375           0 :         if (np->no_pps)
     376           0 :                 np->time.status = SENSOR_S_CRIT;
     377           0 : }
     378             : 
     379             : int
     380           0 : endrun_atoi(char *s, int len)
     381             : {
     382             :         int n;
     383             :         char *p;
     384             : 
     385             :         /* make sure the input contains only numbers */
     386           0 :         for (n = 0, p = s; n < len && *p && *p >= '0' && *p <= '9'; n++, p++)
     387             :                 ;
     388           0 :         if (n != len || *p != '\0')
     389           0 :                 return -1;
     390             : 
     391           0 :         for (n = 0; *s; s++)
     392           0 :                 n = n * 10 + *s - '0';
     393             : 
     394           0 :         return n;
     395           0 : }
     396             : 
     397             : /*
     398             :  * Convert date fields from EndRun to nanoseconds since the epoch.
     399             :  * The year string must be of the form YYYY .
     400             :  * The day of year string must be of the form DDD .
     401             :  * Return 0 on success, -1 if illegal characters are encountered.
     402             :  */
     403             : int
     404           0 : endrun_date_to_nano(char *y, char *doy, int64_t *nano)
     405             : {
     406           0 :         struct clock_ymdhms clock;
     407             :         time_t secs;
     408             :         int n, i;
     409             :         int year_days = 365;
     410           0 :         int month_days[] = {
     411             :                 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
     412             :         };
     413             : 
     414             : #define FEBRUARY                2
     415             : 
     416             : #define LEAPYEAR(x)             \
     417             :         ((x) % 4 == 0 &&        \
     418             :         (x) % 100 != 0) ||      \
     419             :         (x) % 400 == 0
     420             : 
     421           0 :         if ((n = endrun_atoi(y, 4)) == -1)
     422           0 :                 return -1;
     423           0 :         clock.dt_year = n;
     424             : 
     425           0 :         if (LEAPYEAR(n)) {
     426           0 :                 month_days[FEBRUARY]++;
     427             :                 year_days++;
     428           0 :         }
     429             : 
     430           0 :         if ((n = endrun_atoi(doy, 3)) == -1 || n == 0 || n > year_days)
     431           0 :                 return -1;
     432             : 
     433             :         /* convert day of year to month, day */
     434           0 :         for (i = 1; n > month_days[i]; i++) {
     435           0 :                 n -= month_days[i];
     436             :         }
     437           0 :         clock.dt_mon = i;
     438           0 :         clock.dt_day = n;
     439             : 
     440             :         DPRINTFN(1, ("mm/dd %d/%d\n", i, n));
     441             : 
     442           0 :         clock.dt_hour = clock.dt_min = clock.dt_sec = 0;
     443             : 
     444           0 :         secs = clock_ymdhms_to_secs(&clock);
     445           0 :         *nano = secs * 1000000000LL;
     446           0 :         return 0;
     447           0 : }
     448             : 
     449             : /*
     450             :  * Convert time field from EndRun to nanoseconds since midnight.
     451             :  * The string must be of the form HH:MM:SS .
     452             :  * Return 0 on success, -1 if illegal characters are encountered.
     453             :  */
     454             : int
     455           0 : endrun_time_to_nano(char *s, int64_t *nano)
     456             : {
     457             :         struct clock_ymdhms clock;
     458             :         time_t secs;
     459             :         int n;
     460             : 
     461           0 :         if (s[2] != ':' || s[5] != ':')
     462           0 :                 return -1;
     463             : 
     464           0 :         s[2] = '\0';
     465           0 :         s[5] = '\0';
     466             : 
     467           0 :         if ((n = endrun_atoi(&s[0], 2)) == -1 || n > 23)
     468           0 :                 return -1;
     469           0 :         clock.dt_hour = n;
     470           0 :         if ((n = endrun_atoi(&s[3], 2)) == -1 || n > 59)
     471           0 :                 return -1;
     472           0 :         clock.dt_min = n;
     473           0 :         if ((n = endrun_atoi(&s[6], 2)) == -1 || n > 60)
     474           0 :                 return -1;
     475           0 :         clock.dt_sec = n;
     476             : 
     477             :         DPRINTFN(1, ("hh:mm:ss %d:%d:%d\n", (int)clock.dt_hour,
     478             :             (int)clock.dt_min,
     479             :             (int)clock.dt_sec));
     480           0 :         secs = clock.dt_hour * 3600
     481           0 :             + clock.dt_min * 60
     482           0 :             + clock.dt_sec;
     483             :             
     484             :         DPRINTFN(1, ("secs %lu\n", (unsigned long)secs));
     485             : 
     486           0 :         *nano = secs * 1000000000LL;
     487           0 :         return 0;
     488           0 : }
     489             : 
     490             : int
     491           0 : endrun_offset_to_nano(char *s, int64_t *nano)
     492             : {
     493             :         time_t secs;
     494             :         int n;
     495             : 
     496           0 :         if (!(s[0] == '+' || s[0] == '-'))
     497           0 :                 return -1;
     498             : 
     499           0 :         if ((n = endrun_atoi(&s[1], 2)) == -1)
     500           0 :                 return -1;
     501           0 :         secs = n * 30 * 60;
     502             : 
     503           0 :         *nano = secs * 1000000000LL;
     504           0 :         if (s[0] == '+')
     505           0 :                 *nano = -*nano;
     506             : 
     507             :         DPRINTFN(1, ("offset secs %lu nanosecs %lld\n",
     508             :             (unsigned long)secs, (long long)*nano));
     509             : 
     510           0 :         return 0;
     511           0 : }
     512             : 
     513             : /*
     514             :  * Degrade the sensor state if we received no EndRun string for more than
     515             :  * TRUSTTIME seconds.
     516             :  */
     517             : void
     518           0 : endrun_timeout(void *xnp)
     519             : {
     520           0 :         struct endrun *np = xnp;
     521             : 
     522           0 :         if (np->time.status == SENSOR_S_OK) {
     523           0 :                 np->time.status = SENSOR_S_WARN;
     524             :                 /*
     525             :                  * further degrade in TRUSTTIME seconds if no new valid EndRun
     526             :                  * strings are received.
     527             :                  */
     528           0 :                 timeout_add_sec(&np->endrun_tout, TRUSTTIME);
     529           0 :         } else
     530           0 :                 np->time.status = SENSOR_S_CRIT;
     531           0 : }

Generated by: LCOV version 1.13