]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/ntp/ntpd/refclock_gpsvme.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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     if (!up) {
108         msyslog(LOG_ERR, "psc_start: unit: %d, emalloc: %m", unit);
109         return 0;
110     }
111     memset(up, '\0', sizeof *up);
112
113     sprintf(buf, DEVICE, unit);         /* dev file name        */
114     fd[unit] = open(buf, O_RDONLY);     /* open device file     */
115     if (fd[unit] < 0) {
116         msyslog(LOG_ERR, "psc_start: unit: %d, open failed.  %m", unit);
117         return 0;
118     }
119      
120     /* get the address of the mapped regs       */
121     if (ioctl(fd[unit], PSC_REGS, &regp[unit]) < 0) {
122         msyslog(LOG_ERR, "psc_start: unit: %d, ioctl failed.  %m", unit);
123         return 0;
124     }
125
126     /* initialize peer variables        */
127     pp = peer->procptr;
128     pp->io.clock_recv = noentry;
129     pp->io.srcclock = (caddr_t) peer;
130     pp->io.datalen = 0;
131     pp->io.fd = -1;
132     pp->unitptr = (caddr_t) up;
133     get_systime(&pp->lastrec);
134     memcpy((char *)&pp->refid, REFID, 4);
135     peer->precision = PRECISION;
136     pp->clockdesc = DESCRIPTION;
137     up->unit = unit;
138 #ifdef  __hpux     
139     rtprio(0,120);              /* set real time priority       */
140     plock(PROCLOCK);            /* lock process in memory       */
141 #endif  /* __hpux       */     
142     return 1;
143 }
144
145 /* psc_shutdown:  shut down the clock */
146 static void
147 psc_shutdown(
148     int         unit,
149     struct peer *peer
150     )
151 {
152     free(peer->procptr->unitptr);
153     close(fd[unit]);
154 }
155
156 /* psc_poll:  read, decode, and record device time */
157 static void
158 psc_poll(
159     int         unit,
160     struct peer *peer
161     )
162 {
163     struct refclockproc *pp = peer->procptr;
164     struct psc_unit             *up;
165     unsigned                    tlo, thi;
166     unsigned char               status;
167
168     up = (struct psc_unit *) pp->unitptr;
169     tlo = regp[unit]->low_time;         /* latch and read first 4 bytes */
170     thi = regp[unit]->high_time;        /* read 4 higher order bytes    */
171     status = regp[unit]->device_status; /* read device status byte      */
172
173     if (!(status & PSC_SYNC_OK)) {
174         refclock_report(peer, CEVNT_BADTIME);
175         if (!up->msg_flag[unit]) {      /* write once to system log     */
176             msyslog(LOG_WARNING,
177                 "SYNCHRONIZATION LOST on unit %1d, status %02x\n",
178                 status, unit);
179             up->msg_flag[unit] = 1;
180         }
181         return;
182     }
183
184     get_systime(&pp->lastrec);
185     pp->polls++;
186      
187     tlo = SWAP(tlo);                    /* little to big endian swap on */
188     thi = SWAP(thi);                    /* copy of data                 */
189     /* convert the BCD time to broken down time used by refclockproc    */
190     pp->day     = BCD2INT3((thi & 0x0FFF0000) >> 16);
191     pp->hour    = BCD2INT2((thi & 0x0000FF00) >> 8);
192     pp->minute  = BCD2INT2(thi & 0x000000FF);
193     pp->second  = BCD2INT2(tlo >> 24);
194     /* ntp_process() in ntp_refclock.c appears to use usec as fraction of
195        second in microseconds if usec is nonzero. */
196     pp->nsec    = 1000000*BCD2INT3((tlo & 0x00FFF000) >> 12) +
197         BCD2INT3(tlo & 0x00000FFF);
198
199     sprintf(pp->a_lastcode, "%3.3d %2.2d:%2.2d:%2.2d.%09ld %02x %08x %08x",
200         pp->day, pp->hour, pp->minute, pp->second, pp->nsec, status, thi,
201         tlo);
202     pp->lencode = strlen(pp->a_lastcode);
203
204     /* compute the timecode timestamp   */
205     if (!refclock_process(pp)) {
206         refclock_report(peer, CEVNT_BADTIME);
207         return;
208     }
209     /* simulate the NTP receive and packet procedures   */
210     refclock_receive(peer);
211     /* write clock statistics to file   */
212     record_clock_stats(&peer->srcadr, pp->a_lastcode);  
213
214     /* With the first timecode beginning the day, check for a GPS
215        leap second notification.      */
216     if (pp->hour < up->last_hour) {
217         check_leap_sec(pp, unit);
218         up->msg_flag[0] = up->msg_flag[1] = 0;  /* reset flags  */
219     }
220     up->last_hour = pp->hour;
221 }
222
223 /* check_leap_sec:  read the Dual Port RAM leap second day registers.  The
224    onboard GPS receiver should write the hundreds digit of day of year in
225    DP_LeapSec_Day1000Day100 and the tens and ones digits in
226    DP_LeapSec_Day10Day1.  If these values are nonzero and today, we have
227    a leap second pending, so we set the pp->leap flag to LEAP_ADDSECOND.
228    If the BCD data are zero or a date other than today, set pp->leap to
229    LEAP_NOWARNING.  */
230 static void
231 check_leap_sec(struct refclockproc *pp, int unit)
232 {
233     unsigned char       dhi, dlo;
234     int                 leap_day;
235      
236     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY10DAY1;
237     usleep(DELAY);
238     dlo = regp[unit]->dp_ram_data;
239     regp[unit]->dp_ram_addr = DP_LEAPSEC_DAY1000DAY100;
240     usleep(DELAY);
241     dhi = regp[unit]->dp_ram_data;
242     leap_day = BCD2INT2(dlo) + 100*(dhi & 0x0F);
243
244     pp->leap = LEAP_NOWARNING;                  /* default      */
245     if (leap_day && leap_day == pp->day) {
246         pp->leap = LEAP_ADDSECOND;              /* leap second today    */
247         msyslog(LOG_ERR, "LEAP_ADDSECOND flag set, day %d (%x %x).",
248             leap_day, dhi, dlo);
249     }
250 }
251
252 #else
253 int     refclock_gpsvme_bs;
254 #endif  /* REFCLOCK     */