Annotation of embedaddon/ntp/ntpd/refclock_acts.c, revision 1.1.1.1
1.1 misho 1: /*
2: * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
3: * Services
4: */
5: #ifdef HAVE_CONFIG_H
6: #include <config.h>
7: #endif
8:
9: #if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
10:
11: #include "ntpd.h"
12: #include "ntp_io.h"
13: #include "ntp_unixtime.h"
14: #include "ntp_refclock.h"
15: #include "ntp_stdlib.h"
16: #include "ntp_control.h"
17:
18: #include <stdio.h>
19: #include <ctype.h>
20: #ifdef HAVE_SYS_IOCTL_H
21: # include <sys/ioctl.h>
22: #endif /* HAVE_SYS_IOCTL_H */
23:
24: /*
25: * This driver supports the US (NIST, USNO) and European (PTB, NPL,
26: * etc.) modem time services, as well as Spectracom GPS and WWVB
27: * receivers connected via a modem. The driver periodically dials a
28: * number from a telephone list, receives the timecode data and
29: * calculates the local clock correction. It is designed primarily for
30: * use as backup when neither a radio clock nor connectivity to Internet
31: * time servers is available.
32: *
33: * This driver requires a modem with a Hayes-compatible command set and
34: * control over the modem data terminal ready (DTR) control line. The
35: * modem setup string is hard-coded in the driver and may require
36: * changes for nonstandard modems or special circumstances. For reasons
37: * unrelated to this driver, the data set ready (DSR) control line
38: * should not be set when this driver is first started.
39: *
40: * The calling program is initiated by setting fudge flag1, either
41: * manually or automatically. When flag1 is set, the calling program
42: * dials the first number in the phone command of the configuration
43: * file. If that call fails, the calling program dials the second number
44: * and so on. The number is specified by the Hayes ATDT prefix followed
45: * by the number itself, including the prefix and long-distance digits
46: * and delay code, if necessary. The flag1 is reset and the calling
47: * program terminated if (a) a valid clock update has been determined,
48: * (b) no more numbers remain in the list, (c) a device fault or timeout
49: * occurs or (d) fudge flag1 is reset manually.
50: *
51: * The driver is transparent to each of the modem time services and
52: * Spectracom radios. It selects the parsing algorithm depending on the
53: * message length. There is some hazard should the message be corrupted.
54: * However, the data format is checked carefully and only if all checks
55: * succeed is the message accepted. Corrupted lines are discarded
56: * without complaint.
57: *
58: * Fudge controls
59: *
60: * flag1 force a call in manual mode
61: * flag2 enable port locking (not verified)
62: * flag3 no modem; port is directly connected to device
63: * flag4 not used
64: *
65: * time1 offset adjustment (s)
66: *
67: * Ordinarily, the serial port is connected to a modem; however, it can
68: * be connected directly to a device or another computer for testing and
69: * calibration. In this case set fudge flag3 and the driver will send a
70: * single character 'T' at each poll event. In principle, fudge flag2
71: * enables port locking, allowing the modem to be shared when not in use
72: * by this driver. At least on Solaris with the current NTP I/O
73: * routines, this results only in lots of ugly error messages.
74: */
75: /*
76: * National Institute of Science and Technology (NIST)
77: *
78: * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
79: *
80: * Data Format
81: *
82: * National Institute of Standards and Technology
83: * Telephone Time Service, Generator 3B
84: * Enter question mark "?" for HELP
85: * D L D
86: * MJD YR MO DA H M S ST S UT1 msADV <OTM>
87: * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
88: * ...
89: *
90: * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
91: * the on-time markers echoed by the driver and used by NIST to measure
92: * and correct for the propagation delay.
93: *
94: * US Naval Observatory (USNO)
95: *
96: * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
97: *
98: * Data Format (two lines, repeating at one-second intervals)
99: *
100: * jjjjj nnn hhmmss UTC<CR><LF>
101: * *<CR><LF>
102: *
103: * jjjjj modified Julian day number (not used)
104: * nnn day of year
105: * hhmmss second of day
106: * * on-time marker for previous timecode
107: * ...
108: *
109: * USNO does not correct for the propagation delay. A fudge time1 of
110: * about .06 s is advisable.
111: *
112: * European Services (PTB, NPL, etc.)
113: *
114: * PTB: +49 531 512038 (Germany)
115: * NPL: 0906 851 6333 (UK only)
116: *
117: * Data format (see the documentation for phone numbers and formats.)
118: *
119: * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
120: *
121: * Spectracom GPS and WWVB Receivers
122: *
123: * If a modem is connected to a Spectracom receiver, this driver will
124: * call it up and retrieve the time in one of two formats. As this
125: * driver does not send anything, the radio will have to either be
126: * configured in continuous mode or be polled by another local driver.
127: */
128: /*
129: * Interface definitions
130: */
131: #define DEVICE "/dev/acts%d" /* device name and unit */
132: #define SPEED232 B9600 /* uart speed (9600 baud) */
133: #define PRECISION (-10) /* precision assumed (about 1 ms) */
134: #define LOCKFILE "/var/spool/locks/LCK..cua%d"
135: #define DESCRIPTION "Automated Computer Time Service" /* WRU */
136: #define REFID "NONE" /* default reference ID */
137: #define MSGCNT 20 /* max message count */
138: #define SMAX 256 /* max clockstats line length */
139: #define MAXPHONE 10 /* max number of phone numbers */
140:
141: /*
142: * Calling program modes
143: */
144: #define MODE_AUTO 0 /* automatic mode */
145: #define MODE_BACKUP 1 /* backup mode */
146: #define MODE_MANUAL 2 /* manual mode */
147:
148: /*
149: * Service identifiers.
150: */
151: #define REFACTS "NIST" /* NIST reference ID */
152: #define LENACTS 50 /* NIST format */
153: #define REFUSNO "USNO" /* USNO reference ID */
154: #define LENUSNO 20 /* USNO */
155: #define REFPTB "PTB\0" /* PTB/NPL reference ID */
156: #define LENPTB 78 /* PTB/NPL format */
157: #define REFWWVB "WWVB" /* WWVB reference ID */
158: #define LENWWVB0 22 /* WWVB format 0 */
159: #define LENWWVB2 24 /* WWVB format 2 */
160: #define LF 0x0a /* ASCII LF */
161:
162: /*
163: * Modem setup strings. These may have to be changed for some modems.
164: *
165: * AT command prefix
166: * B1 US answer tone
167: * &C0 disable carrier detect
168: * &D2 hang up and return to command mode on DTR transition
169: * E0 modem command echo disabled
170: * l1 set modem speaker volume to low level
171: * M1 speaker enabled until carrier detect
172: * Q0 return result codes
173: * V1 return result codes as English words
174: */
175: #define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
176: #define MODEM_HANGUP "ATH\r" /* modem disconnect */
177:
178: /*
179: * Timeouts (all in seconds)
180: */
181: #define SETUP 3 /* setup timeout */
182: #define DTR 1 /* DTR timeout */
183: #define ANSWER 60 /* answer timeout */
184: #define CONNECT 20 /* first valid message timeout */
185: #define TIMECODE 30 /* all valid messages timeout */
186:
187: /*
188: * State machine codes
189: */
190: #define S_IDLE 0 /* wait for poll */
191: #define S_OK 1 /* wait for modem setup */
192: #define S_DTR 2 /* wait for modem DTR */
193: #define S_CONNECT 3 /* wait for answer*/
194: #define S_FIRST 4 /* wait for first valid message */
195: #define S_MSG 5 /* wait for all messages */
196: #define S_CLOSE 6 /* wait after sending disconnect */
197:
198: /*
199: * Unit control structure
200: */
201: struct actsunit {
202: int unit; /* unit number */
203: int state; /* the first one was Delaware */
204: int timer; /* timeout counter */
205: int retry; /* retry index */
206: int msgcnt; /* count of messages received */
207: l_fp tstamp; /* on-time timestamp */
208: char *bufptr; /* buffer pointer */
209: };
210:
211: /*
212: * Function prototypes
213: */
214: static int acts_start (int, struct peer *);
215: static void acts_shutdown (int, struct peer *);
216: static void acts_receive (struct recvbuf *);
217: static void acts_message (struct peer *);
218: static void acts_timecode (struct peer *, char *);
219: static void acts_poll (int, struct peer *);
220: static void acts_timeout (struct peer *);
221: static void acts_disc (struct peer *);
222: static void acts_timer (int, struct peer *);
223:
224: /*
225: * Transfer vector (conditional structure name)
226: */
227: struct refclock refclock_acts = {
228: acts_start, /* start up driver */
229: acts_shutdown, /* shut down driver */
230: acts_poll, /* transmit poll message */
231: noentry, /* not used */
232: noentry, /* not used */
233: noentry, /* not used */
234: acts_timer /* housekeeping timer */
235: };
236:
237: /*
238: * Initialize data for processing
239: */
240: static int
241: acts_start (
242: int unit,
243: struct peer *peer
244: )
245: {
246: struct actsunit *up;
247: struct refclockproc *pp;
248:
249: /*
250: * Allocate and initialize unit structure
251: */
252: up = emalloc(sizeof(struct actsunit));
253: memset(up, 0, sizeof(struct actsunit));
254: up->unit = unit;
255: pp = peer->procptr;
256: pp->unitptr = (caddr_t)up;
257: pp->io.clock_recv = acts_receive;
258: pp->io.srcclock = (caddr_t)peer;
259: pp->io.datalen = 0;
260:
261: /*
262: * Initialize miscellaneous variables
263: */
264: peer->precision = PRECISION;
265: pp->clockdesc = DESCRIPTION;
266: memcpy((char *)&pp->refid, REFID, 4);
267: peer->sstclktype = CTL_SST_TS_TELEPHONE;
268: up->bufptr = pp->a_lastcode;
269: return (1);
270: }
271:
272:
273: /*
274: * acts_shutdown - shut down the clock
275: */
276: static void
277: acts_shutdown (
278: int unit,
279: struct peer *peer
280: )
281: {
282: struct actsunit *up;
283: struct refclockproc *pp;
284:
285: /*
286: * Warning: do this only when a call is not in progress.
287: */
288: pp = peer->procptr;
289: up = (struct actsunit *)pp->unitptr;
290: free(up);
291: }
292:
293:
294: /*
295: * acts_receive - receive data from the serial interface
296: */
297: static void
298: acts_receive (
299: struct recvbuf *rbufp
300: )
301: {
302: struct actsunit *up;
303: struct refclockproc *pp;
304: struct peer *peer;
305: char tbuf[BMAX];
306: char *tptr;
307:
308: /*
309: * Initialize pointers and read the timecode and timestamp. Note
310: * we are in raw mode and victim of whatever the terminal
311: * interface kicks up; so, we have to reassemble messages from
312: * arbitrary fragments. Capture the timecode at the beginning of
313: * the message and at the '*' and '#' on-time characters.
314: */
315: peer = (struct peer *)rbufp->recv_srcclock;
316: pp = peer->procptr;
317: up = (struct actsunit *)pp->unitptr;
318: pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
319: pp->a_lastcode), &pp->lastrec);
320: for (tptr = tbuf; *tptr != '\0'; tptr++) {
321: if (*tptr == LF) {
322: if (up->bufptr == pp->a_lastcode) {
323: up->tstamp = pp->lastrec;
324: continue;
325:
326: } else {
327: *up->bufptr = '\0';
328: acts_message(peer);
329: up->bufptr = pp->a_lastcode;
330: }
331: } else if (!iscntrl(*tptr)) {
332: *up->bufptr++ = *tptr;
333: if (*tptr == '*' || *tptr == '#') {
334: up->tstamp = pp->lastrec;
335: write(pp->io.fd, tptr, 1);
336: }
337: }
338: }
339: }
340:
341:
342: /*
343: * acts_message - process message
344: */
345: void
346: acts_message(
347: struct peer *peer
348: )
349: {
350: struct actsunit *up;
351: struct refclockproc *pp;
352: int dtr = TIOCM_DTR;
353: char tbuf[SMAX];
354: #ifdef DEBUG
355: u_int modem;
356: #endif
357:
358: /*
359: * What to do depends on the state and the first token in the
360: * message. */
361: pp = peer->procptr;
362: up = (struct actsunit *)pp->unitptr;
363: #ifdef DEBUG
364: ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
365: snprintf(tbuf, sizeof(tbuf), "acts: %04x (%d %d) %lu %s", modem,
366: up->state, up->timer, (u_long)strlen(pp->a_lastcode),
367: pp->a_lastcode);
368: if (debug)
369: printf("%s\n", tbuf);
370: #endif
371:
372: /*
373: * Extract the first token in the line. A NO token sends the
374: * message to the clockstats.
375: */
376: strncpy(tbuf, pp->a_lastcode, SMAX);
377: strtok(tbuf, " ");
378: if (strcmp(tbuf, "NO") == 0) {
379: report_event(PEVNT_CLOCK, peer, pp->a_lastcode);
380: return;
381: }
382: switch(up->state) {
383:
384: /*
385: * We are waiting for the OK response to the modem setup
386: * command. When this happens, raise DTR and dial the number
387: * followed by \r.
388: */
389: case S_OK:
390: if (strcmp(tbuf, "OK") != 0) {
391: msyslog(LOG_ERR, "acts: setup error %s",
392: pp->a_lastcode);
393: acts_disc(peer);
394: return;
395: }
396: ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
397: up->state = S_DTR;
398: up->timer = DTR;
399: return;
400:
401: /*
402: * We are waiting for the call to be answered. All we care about
403: * here is token CONNECT. Send the message to the clockstats.
404: */
405: case S_CONNECT:
406: report_event(PEVNT_CLOCK, peer, pp->a_lastcode);
407: if (strcmp(tbuf, "CONNECT") != 0) {
408: acts_disc(peer);
409: return;
410: }
411: up->state = S_FIRST;
412: up->timer = CONNECT;
413: return;
414:
415: /*
416: * We are waiting for a timecode. Pass it to the parser.
417: */
418: case S_FIRST:
419: case S_MSG:
420: acts_timecode(peer, pp->a_lastcode);
421: break;
422: }
423: }
424:
425: /*
426: * acts_timecode - identify the service and parse the timecode message
427: */
428: void
429: acts_timecode(
430: struct peer *peer, /* peer structure pointer */
431: char *str /* timecode string */
432: )
433: {
434: struct actsunit *up;
435: struct refclockproc *pp;
436: int day; /* day of the month */
437: int month; /* month of the year */
438: u_long mjd; /* Modified Julian Day */
439: double dut1; /* DUT adjustment */
440:
441: u_int dst; /* ACTS daylight/standard time */
442: u_int leap; /* ACTS leap indicator */
443: double msADV; /* ACTS transmit advance (ms) */
444: char utc[10]; /* ACTS timescale */
445: char flag; /* ACTS on-time character (* or #) */
446:
447: char synchar; /* WWVB synchronized indicator */
448: char qualchar; /* WWVB quality indicator */
449: char leapchar; /* WWVB leap indicator */
450: char dstchar; /* WWVB daylight/savings indicator */
451: int tz; /* WWVB timezone */
452:
453: u_int leapmonth; /* PTB/NPL month of leap */
454: char leapdir; /* PTB/NPL leap direction */
455:
456: /*
457: * The parser selects the modem format based on the message
458: * length. Since the data are checked carefully, occasional
459: * errors due noise are forgivable.
460: */
461: pp = peer->procptr;
462: up = (struct actsunit *)pp->unitptr;
463: pp->nsec = 0;
464: switch(strlen(str)) {
465:
466: /*
467: * For USNO format on-time character '*', which is on a line by
468: * itself. Be sure a timecode has been received.
469: */
470: case 1:
471: if (*str == '*' && up->msgcnt > 0)
472: break;
473:
474: return;
475:
476: /*
477: * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
478: * UTC(NIST) *"
479: */
480: case LENACTS:
481: if (sscanf(str,
482: "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
483: &mjd, &pp->year, &month, &day, &pp->hour,
484: &pp->minute, &pp->second, &dst, &leap, &dut1,
485: &msADV, utc, &flag) != 13) {
486: refclock_report(peer, CEVNT_BADREPLY);
487: return;
488: }
489:
490: /*
491: * Wait until ACTS has calculated the roundtrip delay.
492: * We don't need to do anything, as ACTS adjusts the
493: * on-time epoch.
494: */
495: if (flag != '#')
496: return;
497:
498: pp->day = ymd2yd(pp->year, month, day);
499: pp->leap = LEAP_NOWARNING;
500: if (leap == 1)
501: pp->leap = LEAP_ADDSECOND;
502: else if (pp->leap == 2)
503: pp->leap = LEAP_DELSECOND;
504: memcpy(&pp->refid, REFACTS, 4);
505: if (up->msgcnt == 0)
506: record_clock_stats(&peer->srcadr, str);
507: up->msgcnt++;
508: break;
509:
510: /*
511: * USNO format: "jjjjj nnn hhmmss UTC"
512: */
513: case LENUSNO:
514: if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
515: &mjd, &pp->day, &pp->hour, &pp->minute,
516: &pp->second, utc) != 6) {
517: refclock_report(peer, CEVNT_BADREPLY);
518: return;
519: }
520:
521: /*
522: * Wait for the on-time character, which follows in a
523: * separate message. There is no provision for leap
524: * warning.
525: */
526: pp->leap = LEAP_NOWARNING;
527: memcpy(&pp->refid, REFUSNO, 4);
528: if (up->msgcnt == 0)
529: record_clock_stats(&peer->srcadr, str);
530: up->msgcnt++;
531: return;
532:
533: /*
534: * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
535: */
536: case LENPTB:
537: if (sscanf(str,
538: "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
539: &pp->second, &pp->year, &month, &day, &pp->hour,
540: &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
541: &msADV, &flag) != 12) {
542: refclock_report(peer, CEVNT_BADREPLY);
543: return;
544: }
545: pp->leap = LEAP_NOWARNING;
546: if (leapmonth == month) {
547: if (leapdir == '+')
548: pp->leap = LEAP_ADDSECOND;
549: else if (leapdir == '-')
550: pp->leap = LEAP_DELSECOND;
551: }
552: pp->day = ymd2yd(pp->year, month, day);
553: memcpy(&pp->refid, REFPTB, 4);
554: if (up->msgcnt == 0)
555: record_clock_stats(&peer->srcadr, str);
556: up->msgcnt++;
557: break;
558:
559:
560: /*
561: * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
562: */
563: case LENWWVB0:
564: if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
565: &synchar, &pp->day, &pp->hour, &pp->minute,
566: &pp->second, &dstchar, &tz) != 7) {
567: refclock_report(peer, CEVNT_BADREPLY);
568: return;
569: }
570: pp->leap = LEAP_NOWARNING;
571: if (synchar != ' ')
572: pp->leap = LEAP_NOTINSYNC;
573: memcpy(&pp->refid, REFWWVB, 4);
574: if (up->msgcnt == 0)
575: record_clock_stats(&peer->srcadr, str);
576: up->msgcnt++;
577: break;
578:
579: /*
580: * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
581: */
582: case LENWWVB2:
583: if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
584: &synchar, &qualchar, &pp->year, &pp->day,
585: &pp->hour, &pp->minute, &pp->second, &pp->nsec,
586: &dstchar, &leapchar, &dstchar) != 11) {
587: refclock_report(peer, CEVNT_BADREPLY);
588: return;
589: }
590: pp->nsec *= 1000000;
591: pp->leap = LEAP_NOWARNING;
592: if (synchar != ' ')
593: pp->leap = LEAP_NOTINSYNC;
594: else if (leapchar == 'L')
595: pp->leap = LEAP_ADDSECOND;
596: memcpy(&pp->refid, REFWWVB, 4);
597: if (up->msgcnt == 0)
598: record_clock_stats(&peer->srcadr, str);
599: up->msgcnt++;
600: break;
601:
602: /*
603: * None of the above. Just forget about it and wait for the next
604: * message or timeout.
605: */
606: default:
607: return;
608: }
609:
610: /*
611: * We have a valid timecode. The fudge time1 value is added to
612: * each sample by the main line routines. Note that in current
613: * telephone networks the propatation time can be different for
614: * each call and can reach 200 ms for some calls.
615: */
616: peer->refid = pp->refid;
617: pp->lastrec = up->tstamp;
618: if (!refclock_process(pp)) {
619: refclock_report(peer, CEVNT_BADTIME);
620: return;
621: }
622: pp->lastref = pp->lastrec;
623: if (up->state != S_MSG) {
624: up->state = S_MSG;
625: up->timer = TIMECODE;
626: }
627: }
628:
629:
630: /*
631: * acts_poll - called by the transmit routine
632: */
633: static void
634: acts_poll (
635: int unit,
636: struct peer *peer
637: )
638: {
639: struct actsunit *up;
640: struct refclockproc *pp;
641:
642: /*
643: * This routine is called at every system poll. All it does is
644: * set flag1 under certain conditions. The real work is done by
645: * the timeout routine and state machine.
646: */
647: pp = peer->procptr;
648: up = (struct actsunit *)pp->unitptr;
649: switch (peer->ttl) {
650:
651: /*
652: * In manual mode the calling program is activated by the ntpdc
653: * program using the enable flag (fudge flag1), either manually
654: * or by a cron job.
655: */
656: case MODE_MANUAL:
657: /* fall through */
658: break;
659:
660: /*
661: * In automatic mode the calling program runs continuously at
662: * intervals determined by the poll event or specified timeout.
663: */
664: case MODE_AUTO:
665: pp->sloppyclockflag |= CLK_FLAG1;
666: break;
667:
668: /*
669: * In backup mode the calling program runs continuously as long
670: * as either no peers are available or this peer is selected.
671: */
672: case MODE_BACKUP:
673: if (sys_peer == NULL || sys_peer == peer)
674: pp->sloppyclockflag |= CLK_FLAG1;
675: break;
676: }
677: }
678:
679:
680: /*
681: * acts_timer - called at one-second intervals
682: */
683: static void
684: acts_timer(
685: int unit,
686: struct peer *peer
687: )
688: {
689: struct actsunit *up;
690: struct refclockproc *pp;
691:
692: /*
693: * This routine implments a timeout which runs for a programmed
694: * interval. The counter is initialized by the state machine and
695: * counts down to zero. Upon reaching zero, the state machine is
696: * called. If flag1 is set while in S_IDLE state, force a
697: * timeout.
698: */
699: pp = peer->procptr;
700: up = (struct actsunit *)pp->unitptr;
701: if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) {
702: acts_timeout(peer);
703: return;
704: }
705: if (up->timer == 0)
706: return;
707:
708: up->timer--;
709: if (up->timer == 0)
710: acts_timeout(peer);
711: }
712:
713:
714: /*
715: * acts_timeout - called on timeout
716: */
717: static void
718: acts_timeout(
719: struct peer *peer
720: )
721: {
722: struct actsunit *up;
723: struct refclockproc *pp;
724: int fd;
725: char device[20];
726: char lockfile[128], pidbuf[8];
727: char tbuf[SMAX];
728:
729: /*
730: * The state machine is driven by messages from the modem, when
731: * first stated and at timeout.
732: */
733: pp = peer->procptr;
734: up = (struct actsunit *)pp->unitptr;
735: pp->sloppyclockflag &= ~CLK_FLAG1;
736: if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag &
737: CLK_FLAG3)) {
738: msyslog(LOG_ERR, "acts: no phones");
739: return;
740: }
741: switch(up->state) {
742:
743: /*
744: * System poll event. Lock the modem port and open the device.
745: */
746: case S_IDLE:
747:
748: /*
749: * Lock the modem port. If busy, retry later. Note: if
750: * something fails between here and the close, the lock
751: * file may not be removed.
752: */
753: if (pp->sloppyclockflag & CLK_FLAG2) {
754: snprintf(lockfile, sizeof(lockfile), LOCKFILE,
755: up->unit);
756: fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
757: 0644);
758: if (fd < 0) {
759: msyslog(LOG_ERR, "acts: port busy");
760: return;
761: }
762: snprintf(pidbuf, sizeof(pidbuf), "%d\n",
763: (u_int)getpid());
764: write(fd, pidbuf, strlen(pidbuf));
765: close(fd);
766: }
767:
768: /*
769: * Open the device in raw mode and link the I/O.
770: */
771: if (!pp->io.fd) {
772: snprintf(device, sizeof(device), DEVICE,
773: up->unit);
774: fd = refclock_open(device, SPEED232,
775: LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
776: if (fd == 0) {
777: msyslog(LOG_ERR,
778: "acts: open fails");
779: return;
780: }
781: pp->io.fd = fd;
782: if (!io_addclock(&pp->io)) {
783: msyslog(LOG_ERR,
784: "acts: addclock fails");
785: close(fd);
786: pp->io.fd = 0;
787: return;
788: }
789: }
790:
791: /*
792: * If the port is directly connected to the device, skip
793: * the modem business and send 'T' for Spectrabum.
794: */
795: if (pp->sloppyclockflag & CLK_FLAG3) {
796: if (write(pp->io.fd, "T", 1) < 0) {
797: msyslog(LOG_ERR, "acts: write %m");
798: return;
799: }
800: up->state = S_FIRST;
801: up->timer = CONNECT;
802: return;
803: }
804:
805: /*
806: * Initialize the modem. This works with Hayes commands.
807: */
808: #ifdef DEBUG
809: if (debug)
810: printf("acts: setup %s\n", MODEM_SETUP);
811: #endif
812: if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
813: 0) {
814: msyslog(LOG_ERR, "acts: write %m");
815: return;
816: }
817: up->state = S_OK;
818: up->timer = SETUP;
819: return;
820:
821: /*
822: * In OK state the modem did not respond to setup.
823: */
824: case S_OK:
825: msyslog(LOG_ERR, "acts: no modem");
826: break;
827:
828: /*
829: * In DTR state we are waiting for the modem to settle down
830: * before hammering it with a dial command.
831: */
832: case S_DTR:
833: snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry,
834: sys_phone[up->retry]);
835: report_event(PEVNT_CLOCK, peer, tbuf);
836: #ifdef DEBUG
837: if (debug)
838: printf("%s\n", tbuf);
839: #endif
840: write(pp->io.fd, sys_phone[up->retry],
841: strlen(sys_phone[up->retry]));
842: write(pp->io.fd, "\r", 1);
843: up->state = S_CONNECT;
844: up->timer = ANSWER;
845: return;
846:
847: /*
848: * In CONNECT state the call did not complete.
849: */
850: case S_CONNECT:
851: msyslog(LOG_ERR, "acts: no answer");
852: break;
853:
854: /*
855: * In FIRST state no messages were received.
856: */
857: case S_FIRST:
858: msyslog(LOG_ERR, "acts: no messages");
859: break;
860:
861: /*
862: * In CLOSE state hangup is complete. Close the doors and
863: * windows and get some air.
864: */
865: case S_CLOSE:
866:
867: /*
868: * Close the device and unlock a shared modem.
869: */
870: if (pp->io.fd) {
871: io_closeclock(&pp->io);
872: close(pp->io.fd);
873: if (pp->sloppyclockflag & CLK_FLAG2) {
874: snprintf(lockfile, sizeof(lockfile),
875: LOCKFILE, up->unit);
876: unlink(lockfile);
877: }
878: pp->io.fd = 0;
879: }
880:
881: /*
882: * If messages were received, fold the tent and wait for
883: * the next poll. If no messages and there are more
884: * numbers to dial, retry after a short wait.
885: */
886: up->bufptr = pp->a_lastcode;
887: up->timer = 0;
888: up->state = S_IDLE;
889: if ( up->msgcnt == 0) {
890: up->retry++;
891: if (sys_phone[up->retry] == NULL)
892: up->retry = 0;
893: else
894: up->timer = SETUP;
895: } else {
896: up->retry = 0;
897: }
898: up->msgcnt = 0;
899: return;
900: }
901: acts_disc(peer);
902: }
903:
904:
905: /*
906: * acts_disc - disconnect the call and clean the place up.
907: */
908: static void
909: acts_disc (
910: struct peer *peer
911: )
912: {
913: struct actsunit *up;
914: struct refclockproc *pp;
915: int dtr = TIOCM_DTR;
916:
917: /*
918: * We get here if the call terminated successfully or if an
919: * error occured. If the median filter has something in it,
920: * feed the data to the clock filter. If a modem port, drop DTR
921: * to force command mode and send modem hangup.
922: */
923: pp = peer->procptr;
924: up = (struct actsunit *)pp->unitptr;
925: if (up->msgcnt > 0)
926: refclock_receive(peer);
927: if (!(pp->sloppyclockflag & CLK_FLAG3)) {
928: ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
929: write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
930: }
931: up->timer = SETUP;
932: up->state = S_CLOSE;
933: }
934: #else
935: int refclock_acts_bs;
936: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>