]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpd/refclock_gpsvme.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / ntp / ntpd / refclock_gpsvme.c
1 /* refclock_psc.c:  clock driver for Brandywine PCI-SyncClock32/HP-UX 11.X */
2
3 #ifdef  HAVE_CONFIG_H
4 #include        <config.h>
5 #endif  /* HAVE_CONFIG_H        */
6
7 #if defined(REFCLOCK) && defined(CLOCK_GPSVME)
8
9 #include        "ntpd.h"
10 #include        "ntp_io.h"
11 #include        "ntp_refclock.h"
12 #include        "ntp_unixtime.h"
13 #include        "ntp_stdlib.h"
14
15 #ifdef  __hpux
16 #include        <sys/rtprio.h>  /* may already be included above        */
17 #include        <sys/lock.h>    /* NEEDED for PROCLOCK                  */
18 #endif  /* __hpux       */
19
20 #ifdef  __linux__
21 #include        <sys/ioctl.h>   /* for _IOR, ioctl                      */
22 #endif  /* __linux__    */
23
24 enum {                          /* constants    */
25     BUFSIZE                     =       32,
26     PSC_SYNC_OK                 =       0x40,   /* Sync status bit      */
27     DP_LEAPSEC_DAY10DAY1        =       0x82,   /* DP RAM address       */
28     DP_LEAPSEC_DAY1000DAY100    =       0x83,
29     DELAY                       =       1,
30     NUNIT                       =       2       /* max UNITS            */
31 };
32
33 /*      clock card registers    */
34 struct psc_regs {
35     uint32_t            low_time;       /* card base + 0x00     */
36     uint32_t            high_time;      /* card base + 0x04     */
37     uint32_t            ext_low_time;   /* card base + 0x08     */
38     uint32_t            ext_high_time;  /* card base + 0x0C     */
39     uint8_t             device_status;  /* card base + 0x10     */
40     uint8_t             device_control; /* card base + 0x11     */
41     uint8_t             reserved0;      /* card base + 0x12     */
42     uint8_t             ext_100ns;      /* card base + 0x13     */
43     uint8_t             match_usec;     /* card base + 0x14     */
44     uint8_t             match_msec;     /* card base + 0x15     */
45     uint8_t             reserved1;      /* card base + 0x16     */
46     uint8_t             reserved2;      /* card base + 0x17     */
47     uint8_t             reserved3;      /* card base + 0x18     */
48     uint8_t             reserved4;      /* card base + 0x19     */
49     uint8_t             dp_ram_addr;    /* card base + 0x1A     */
50     uint8_t             reserved5;      /* card base + 0x1B     */
51     uint8_t             reserved6;      /* card base + 0x1C     */
52     uint8_t             reserved7;      /* card base + 0x1D     */
53     uint8_t             dp_ram_data;    /* card base + 0x1E     */
54     uint8_t             reserved8;      /* card base + 0x1F     */
55 } *volatile regp[NUNIT];
56
57 #define PSC_REGS        _IOR('K', 0, long)      /* ioctl argument       */
58
59 /* Macros to swap byte order and convert BCD to binary  */
60 #define SWAP(val) ( ((val) >> 24) | (((val) & 0x00ff0000) >> 8) | \
61 (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24) )
62 #define BCD2INT2(val)  ( ((val) >> 4 & 0x0f)*10 + ((val) & 0x0f) )
63 #define BCD2INT3(val)  ( ((val) >> 8 & 0x0f)*100 + ((val) >> 4 & 0x0f)*10 + \
64 ((val) & 0x0f) )
65
66 /* PSC interface definitions */
67 #define PRECISION       (-20)   /* precision assumed (1 us)     */
68 #define REFID           "USNO"  /* reference ID */
69 #define DESCRIPTION     "Brandywine PCI-SyncClock32"
70 #define DEVICE          "/dev/refclock%1d"      /* device file  */
71
72 /* clock unit control structure */
73 struct psc_unit {
74     short       unit;           /* NTP refclock unit number     */
75     short       last_hour;      /* last hour (monitor leap sec) */
76     int         msg_flag[2];    /* count error messages         */
77 };
78 int     fd[NUNIT];              /* file descriptor      */
79
80 /* Local function prototypes */
81 static int              psc_start(int, struct peer *);
82 static void             psc_shutdown(int, struct peer *);
83 static void             psc_poll(int, struct peer *);
84 static void             check_leap_sec(struct refclockproc *, int);
85
86 /* Transfer vector      */
87 struct refclock refclock_gpsvme = {
88     psc_start, psc_shutdown, psc_poll, noentry, noentry, noentry, NOFLAGS
89 };
90
91 /* psc_start:  open device and initialize data for processing */
92 static int
93 psc_start(
94     int         unit,
95     struct peer *peer
96     )
97 {
98     char                        buf[BUFSIZE];
99     struct refclockproc         *pp;
100     struct psc_unit             *up = emalloc(sizeof *up);
101
102     if (unit < 0 || unit > 1) {         /* support units 0 and 1        */
103         msyslog(LOG_ERR, "psc_start: bad unit: %d", unit);
104         return 0;
105     }
106
107     memset(up, '\0', sizeof *up);
108
109     snprintf(buf, sizeof(buf), DEVICE, unit);   /* dev file name        */
110     fd[unit] = open(buf, O_RDONLY);     /* open device file     */
111     if (fd[unit] < 0) {
112         msyslog(LOG_ERR, "psc_start: unit: %d, open failed.  %m", unit);
113         return 0;
114     }
115      
116     /* get the address of the mapped regs       */
117     if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
118         msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed.  %m", unit);
119         return 0;
120     }
121
122     /* initialize peer variables        */
123     pp = peer->procptr;
124     pp->io.clock_recv = noentry;
125     pp->io.srcclock = peer;
126     pp->io.datalen = 0;
127     pp->io.fd = -1;
128     pp->unitptr = up;
129     get_systime(&pp->lastrec);
130     memcpy(&pp->refid, REFID, 4);
131     peer->precision = PRECISION;
132     pp->clockdesc = DESCRIPTION;
133     up->unit = unit;
134 #ifdef  __hpux     
135     rtprio(0,120);              /* set real time priority       */
136     plock(PROCLOCK);            /* lock process in memory       */
137 #endif  /* __hpux       */     
138     return 1;
139 }
140
141 /* psc_shutdown:  shut down the clock */
142 static void
143 psc_shutdown(
144     int         unit,
145     struct peer *peer
146     )
147 {
148     if (NULL != peer->procptr->unitptr)
149         free(peer->procptr->unitptr);
150     if (fd[unit] > 0)
151         close(fd[unit]);
152 }
153
154 /* psc_poll:  read, decode, and record device time */
155 static void
156 psc_poll(
157     int         unit,
158     struct peer *peer
159     )
160 {
161     struct refclockproc *pp = peer->procptr;
162     struct psc_unit             *up;
163     unsigned                    tlo, thi;
164     unsigned char               status;
165
166     up = (struct psc_unit *) pp->unitptr;
167     tlo = regp[unit]->low_time;         /* latch and read first 4 bytes */
168     thi = regp[unit]->high_time;        /* read 4 higher order bytes    */
169     status = regp[unit]->device_status; /* read device status byte      */
170
171     if (!(status & PSC_SYNC_OK)) {
172         refclock_report(peer, CEVNT_BADTIME);
173         if (!up->msg_flag[unit]) {      /* write once to system log     */
174             msyslog(LOG_WARNING,
175                 "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
176                 unit, status);
177             up->msg_flag[unit] = 1;
178         }
179         return;
180     }
181
182     get_systime(&pp->lastrec);
183     pp->polls++;
184      
185     tlo = SWAP(tlo);                    /* little to big endian swap on */
186     thi = SWAP(thi);                    /* copy of data                 */
187     /* convert the BCD time to broken down time used by refclockproc    */
188     pp->day     = BCD2INT3((thi & 0x0FFF0000) >> 16);
189     pp->hour    = BCD2INT2((thi & 0x0000FF00) >> 8);
190     pp->minute  = BCD2INT2(thi & 0x000000FF);
191     pp->second  = BCD2INT2(tlo >> 24);
192     /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
193        second in microseconds if usec is nonzero. */
194     pp->nsec    = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
195         BCD2INT3(tlo & 0x00000FFF);
196
197     snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
198              "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x", pp->day,
199              pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
200              tlo);
201     pp->lencode = strlen(pp->a_lastcode);
202
203     /* compute the timecode timestamp   */
204     if (!refclock_process(pp)) {
205         refclock_report(peer, CEVNT_BADTIME);
206         return;
207     }
208     /* simulate the NTP receive and packet procedures   */
209     refclock_receive(peer);
210     /* write clock statistics to file   */
211     record_clock_stats(&peer->srcadr, pp->a_lastcode);  
212
213     /* With the first timecode beginning the day, check for a GPS
214        leap second notification.      */
215     if (pp->hour < up->last_hour) {
216         check_leap_sec(pp, unit);
217         up->msg_flag[0] = up->msg_flag[1] = 0;  /* reset flags  */
218     }
219     up->last_hour = pp->hour;
220 }
221
222 /* check_leap_sec:  read the Dual Port RAM leap second day registers.  The
223    onboard GPS receiver should write the hundreds digit of day of year in
224    DP_LeapSec_Day1000Day100 and the tens and ones digits in
225    DP_LeapSec_Day10Day1.  If these values are nonzero and today, we have
226    a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
227    If the BCD data are zero or a date other than today, set pp->leap to
228    LEAP_NOWARNING.  */
229 static void
230 check_leap_sec(struct refclockproc *pp, int unit)
231 {
232     unsigned char       dhi, dlo;
233     int                 leap_day;
234      
235     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
236     usleep(DELAY);
237     dlo = regp[unit]->dp_ram_data;
238     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
239     usleep(DELAY);
240     dhi = regp[unit]->dp_ram_data;
241     leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
242
243     pp->leap = LEAP_NOWARNING;                  /* default      */
244     if (leap_day && leap_day == pp->day) {
245         pp->leap = LEAP_ADDSECOND;              /* leap second today    */
246         msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
247             leap_day, dhi, dlo);
248     }
249 }
250
251 #else
252 int     refclock_gpsvme_bs;
253 #endif  /* REFCLOCK     */