Annotation of embedaddon/ntp/ntpd/refclock_bancomm.c, revision 1.1.1.1
1.1 misho 1: /* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME
2: * Time and Frequency Processor. It requires the BANCOMM bc635VME/
3: * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
4: * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
5: * IIi-cEngine running Solaris 2.6.
6: *
7: * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
8: * Ottawa, Canada
9: *
10: * Date: July 1999
11: *
12: * Note(s): The refclock type has been defined as 16.
13: *
14: * This program has been modelled after the Bancomm driver
15: * originally written by R. Schmidt of Time Service, U.S.
16: * Naval Observatory for a HP-UX machine. Since the original
17: * authors no longer plan to maintain this code, all
18: * references to the HP-UX vme2 driver subsystem bave been
19: * removed. Functions vme_report_event(), vme_receive(),
20: * vme_control() and vme_buginfo() have been deleted because
21: * they are no longer being used.
22: *
23: * 04/28/2005 Rob Neal
24: * Modified to add support for Symmetricom bc637PCI-U Time &
25: * Frequency Processor.
26: * 2/21/2007 Ali Ghorashi
27: * Modified to add support for Symmetricom bc637PCI-U Time &
28: * Frequency Processor on Solaris.
29: * Tested on Solaris 10 with a bc635 card.
30: *
31: * Card bus type (VME/VXI or PCI) and environment are specified via the
32: * "mode" keyword on the server command in ntp.conf.
33: * server 127.127.16.u prefer mode M
34: * where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
35: *
36: * and M is one of the following modes:
37: * 1 : FreeBSD PCI 635/637.
38: * 2 : Linux or Windows PCI 635/637.
39: * 3 : Solaris PCI 635/637
40: * not specified, or other number:
41: * : Assumed to be VME/VXI legacy Bancomm card on Solaris.
42: * Linux and Windows platforms require Symmetricoms' proprietary driver
43: * for the TFP card.
44: * Solaris requires Symmetricom's driver and its header file (freely distributed) to
45: * be installed and running.
46: */
47:
48: #ifdef HAVE_CONFIG_H
49: #include <config.h>
50: #endif
51:
52: #if defined(REFCLOCK) && defined(CLOCK_BANC)
53:
54: #include "ntpd.h"
55: #include "ntp_io.h"
56: #include "ntp_refclock.h"
57: #include "ntp_unixtime.h"
58: #include "ntp_stdlib.h"
59:
60: #include <stdio.h>
61: #include <syslog.h>
62: #include <ctype.h>
63:
64: struct btfp_time /* Structure for reading 5 time words */
65: /* in one ioctl(2) operation. */
66: {
67: unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
68: };
69: /* SunOS5 ioctl commands definitions.*/
70: #define BTFPIOC ( 'b'<< 8 )
71: #define IOCIO( l, n ) ( BTFPIOC | n )
72: #define IOCIOR( l, n, s ) ( BTFPIOC | n )
73: #define IOCIORN( l, n, s ) ( BTFPIOC | n )
74: #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
75:
76: /***** Simple ioctl commands *****/
77: #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
78: #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
79: #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
80: /***** Compound ioctl commands *****/
81:
82: /* Read all 5 time words in one call. */
83: #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
84:
85: #if defined(__FreeBSD__)
86: #undef READTIME
87: #define READTIME _IOR('u', 5, struct btfp_time )
88: #endif
89:
90: /* Solaris specific section */
91: struct stfp_tm {
92: int32_t tm_sec;
93: int32_t tm_min;
94: int32_t tm_hour;
95: int32_t tm_mday;
96: int32_t tm_mon;
97: int32_t tm_year;
98: int32_t tm_wday;
99: int32_t tm_yday;
100: int32_t tm_isdst;
101: };
102:
103: struct stfp_time {
104: struct stfp_tm tm;
105: int32_t usec; /* usec 0 - 999999 */
106: int32_t hnsec; /* hnsec 0 - 9 (hundreds of nsecs) */
107: int32_t status;
108: };
109:
110: #define SELTIMEFORMAT 2
111: # define TIME_DECIMAL 0
112: # define TIME_BINARY 1
113:
114: #if defined(__sun__)
115: #undef READTIME
116: #define READTIME 9
117: #endif /** __sun___ **/
118: /* end solaris specific section */
119:
120: struct vmedate { /* structure returned by get_vmetime.c */
121: unsigned short year;
122: unsigned short day;
123: unsigned short hr;
124: unsigned short mn;
125: unsigned short sec;
126: long frac;
127: unsigned short status;
128: };
129:
130: typedef void *SYMMT_PCI_HANDLE;
131:
132: /*
133: * VME interface parameters.
134: */
135: #define VMEPRECISION (-21) /* precision assumed (1 us) */
136: #define USNOREFID "BTFP" /* or whatever */
137: #define VMEREFID "BTFP" /* reference id */
138: #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
139: #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
140: /* clock type 16 is used here */
141: #define GMT 0 /* hour offset from Greenwich */
142:
143: /*
144: * Imported from ntp_timer module
145: */
146: extern u_long current_time; /* current time(s) */
147:
148: /*
149: * Imported from ntpd module
150: */
151: extern volatile int debug; /* global debug flag */
152:
153: /*
154: * VME unit control structure.
155: * Changes made to vmeunit structure. Most members are now available in the
156: * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
157: */
158: struct vmeunit {
159: struct vmedate vmedata; /* data returned from vme read */
160: u_long lasttime; /* last time clock heard from */
161: };
162:
163: /*
164: * Function prototypes
165: */
166: static int vme_start (int, struct peer *);
167: static void vme_shutdown (int, struct peer *);
168: static void vme_receive (struct recvbuf *);
169: static void vme_poll (int unit, struct peer *);
170: struct vmedate *get_datumtime(struct vmedate *);
171: void tvme_fill(struct vmedate *, uint32_t btm[2]);
172: void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
173: inline const char *DEVICE_NAME(int n);
174:
175:
176: /*
177: * Define the bc*() functions as weak so we can compile/link without them.
178: * Only clients with the card will have the proprietary vendor device driver
179: * and interface library needed for use on Linux/Windows platforms.
180: */
181: extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
182: extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
183: extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
184:
185: /*
186: * Transfer vector
187: */
188: struct refclock refclock_bancomm = {
189: vme_start, /* start up driver */
190: vme_shutdown, /* shut down driver */
191: vme_poll, /* transmit poll message */
192: noentry, /* not used (old vme_control) */
193: noentry, /* initialize driver */
194: noentry, /* not used (old vme_buginfo) */
195: NOFLAGS /* not used */
196: };
197:
198: int fd_vme; /* file descriptor for ioctls */
199: int regvalue;
200: int tfp_type; /* mode selector, indicate platform and driver interface */
201: SYMMT_PCI_HANDLE stfp_handle;
202:
203: /**
204: * this macro returns the device name based on
205: * the platform we are running on and the device number
206: */
207: #if defined(__sun__)
208: inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
209: #else
210: inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
211: #endif /**__sun__**/
212:
213: /*
214: * vme_start - open the VME device and initialize data for processing
215: */
216: static int
217: vme_start(
218: int unit,
219: struct peer *peer
220: )
221: {
222: register struct vmeunit *vme;
223: struct refclockproc *pp;
224: int dummy;
225: char vmedev[20];
226:
227: tfp_type = (int)(peer->ttl);
228: switch (tfp_type) {
229: case 1:
230: case 3:
231: break;
232: case 2:
233: stfp_handle = bcStartPci(); /* init the card in lin/win */
234: break;
235: default:
236: break;
237: }
238: /*
239: * Open VME device
240: */
241: #ifdef DEBUG
242:
243: printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
244: #endif
245: if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
246: msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
247: return (0);
248: }
249: else {
250: switch (tfp_type) {
251: case 1: break;
252: case 2: break;
253: case 3:break;
254: default:
255: /* Release capture lockout in case it was set before. */
256: if( ioctl( fd_vme, RUNLOCK, &dummy ) )
257: msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
258:
259: regvalue = 0; /* More esoteric stuff to do... */
260: if( ioctl( fd_vme, WCR0, ®value ) )
261: msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
262: break;
263: }
264: }
265:
266: /*
267: * Allocate unit structure
268: */
269: vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
270: bzero((char *)vme, sizeof(struct vmeunit));
271:
272:
273: /*
274: * Set up the structures
275: */
276: pp = peer->procptr;
277: pp->unitptr = (caddr_t) vme;
278: pp->timestarted = current_time;
279:
280: pp->io.clock_recv = vme_receive;
281: pp->io.srcclock = (caddr_t)peer;
282: pp->io.datalen = 0;
283: pp->io.fd = fd_vme;
284:
285: /*
286: * All done. Initialize a few random peer variables, then
287: * return success. Note that root delay and root dispersion are
288: * always zero for this clock.
289: */
290: peer->precision = VMEPRECISION;
291: memcpy(&pp->refid, USNOREFID,4);
292: return (1);
293: }
294:
295:
296: /*
297: * vme_shutdown - shut down a VME clock
298: */
299: static void
300: vme_shutdown(
301: int unit,
302: struct peer *peer
303: )
304: {
305: register struct vmeunit *vme;
306: struct refclockproc *pp;
307:
308: /*
309: * Tell the I/O module to turn us off. We're history.
310: */
311: pp = peer->procptr;
312: vme = (struct vmeunit *)pp->unitptr;
313: io_closeclock(&pp->io);
314: pp->unitptr = NULL;
315: if (NULL != vme)
316: free(vme);
317: if (tfp_type == 2)
318: bcStopPci(stfp_handle);
319: }
320:
321:
322: /*
323: * vme_receive - receive data from the VME device.
324: *
325: * Note: This interface would be interrupt-driven. We don't use that
326: * now, but include a dummy routine for possible future adventures.
327: */
328: static void
329: vme_receive(
330: struct recvbuf *rbufp
331: )
332: {
333: }
334:
335:
336: /*
337: * vme_poll - called by the transmit procedure
338: */
339: static void
340: vme_poll(
341: int unit,
342: struct peer *peer
343: )
344: {
345: struct vmedate *tptr;
346: struct vmeunit *vme;
347: struct refclockproc *pp;
348: time_t tloc;
349: struct tm *tadr;
350:
351: pp = peer->procptr;
352: vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */
353:
354: tptr = &vme->vmedata;
355: if ((tptr = get_datumtime(tptr)) == NULL ) {
356: refclock_report(peer, CEVNT_BADREPLY);
357: return;
358: }
359:
360: get_systime(&pp->lastrec);
361: pp->polls++;
362: vme->lasttime = current_time;
363:
364: /*
365: * Get VME time and convert to timestamp format.
366: * The year must come from the system clock.
367: */
368:
369: time(&tloc);
370: tadr = gmtime(&tloc);
371: tptr->year = (unsigned short)(tadr->tm_year + 1900);
372:
373: snprintf(pp->a_lastcode,
374: sizeof(pp->a_lastcode),
375: "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
376: tptr->day,
377: tptr->hr,
378: tptr->mn,
379: tptr->sec,
380: tptr->frac,
381: tptr->status);
382:
383: pp->lencode = (u_short) strlen(pp->a_lastcode);
384:
385: pp->day = tptr->day;
386: pp->hour = tptr->hr;
387: pp->minute = tptr->mn;
388: pp->second = tptr->sec;
389: pp->nsec = tptr->frac;
390:
391: #ifdef DEBUG
392: if (debug)
393: printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
394: pp->day, pp->hour, pp->minute, pp->second,
395: pp->nsec, tptr->status);
396: #endif
397: if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
398: refclock_report(peer, CEVNT_BADREPLY);
399: return;
400: }
401:
402: /*
403: * Now, compute the reference time value. Use the heavy
404: * machinery for the seconds and the millisecond field for the
405: * fraction when present. If an error in conversion to internal
406: * format is found, the program declares bad data and exits.
407: * Note that this code does not yet know how to do the years and
408: * relies on the clock-calendar chip for sanity.
409: */
410: if (!refclock_process(pp)) {
411: refclock_report(peer, CEVNT_BADTIME);
412: return;
413: }
414: pp->lastref = pp->lastrec;
415: refclock_receive(peer);
416: record_clock_stats(&peer->srcadr, pp->a_lastcode);
417: }
418:
419: struct vmedate *
420: get_datumtime(struct vmedate *time_vme)
421: {
422: char cbuf[7];
423: struct btfp_time vts;
424: uint32_t btm[2];
425: uint8_t dmy;
426: struct stfp_time stfpm;
427:
428: if (time_vme == NULL)
429: time_vme = emalloc(sizeof(*time_vme));
430:
431: switch (tfp_type) {
432: case 1: /* BSD, PCI, 2 32bit time words */
433: if (ioctl(fd_vme, READTIME, &btm)) {
434: msyslog(LOG_ERR, "get_bc63x error: %m");
435: return(NULL);
436: }
437: tvme_fill(time_vme, btm);
438: break;
439:
440: case 2: /* Linux/Windows, PCI, 2 32bit time words */
441: if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
442: msyslog(LOG_ERR, "get_datumtime error: %m");
443: return(NULL);
444: }
445: tvme_fill(time_vme, btm);
446: break;
447:
448: case 3: /** solaris **/
449: memset(&stfpm,0,sizeof(stfpm));
450:
451: /* we need the time in decimal format */
452: /* Here we rudely assume that we are the only user of the driver.
453: * Other programs will have to set their own time format before reading
454: * the time.
455: */
456: if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
457: msyslog(LOG_ERR, "Could not set time format\n");
458: return (NULL);
459: }
460: /* read the time */
461: if (ioctl(fd_vme, READTIME, &stfpm)) {
462: msyslog(LOG_ERR, "ioctl error: %m");
463: return(NULL);
464: }
465: stfp_time2tvme(time_vme, &stfpm);
466: break;
467:
468: default: /* legacy bancomm card */
469:
470: if (ioctl(fd_vme, READTIME, &vts)) {
471: msyslog(LOG_ERR,
472: "get_datumtime error: %m");
473: return(NULL);
474: }
475: /* Get day */
476: snprintf(cbuf, sizeof(cbuf), "%3.3x",
477: ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
478: ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
479: time_vme->day = (unsigned short)atoi(cbuf);
480:
481: /* Get hour */
482: snprintf(cbuf, sizeof(cbuf), "%2.2x",
483: vts.btfp_time[ 1 ] & 0x00ff);
484: time_vme->hr = (unsigned short)atoi(cbuf);
485:
486: /* Get minutes */
487: snprintf(cbuf, sizeof(cbuf), "%2.2x",
488: (vts.btfp_time[ 2 ] & 0xff00) >> 8);
489: time_vme->mn = (unsigned short)atoi(cbuf);
490:
491: /* Get seconds */
492: snprintf(cbuf, sizeof(cbuf), "%2.2x",
493: vts.btfp_time[ 2 ] & 0x00ff);
494: time_vme->sec = (unsigned short)atoi(cbuf);
495:
496: /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
497: we can use the TVTOTSF function later on...*/
498:
499: snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
500: vts.btfp_time[ 3 ],
501: vts.btfp_time[ 4 ] >> 8);
502: time_vme->frac = (u_long) atoi(cbuf);
503:
504: /* Get status bit */
505: time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
506:
507: break;
508: }
509:
510: if (time_vme->status)
511: return ((void *)NULL);
512: else
513: return (time_vme);
514: }
515: /* Assign values to time_vme struct. Mostly for readability */
516: void
517: tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
518: {
519: struct tm maj;
520: uint32_t dmaj, dmin;
521:
522: dmaj = btm[1]; /* syntax sugar */
523: dmin = btm[0];
524:
525: gmtime_r(&dmaj, &maj);
526: time_vme->day = maj.tm_yday+1;
527: time_vme->hr = maj.tm_hour;
528: time_vme->mn = maj.tm_min;
529: time_vme->sec = maj.tm_sec;
530: time_vme->frac = (dmin & 0x000fffff) * 1000;
531: time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
532: time_vme->status = (dmin & 0x01000000) >> 24;
533: return;
534: }
535:
536:
537: /* Assign values to time_vme struct. Mostly for readability */
538: void
539: stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
540: {
541:
542: time_vme->day = stfp->tm.tm_yday+1;
543: time_vme->hr = stfp->tm.tm_hour;
544: time_vme->mn = stfp->tm.tm_min;
545: time_vme->sec = stfp->tm.tm_sec;
546: time_vme->frac = stfp->usec*1000;
547: time_vme->frac += stfp->hnsec * 100;
548: time_vme->status = stfp->status;
549: return;
550: }
551: #else
552: int refclock_bancomm_bs;
553: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>