/contrib/ntp/ntpd/refclock_bancomm.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 467 lines · 274 code · 64 blank · 129 comment · 22 complexity · b7ae823c55a9164f269d9c1310d25941 MD5 · raw file

  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. * The time on the bc635 TFP must be set to GMT due to the
  24. * fact that NTP makes use of GMT for all its calculations.
  25. *
  26. * Installation of the Datum/Bancomm driver creates the
  27. * device file /dev/btfp0
  28. *
  29. * 04/28/2005 Rob Neal
  30. * Modified to add support for Symmetricom bc637PCI-U Time &
  31. * Frequency Processor.
  32. * Card bus type (VME/VXI or PCI) and environment are specified via the
  33. * "mode" keyword on the server command in ntp.conf.
  34. * server 127.127.16.u prefer mode m (...)
  35. * Modes currently supported are
  36. * 1 : FreeBSD PCI 635/637.
  37. * 2 : Linux or Windows PCI 635/637.
  38. * not specified, or other number:
  39. * : Assumed to be VME/VXI legacy Bancomm card on Solaris.
  40. * Linux and Windows platforms require Symmetricoms' proprietary driver
  41. * for the TFP card.
  42. * Tested on FreeBSD 5.3 with a 637 card.
  43. */
  44. #ifdef HAVE_CONFIG_H
  45. #include <config.h>
  46. #endif
  47. #if defined(REFCLOCK) && defined(CLOCK_BANC)
  48. #include "ntpd.h"
  49. #include "ntp_io.h"
  50. #include "ntp_refclock.h"
  51. #include "ntp_unixtime.h"
  52. #include "ntp_stdlib.h"
  53. #include <stdio.h>
  54. #include <syslog.h>
  55. #include <ctype.h>
  56. /* STUFF BY RES */
  57. struct btfp_time /* Structure for reading 5 time words */
  58. /* in one ioctl(2) operation. */
  59. {
  60. unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
  61. };
  62. /* SunOS5 ioctl commands definitions.*/
  63. #define BTFPIOC ( 'b'<< 8 )
  64. #define IOCIO( l, n ) ( BTFPIOC | n )
  65. #define IOCIOR( l, n, s ) ( BTFPIOC | n )
  66. #define IOCIORN( l, n, s ) ( BTFPIOC | n )
  67. #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
  68. /***** Simple ioctl commands *****/
  69. #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
  70. #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
  71. #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
  72. /***** Compound ioctl commands *****/
  73. /* Read all 5 time words in one call. */
  74. #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
  75. #if defined(__FreeBSD__)
  76. #undef READTIME
  77. #define READTIME _IOR('u', 5, struct btfp_time )
  78. #endif
  79. #define VMEFD "/dev/btfp0"
  80. struct vmedate { /* structure returned by get_vmetime.c */
  81. unsigned short year;
  82. unsigned short day;
  83. unsigned short hr;
  84. unsigned short mn;
  85. unsigned short sec;
  86. long frac;
  87. unsigned short status;
  88. };
  89. /* END OF STUFF FROM RES */
  90. typedef void *SYMMT_PCI_HANDLE;
  91. /*
  92. * VME interface parameters.
  93. */
  94. #define VMEPRECISION (-21) /* precision assumed (1 us) */
  95. #define USNOREFID "BTFP" /* or whatever */
  96. #define VMEREFID "BTFP" /* reference id */
  97. #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
  98. #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
  99. /* clock type 16 is used here */
  100. #define GMT 0 /* hour offset from Greenwich */
  101. /*
  102. * Imported from ntp_timer module
  103. */
  104. extern u_long current_time; /* current time(s) */
  105. /*
  106. * Imported from ntpd module
  107. */
  108. extern volatile int debug; /* global debug flag */
  109. /*
  110. * VME unit control structure.
  111. * Changes made to vmeunit structure. Most members are now available in the
  112. * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
  113. */
  114. struct vmeunit {
  115. struct vmedate vmedata; /* data returned from vme read */
  116. u_long lasttime; /* last time clock heard from */
  117. };
  118. /*
  119. * Function prototypes
  120. */
  121. static int vme_start (int, struct peer *);
  122. static void vme_shutdown (int, struct peer *);
  123. static void vme_receive (struct recvbuf *);
  124. static void vme_poll (int unit, struct peer *);
  125. struct vmedate *get_datumtime(struct vmedate *);
  126. void tvme_fill(struct vmedate *, uint32_t btm[2]);
  127. /*
  128. * Define the bc*() functions as weak so we can compile/link without them.
  129. * Only clients with the card will have the proprietary vendor device driver
  130. * and interface library needed for use on Linux/Windows platforms.
  131. */
  132. extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
  133. extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
  134. extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
  135. /*
  136. * Transfer vector
  137. */
  138. struct refclock refclock_bancomm = {
  139. vme_start, /* start up driver */
  140. vme_shutdown, /* shut down driver */
  141. vme_poll, /* transmit poll message */
  142. noentry, /* not used (old vme_control) */
  143. noentry, /* initialize driver */
  144. noentry, /* not used (old vme_buginfo) */
  145. NOFLAGS /* not used */
  146. };
  147. int fd_vme; /* file descriptor for ioctls */
  148. int regvalue;
  149. int tfp_type; /* mode selector, indicate platform and driver interface */
  150. SYMMT_PCI_HANDLE stfp_handle;
  151. /*
  152. * vme_start - open the VME device and initialize data for processing
  153. */
  154. static int
  155. vme_start(
  156. int unit,
  157. struct peer *peer
  158. )
  159. {
  160. register struct vmeunit *vme;
  161. struct refclockproc *pp;
  162. int dummy;
  163. char vmedev[20];
  164. tfp_type = (int)(peer->ttl);
  165. switch (tfp_type) {
  166. case 1:
  167. break;
  168. case 2:
  169. stfp_handle = bcStartPci(); /* init the card in lin/win */
  170. break;
  171. default:
  172. break;
  173. }
  174. /*
  175. * Open VME device
  176. */
  177. #ifdef DEBUG
  178. printf("Opening DATUM VME DEVICE \n");
  179. #endif
  180. if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
  181. msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
  182. return (0);
  183. }
  184. else {
  185. switch (tfp_type) {
  186. case 1: break;
  187. case 2: break;
  188. default:
  189. /* Release capture lockout in case it was set before. */
  190. if( ioctl( fd_vme, RUNLOCK, &dummy ) )
  191. msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
  192. regvalue = 0; /* More esoteric stuff to do... */
  193. if( ioctl( fd_vme, WCR0, &regvalue ) )
  194. msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
  195. break;
  196. }
  197. }
  198. /*
  199. * Allocate unit structure
  200. */
  201. vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
  202. bzero((char *)vme, sizeof(struct vmeunit));
  203. /*
  204. * Set up the structures
  205. */
  206. pp = peer->procptr;
  207. pp->unitptr = (caddr_t) vme;
  208. pp->timestarted = current_time;
  209. pp->io.clock_recv = vme_receive;
  210. pp->io.srcclock = (caddr_t)peer;
  211. pp->io.datalen = 0;
  212. pp->io.fd = fd_vme;
  213. /*
  214. * All done. Initialize a few random peer variables, then
  215. * return success. Note that root delay and root dispersion are
  216. * always zero for this clock.
  217. */
  218. peer->precision = VMEPRECISION;
  219. memcpy(&pp->refid, USNOREFID,4);
  220. return (1);
  221. }
  222. /*
  223. * vme_shutdown - shut down a VME clock
  224. */
  225. static void
  226. vme_shutdown(
  227. int unit,
  228. struct peer *peer
  229. )
  230. {
  231. register struct vmeunit *vme;
  232. struct refclockproc *pp;
  233. /*
  234. * Tell the I/O module to turn us off. We're history.
  235. */
  236. pp = peer->procptr;
  237. vme = (struct vmeunit *)pp->unitptr;
  238. io_closeclock(&pp->io);
  239. pp->unitptr = NULL;
  240. free(vme);
  241. if (tfp_type == 2) bcStopPci(stfp_handle);
  242. }
  243. /*
  244. * vme_receive - receive data from the VME device.
  245. *
  246. * Note: This interface would be interrupt-driven. We don't use that
  247. * now, but include a dummy routine for possible future adventures.
  248. */
  249. static void
  250. vme_receive(
  251. struct recvbuf *rbufp
  252. )
  253. {
  254. }
  255. /*
  256. * vme_poll - called by the transmit procedure
  257. */
  258. static void
  259. vme_poll(
  260. int unit,
  261. struct peer *peer
  262. )
  263. {
  264. struct vmedate *tptr;
  265. struct vmeunit *vme;
  266. struct refclockproc *pp;
  267. time_t tloc;
  268. struct tm *tadr;
  269. pp = peer->procptr;
  270. vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */
  271. tptr = &vme->vmedata;
  272. if ((tptr = get_datumtime(tptr)) == NULL ) {
  273. refclock_report(peer, CEVNT_BADREPLY);
  274. return;
  275. }
  276. get_systime(&pp->lastrec);
  277. pp->polls++;
  278. vme->lasttime = current_time;
  279. /*
  280. * Get VME time and convert to timestamp format.
  281. * The year must come from the system clock.
  282. */
  283. time(&tloc);
  284. tadr = gmtime(&tloc);
  285. tptr->year = (unsigned short)(tadr->tm_year + 1900);
  286. sprintf(pp->a_lastcode,
  287. "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
  288. tptr->day,
  289. tptr->hr,
  290. tptr->mn,
  291. tptr->sec,
  292. tptr->frac,
  293. tptr->status);
  294. pp->lencode = (u_short) strlen(pp->a_lastcode);
  295. pp->day = tptr->day;
  296. pp->hour = tptr->hr;
  297. pp->minute = tptr->mn;
  298. pp->second = tptr->sec;
  299. pp->nsec = tptr->frac;
  300. #ifdef DEBUG
  301. if (debug)
  302. printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
  303. pp->day, pp->hour, pp->minute, pp->second,
  304. pp->nsec, tptr->status);
  305. #endif
  306. if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
  307. refclock_report(peer, CEVNT_BADREPLY);
  308. return;
  309. }
  310. /*
  311. * Now, compute the reference time value. Use the heavy
  312. * machinery for the seconds and the millisecond field for the
  313. * fraction when present. If an error in conversion to internal
  314. * format is found, the program declares bad data and exits.
  315. * Note that this code does not yet know how to do the years and
  316. * relies on the clock-calendar chip for sanity.
  317. */
  318. if (!refclock_process(pp)) {
  319. refclock_report(peer, CEVNT_BADTIME);
  320. return;
  321. }
  322. pp->lastref = pp->lastrec;
  323. refclock_receive(peer);
  324. record_clock_stats(&peer->srcadr, pp->a_lastcode);
  325. }
  326. struct vmedate *
  327. get_datumtime(struct vmedate *time_vme)
  328. {
  329. char cbuf[7];
  330. struct btfp_time vts;
  331. uint32_t btm[2];
  332. uint8_t dmy;
  333. if ( time_vme == (struct vmedate *)NULL) {
  334. time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
  335. }
  336. switch (tfp_type) {
  337. case 1: /* BSD, PCI, 2 32bit time words */
  338. if (ioctl(fd_vme, READTIME, &btm)) {
  339. msyslog(LOG_ERR, "get_bc63x error: %m");
  340. return(NULL);
  341. }
  342. tvme_fill(time_vme, btm);
  343. break;
  344. case 2: /* Linux/Windows, PCI, 2 32bit time words */
  345. if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
  346. msyslog(LOG_ERR, "get_datumtime error: %m");
  347. return(NULL);
  348. }
  349. tvme_fill(time_vme, btm);
  350. break;
  351. default: /* legacy bancomm card */
  352. if (ioctl(fd_vme, READTIME, &vts)) {
  353. msyslog(LOG_ERR, "get_datumtime error: %m");
  354. return(NULL);
  355. }
  356. /* Get day */
  357. sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
  358. ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
  359. time_vme->day = (unsigned short)atoi(cbuf);
  360. /* Get hour */
  361. sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
  362. time_vme->hr = (unsigned short)atoi(cbuf);
  363. /* Get minutes */
  364. sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
  365. time_vme->mn = (unsigned short)atoi(cbuf);
  366. /* Get seconds */
  367. sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
  368. time_vme->sec = (unsigned short)atoi(cbuf);
  369. /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
  370. we can use the TVTOTSF function later on...*/
  371. sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
  372. vts.btfp_time[ 4 ]>>8);
  373. time_vme->frac = (u_long) atoi(cbuf);
  374. /* Get status bit */
  375. time_vme->status = (vts.btfp_time[0] & 0x0010) >>4;
  376. break;
  377. }
  378. if (time_vme->status)
  379. return ((void *)NULL);
  380. else
  381. return (time_vme);
  382. }
  383. /* Assign values to time_vme struct. Mostly for readability */
  384. void
  385. tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
  386. {
  387. struct tm maj;
  388. uint32_t dmaj, dmin;
  389. dmaj = btm[1]; /* syntax sugar */
  390. dmin = btm[0];
  391. gmtime_r(&dmaj, &maj);
  392. time_vme->day = maj.tm_yday+1;
  393. time_vme->hr = maj.tm_hour;
  394. time_vme->mn = maj.tm_min;
  395. time_vme->sec = maj.tm_sec;
  396. time_vme->frac = (dmin & 0x000fffff) * 1000;
  397. time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
  398. time_vme->status = (dmin & 0x01000000) >> 24;
  399. return;
  400. }
  401. #else
  402. int refclock_bancomm_bs;
  403. #endif /* REFCLOCK */