1c0b746e5SOllivier Robert /* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME 2c0b746e5SOllivier Robert * Time and Frequency Processor. It requires the BANCOMM bc635VME/ 3c0b746e5SOllivier Robert * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x 4c0b746e5SOllivier Robert * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc 5c0b746e5SOllivier Robert * IIi-cEngine running Solaris 2.6. 6c0b746e5SOllivier Robert * 7c0b746e5SOllivier Robert * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada, 8c0b746e5SOllivier Robert * Ottawa, Canada 9c0b746e5SOllivier Robert * 10c0b746e5SOllivier Robert * Date: July 1999 11c0b746e5SOllivier Robert * 12c0b746e5SOllivier Robert * Note(s): The refclock type has been defined as 16. 13c0b746e5SOllivier Robert * 14c0b746e5SOllivier Robert * This program has been modelled after the Bancomm driver 15c0b746e5SOllivier Robert * originally written by R. Schmidt of Time Service, U.S. 16c0b746e5SOllivier Robert * Naval Observatory for a HP-UX machine. Since the original 17c0b746e5SOllivier Robert * authors no longer plan to maintain this code, all 18c0b746e5SOllivier Robert * references to the HP-UX vme2 driver subsystem bave been 19c0b746e5SOllivier Robert * removed. Functions vme_report_event(), vme_receive(), 20c0b746e5SOllivier Robert * vme_control() and vme_buginfo() have been deleted because 21c0b746e5SOllivier Robert * they are no longer being used. 22c0b746e5SOllivier Robert * 23ea906c41SOllivier Robert * 04/28/2005 Rob Neal 24ea906c41SOllivier Robert * Modified to add support for Symmetricom bc637PCI-U Time & 25ea906c41SOllivier Robert * Frequency Processor. 262b15cb3dSCy Schubert * 2/21/2007 Ali Ghorashi 272b15cb3dSCy Schubert * Modified to add support for Symmetricom bc637PCI-U Time & 282b15cb3dSCy Schubert * Frequency Processor on Solaris. 292b15cb3dSCy Schubert * Tested on Solaris 10 with a bc635 card. 302b15cb3dSCy Schubert * 31ea906c41SOllivier Robert * Card bus type (VME/VXI or PCI) and environment are specified via the 32ea906c41SOllivier Robert * "mode" keyword on the server command in ntp.conf. 332b15cb3dSCy Schubert * server 127.127.16.u prefer mode M 342b15cb3dSCy Schubert * where u is the id (usually 0) of the entry in /dev (/dev/stfp0) 352b15cb3dSCy Schubert * 362b15cb3dSCy Schubert * and M is one of the following modes: 37ea906c41SOllivier Robert * 1 : FreeBSD PCI 635/637. 38ea906c41SOllivier Robert * 2 : Linux or Windows PCI 635/637. 392b15cb3dSCy Schubert * 3 : Solaris PCI 635/637 40ea906c41SOllivier Robert * not specified, or other number: 41ea906c41SOllivier Robert * : Assumed to be VME/VXI legacy Bancomm card on Solaris. 42ea906c41SOllivier Robert * Linux and Windows platforms require Symmetricoms' proprietary driver 43ea906c41SOllivier Robert * for the TFP card. 442b15cb3dSCy Schubert * Solaris requires Symmetricom's driver and its header file (freely distributed) to 452b15cb3dSCy Schubert * be installed and running. 46c0b746e5SOllivier Robert */ 47c0b746e5SOllivier Robert 48c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 49c0b746e5SOllivier Robert #include <config.h> 50c0b746e5SOllivier Robert #endif 51c0b746e5SOllivier Robert 52c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_BANC) 53c0b746e5SOllivier Robert 54c0b746e5SOllivier Robert #include "ntpd.h" 55c0b746e5SOllivier Robert #include "ntp_io.h" 56c0b746e5SOllivier Robert #include "ntp_refclock.h" 57c0b746e5SOllivier Robert #include "ntp_unixtime.h" 58c0b746e5SOllivier Robert #include "ntp_stdlib.h" 59c0b746e5SOllivier Robert 60224ba2bdSOllivier Robert #include <stdio.h> 61224ba2bdSOllivier Robert #include <syslog.h> 62224ba2bdSOllivier Robert #include <ctype.h> 63052d159aSCy Schubert #ifdef HAVE_SYS_IOCTL_H 64052d159aSCy Schubert # include <sys/ioctl.h> 65052d159aSCy Schubert #endif 66224ba2bdSOllivier Robert 67c0b746e5SOllivier Robert struct btfp_time /* Structure for reading 5 time words */ 68c0b746e5SOllivier Robert /* in one ioctl(2) operation. */ 69c0b746e5SOllivier Robert { 70c0b746e5SOllivier Robert unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/ 71c0b746e5SOllivier Robert }; 72c0b746e5SOllivier Robert /* SunOS5 ioctl commands definitions.*/ 73c0b746e5SOllivier Robert #define BTFPIOC ( 'b'<< 8 ) 74c0b746e5SOllivier Robert #define IOCIO( l, n ) ( BTFPIOC | n ) 75c0b746e5SOllivier Robert #define IOCIOR( l, n, s ) ( BTFPIOC | n ) 76c0b746e5SOllivier Robert #define IOCIORN( l, n, s ) ( BTFPIOC | n ) 77c0b746e5SOllivier Robert #define IOCIOWN( l, n, s ) ( BTFPIOC | n ) 78c0b746e5SOllivier Robert 79c0b746e5SOllivier Robert /***** Simple ioctl commands *****/ 80c0b746e5SOllivier Robert #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */ 81c0b746e5SOllivier Robert #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/ 82c0b746e5SOllivier Robert #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/ 83c0b746e5SOllivier Robert /***** Compound ioctl commands *****/ 84c0b746e5SOllivier Robert 85c0b746e5SOllivier Robert /* Read all 5 time words in one call. */ 86ea906c41SOllivier Robert #if defined(__FreeBSD__) 87ea906c41SOllivier Robert # define READTIME _IOR('u', 5, struct btfp_time ) 88052d159aSCy Schubert #else 89052d159aSCy Schubert # define READTIME IOCIORN(b, 32, sizeof( struct btfp_time )) 90ea906c41SOllivier Robert #endif 91ea906c41SOllivier Robert 922b15cb3dSCy Schubert /* Solaris specific section */ 932b15cb3dSCy Schubert struct stfp_tm { 942b15cb3dSCy Schubert int32_t tm_sec; 952b15cb3dSCy Schubert int32_t tm_min; 962b15cb3dSCy Schubert int32_t tm_hour; 972b15cb3dSCy Schubert int32_t tm_mday; 982b15cb3dSCy Schubert int32_t tm_mon; 992b15cb3dSCy Schubert int32_t tm_year; 1002b15cb3dSCy Schubert int32_t tm_wday; 1012b15cb3dSCy Schubert int32_t tm_yday; 1022b15cb3dSCy Schubert int32_t tm_isdst; 1032b15cb3dSCy Schubert }; 1042b15cb3dSCy Schubert 1052b15cb3dSCy Schubert struct stfp_time { 1062b15cb3dSCy Schubert struct stfp_tm tm; 1072b15cb3dSCy Schubert int32_t usec; /* usec 0 - 999999 */ 1082b15cb3dSCy Schubert int32_t hnsec; /* hnsec 0 - 9 (hundreds of nsecs) */ 1092b15cb3dSCy Schubert int32_t status; 1102b15cb3dSCy Schubert }; 1112b15cb3dSCy Schubert 1122b15cb3dSCy Schubert #define SELTIMEFORMAT 2 1132b15cb3dSCy Schubert # define TIME_DECIMAL 0 1142b15cb3dSCy Schubert # define TIME_BINARY 1 1152b15cb3dSCy Schubert 1162b15cb3dSCy Schubert #if defined(__sun__) 1172b15cb3dSCy Schubert #undef READTIME 1182b15cb3dSCy Schubert #define READTIME 9 1192b15cb3dSCy Schubert #endif /** __sun___ **/ 1202b15cb3dSCy Schubert /* end solaris specific section */ 121c0b746e5SOllivier Robert 122c0b746e5SOllivier Robert struct vmedate { /* structure returned by get_vmetime.c */ 123c0b746e5SOllivier Robert unsigned short year; 124c0b746e5SOllivier Robert unsigned short day; 125c0b746e5SOllivier Robert unsigned short hr; 126c0b746e5SOllivier Robert unsigned short mn; 127c0b746e5SOllivier Robert unsigned short sec; 128ea906c41SOllivier Robert long frac; 129c0b746e5SOllivier Robert unsigned short status; 130c0b746e5SOllivier Robert }; 131c0b746e5SOllivier Robert 132ea906c41SOllivier Robert typedef void *SYMMT_PCI_HANDLE; 133c0b746e5SOllivier Robert 134c0b746e5SOllivier Robert /* 135c0b746e5SOllivier Robert * VME interface parameters. 136c0b746e5SOllivier Robert */ 137c0b746e5SOllivier Robert #define VMEPRECISION (-21) /* precision assumed (1 us) */ 138c0b746e5SOllivier Robert #define USNOREFID "BTFP" /* or whatever */ 139c0b746e5SOllivier Robert #define VMEREFID "BTFP" /* reference id */ 140c0b746e5SOllivier Robert #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */ 141c0b746e5SOllivier Robert #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */ 142c0b746e5SOllivier Robert /* clock type 16 is used here */ 143c0b746e5SOllivier Robert #define GMT 0 /* hour offset from Greenwich */ 144c0b746e5SOllivier Robert 145c0b746e5SOllivier Robert /* 146c0b746e5SOllivier Robert * Imported from ntp_timer module 147c0b746e5SOllivier Robert */ 148c0b746e5SOllivier Robert extern u_long current_time; /* current time(s) */ 149c0b746e5SOllivier Robert 150c0b746e5SOllivier Robert /* 151c0b746e5SOllivier Robert * VME unit control structure. 152c0b746e5SOllivier Robert * Changes made to vmeunit structure. Most members are now available in the 153c0b746e5SOllivier Robert * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan 154c0b746e5SOllivier Robert */ 155c0b746e5SOllivier Robert struct vmeunit { 156c0b746e5SOllivier Robert struct vmedate vmedata; /* data returned from vme read */ 157c0b746e5SOllivier Robert u_long lasttime; /* last time clock heard from */ 158c0b746e5SOllivier Robert }; 159c0b746e5SOllivier Robert 160c0b746e5SOllivier Robert /* 161c0b746e5SOllivier Robert * Function prototypes 162c0b746e5SOllivier Robert */ 163c0b746e5SOllivier Robert static int vme_start (int, struct peer *); 164c0b746e5SOllivier Robert static void vme_shutdown (int, struct peer *); 165c0b746e5SOllivier Robert static void vme_receive (struct recvbuf *); 166c0b746e5SOllivier Robert static void vme_poll (int unit, struct peer *); 167c0b746e5SOllivier Robert struct vmedate *get_datumtime(struct vmedate *); 168ea906c41SOllivier Robert void tvme_fill(struct vmedate *, uint32_t btm[2]); 1692b15cb3dSCy Schubert void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp); 170052d159aSCy Schubert static const char *get_devicename(int n); 1712b15cb3dSCy Schubert 172052d159aSCy Schubert /* [Bug 3558] and [Bug 1674] perlinger@ntp.org says: 173052d159aSCy Schubert * 174052d159aSCy Schubert * bcReadBinTime() is defined to use two DWORD pointers on Windows and 175052d159aSCy Schubert * Linux in the BANCOMM SDK. DWORD is of course Windows-specific 176052d159aSCy Schubert * (*shudder*), and it is defined as 'unsigned long' under 177052d159aSCy Schubert * Linux/Unix. (*sigh*) 178052d159aSCy Schubert * 179052d159aSCy Schubert * This creates quite some headache. The size of 'unsigned long' is 180052d159aSCy Schubert * platform/compiler/memory-model dependent (LP32 vs LP64 vs LLP64), 181052d159aSCy Schubert * while the card itself always creates 32bit time stamps. What a 182052d159aSCy Schubert * bummer. And DWORD has tendency to contain 64bit on Win64 (which is 183052d159aSCy Schubert * why we have a DWORD32 defined on Win64) so it can be used as 184052d159aSCy Schubert * substitute for 'UINT_PTR' in Windows API headers. I won't even try 185052d159aSCy Schubert * to comment on that, because anything I have to say will not be civil. 186052d159aSCy Schubert * 187052d159aSCy Schubert * We work around this by possibly using a wrapper function that makes 188052d159aSCy Schubert * the necessary conversions/casts. It might be a bit tricky to 189052d159aSCy Schubert * maintain the conditional logic below, but any lingering disease needs 190052d159aSCy Schubert * constant care to avoid a breakout. 191052d159aSCy Schubert */ 192052d159aSCy Schubert #if defined(__linux__) 193052d159aSCy Schubert typedef unsigned long bcBinTimeT; 194052d159aSCy Schubert # if SIZEOF_LONG == 4 195052d159aSCy Schubert # define safeReadBinTime bcReadBinTime 196052d159aSCy Schubert # endif 197052d159aSCy Schubert #elif defined(SYS_WINNT) 198052d159aSCy Schubert typedef DWORD bcBinTimeT; 199052d159aSCy Schubert # if !defined(_WIN64) || _WIN64 == 0 200052d159aSCy Schubert # define safeReadBinTime bcReadBinTime 201052d159aSCy Schubert # endif 202052d159aSCy Schubert #else 203052d159aSCy Schubert typedef uint32_t bcBinTimeT; 204052d159aSCy Schubert # define safeReadBinTime bcReadBinTime 205052d159aSCy Schubert #endif 2062b15cb3dSCy Schubert 207ea906c41SOllivier Robert /* 208ea906c41SOllivier Robert * Define the bc*() functions as weak so we can compile/link without them. 209ea906c41SOllivier Robert * Only clients with the card will have the proprietary vendor device driver 210ea906c41SOllivier Robert * and interface library needed for use on Linux/Windows platforms. 211ea906c41SOllivier Robert */ 212052d159aSCy Schubert extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, bcBinTimeT*, bcBinTimeT*, uint8_t*); 213ea906c41SOllivier Robert extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void); 214ea906c41SOllivier Robert extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE); 215c0b746e5SOllivier Robert 216052d159aSCy Schubert /* This is the conversion wrapper for the long/DWORD/uint32_t clash in 217052d159aSCy Schubert * reading binary times. 218052d159aSCy Schubert */ 219052d159aSCy Schubert #ifndef safeReadBinTime 220052d159aSCy Schubert static uint32_t 221052d159aSCy Schubert safeReadBinTime( 222052d159aSCy Schubert SYMMT_PCI_HANDLE hnd, 223052d159aSCy Schubert uint32_t *pt1, 224052d159aSCy Schubert uint32_t *pt2, 225052d159aSCy Schubert uint8_t *p3 226052d159aSCy Schubert ) 227052d159aSCy Schubert { 228052d159aSCy Schubert bcBinTimeT t1, t2; 229052d159aSCy Schubert uint32_t rc; 230052d159aSCy Schubert 231052d159aSCy Schubert rc = bcReadBinTime(hnd, &t1, &t2, p3); 232052d159aSCy Schubert if (rc != 0) { 233052d159aSCy Schubert *pt1 = (uint32_t)t1; 234052d159aSCy Schubert *pt2 = (uint32_t)t2; 235052d159aSCy Schubert } 236052d159aSCy Schubert return rc; 237052d159aSCy Schubert } 238052d159aSCy Schubert #endif /* !defined(safeReadBinTime) */ 239052d159aSCy Schubert 240c0b746e5SOllivier Robert /* 241c0b746e5SOllivier Robert * Transfer vector 242c0b746e5SOllivier Robert */ 243c0b746e5SOllivier Robert struct refclock refclock_bancomm = { 244224ba2bdSOllivier Robert vme_start, /* start up driver */ 245224ba2bdSOllivier Robert vme_shutdown, /* shut down driver */ 246224ba2bdSOllivier Robert vme_poll, /* transmit poll message */ 247c0b746e5SOllivier Robert noentry, /* not used (old vme_control) */ 248224ba2bdSOllivier Robert noentry, /* initialize driver */ 249c0b746e5SOllivier Robert noentry, /* not used (old vme_buginfo) */ 250224ba2bdSOllivier Robert NOFLAGS /* not used */ 251c0b746e5SOllivier Robert }; 252c0b746e5SOllivier Robert 253c0b746e5SOllivier Robert int fd_vme; /* file descriptor for ioctls */ 254c0b746e5SOllivier Robert int regvalue; 255ea906c41SOllivier Robert int tfp_type; /* mode selector, indicate platform and driver interface */ 256ea906c41SOllivier Robert SYMMT_PCI_HANDLE stfp_handle; 257c0b746e5SOllivier Robert 258052d159aSCy Schubert /* This helper function returns the device name based on the platform we 259052d159aSCy Schubert * are running on and the device number. 260052d159aSCy Schubert * 261052d159aSCy Schubert * Uses a static buffer, so the result is valid only to the next call of 262052d159aSCy Schubert * this function! 2632b15cb3dSCy Schubert */ 264052d159aSCy Schubert static const char* 265052d159aSCy Schubert get_devicename(int n) 266052d159aSCy Schubert { 267052d159aSCy Schubert 2682b15cb3dSCy Schubert # if defined(__sun__) 269052d159aSCy Schubert static const char * const template ="/dev/stfp%d"; 2702b15cb3dSCy Schubert # else 271052d159aSCy Schubert static const char * const template ="/dev/btfp%d"; 272052d159aSCy Schubert # endif 273052d159aSCy Schubert static char namebuf[20]; 274052d159aSCy Schubert 275052d159aSCy Schubert snprintf(namebuf, sizeof(namebuf), template, n); 276052d159aSCy Schubert namebuf[sizeof(namebuf)-1] = '\0'; /* paranoia rulez! */ 277052d159aSCy Schubert return namebuf; 278052d159aSCy Schubert } 279c0b746e5SOllivier Robert 280c0b746e5SOllivier Robert /* 281c0b746e5SOllivier Robert * vme_start - open the VME device and initialize data for processing 282c0b746e5SOllivier Robert */ 283c0b746e5SOllivier Robert static int 284c0b746e5SOllivier Robert vme_start( 285c0b746e5SOllivier Robert int unit, 286c0b746e5SOllivier Robert struct peer *peer 287c0b746e5SOllivier Robert ) 288c0b746e5SOllivier Robert { 289c0b746e5SOllivier Robert register struct vmeunit *vme; 290c0b746e5SOllivier Robert struct refclockproc *pp; 291c0b746e5SOllivier Robert int dummy; 292c0b746e5SOllivier Robert char vmedev[20]; 293c0b746e5SOllivier Robert 294ea906c41SOllivier Robert tfp_type = (int)(peer->ttl); 295ea906c41SOllivier Robert switch (tfp_type) { 296ea906c41SOllivier Robert case 1: 2972b15cb3dSCy Schubert case 3: 298ea906c41SOllivier Robert break; 299ea906c41SOllivier Robert case 2: 300ea906c41SOllivier Robert stfp_handle = bcStartPci(); /* init the card in lin/win */ 301ea906c41SOllivier Robert break; 302ea906c41SOllivier Robert default: 303ea906c41SOllivier Robert break; 304ea906c41SOllivier Robert } 305c0b746e5SOllivier Robert /* 306c0b746e5SOllivier Robert * Open VME device 307c0b746e5SOllivier Robert */ 308c0b746e5SOllivier Robert #ifdef DEBUG 309c0b746e5SOllivier Robert 310052d159aSCy Schubert printf("Opening DATUM DEVICE %s\n",get_devicename(peer->refclkunit)); 311c0b746e5SOllivier Robert #endif 312052d159aSCy Schubert if ( (fd_vme = open(get_devicename(peer->refclkunit), O_RDWR)) < 0) { 313c0b746e5SOllivier Robert msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev); 314c0b746e5SOllivier Robert return (0); 315c0b746e5SOllivier Robert } 316ea906c41SOllivier Robert else { 317ea906c41SOllivier Robert switch (tfp_type) { 318ea906c41SOllivier Robert case 1: break; 319ea906c41SOllivier Robert case 2: break; 3202b15cb3dSCy Schubert case 3:break; 321ea906c41SOllivier Robert default: 322ea906c41SOllivier Robert /* Release capture lockout in case it was set before. */ 323c0b746e5SOllivier Robert if( ioctl( fd_vme, RUNLOCK, &dummy ) ) 324c0b746e5SOllivier Robert msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); 325c0b746e5SOllivier Robert 326c0b746e5SOllivier Robert regvalue = 0; /* More esoteric stuff to do... */ 327c0b746e5SOllivier Robert if( ioctl( fd_vme, WCR0, ®value ) ) 328c0b746e5SOllivier Robert msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); 329ea906c41SOllivier Robert break; 330ea906c41SOllivier Robert } 331c0b746e5SOllivier Robert } 332c0b746e5SOllivier Robert 333c0b746e5SOllivier Robert /* 334c0b746e5SOllivier Robert * Allocate unit structure 335c0b746e5SOllivier Robert */ 3362b15cb3dSCy Schubert vme = emalloc_zero(sizeof(struct vmeunit)); 337c0b746e5SOllivier Robert 338c0b746e5SOllivier Robert 339c0b746e5SOllivier Robert /* 340c0b746e5SOllivier Robert * Set up the structures 341c0b746e5SOllivier Robert */ 342c0b746e5SOllivier Robert pp = peer->procptr; 3432b15cb3dSCy Schubert pp->unitptr = vme; 344c0b746e5SOllivier Robert pp->timestarted = current_time; 345c0b746e5SOllivier Robert 346c0b746e5SOllivier Robert pp->io.clock_recv = vme_receive; 3472b15cb3dSCy Schubert pp->io.srcclock = peer; 348c0b746e5SOllivier Robert pp->io.datalen = 0; 349c0b746e5SOllivier Robert pp->io.fd = fd_vme; 3502b15cb3dSCy Schubert /* shouldn't there be an io_addclock() call? */ 351c0b746e5SOllivier Robert 352c0b746e5SOllivier Robert /* 353c0b746e5SOllivier Robert * All done. Initialize a few random peer variables, then 354c0b746e5SOllivier Robert * return success. Note that root delay and root dispersion are 355c0b746e5SOllivier Robert * always zero for this clock. 356c0b746e5SOllivier Robert */ 357c0b746e5SOllivier Robert peer->precision = VMEPRECISION; 358224ba2bdSOllivier Robert memcpy(&pp->refid, USNOREFID,4); 359c0b746e5SOllivier Robert return (1); 360c0b746e5SOllivier Robert } 361c0b746e5SOllivier Robert 362c0b746e5SOllivier Robert 363c0b746e5SOllivier Robert /* 364c0b746e5SOllivier Robert * vme_shutdown - shut down a VME clock 365c0b746e5SOllivier Robert */ 366c0b746e5SOllivier Robert static void 367c0b746e5SOllivier Robert vme_shutdown( 368c0b746e5SOllivier Robert int unit, 369c0b746e5SOllivier Robert struct peer *peer 370c0b746e5SOllivier Robert ) 371c0b746e5SOllivier Robert { 372c0b746e5SOllivier Robert register struct vmeunit *vme; 373c0b746e5SOllivier Robert struct refclockproc *pp; 374c0b746e5SOllivier Robert 375c0b746e5SOllivier Robert /* 376c0b746e5SOllivier Robert * Tell the I/O module to turn us off. We're history. 377c0b746e5SOllivier Robert */ 378224ba2bdSOllivier Robert pp = peer->procptr; 3792b15cb3dSCy Schubert vme = pp->unitptr; 380c0b746e5SOllivier Robert io_closeclock(&pp->io); 381c0b746e5SOllivier Robert pp->unitptr = NULL; 3822b15cb3dSCy Schubert if (NULL != vme) 383c0b746e5SOllivier Robert free(vme); 3842b15cb3dSCy Schubert if (tfp_type == 2) 3852b15cb3dSCy Schubert bcStopPci(stfp_handle); 386c0b746e5SOllivier Robert } 387c0b746e5SOllivier Robert 388c0b746e5SOllivier Robert 389c0b746e5SOllivier Robert /* 390c0b746e5SOllivier Robert * vme_receive - receive data from the VME device. 391c0b746e5SOllivier Robert * 392c0b746e5SOllivier Robert * Note: This interface would be interrupt-driven. We don't use that 393c0b746e5SOllivier Robert * now, but include a dummy routine for possible future adventures. 394c0b746e5SOllivier Robert */ 395c0b746e5SOllivier Robert static void 396c0b746e5SOllivier Robert vme_receive( 397c0b746e5SOllivier Robert struct recvbuf *rbufp 398c0b746e5SOllivier Robert ) 399c0b746e5SOllivier Robert { 400c0b746e5SOllivier Robert } 401c0b746e5SOllivier Robert 402c0b746e5SOllivier Robert 403c0b746e5SOllivier Robert /* 404c0b746e5SOllivier Robert * vme_poll - called by the transmit procedure 405c0b746e5SOllivier Robert */ 406c0b746e5SOllivier Robert static void 407c0b746e5SOllivier Robert vme_poll( 408c0b746e5SOllivier Robert int unit, 409c0b746e5SOllivier Robert struct peer *peer 410c0b746e5SOllivier Robert ) 411c0b746e5SOllivier Robert { 412c0b746e5SOllivier Robert struct vmedate *tptr; 413c0b746e5SOllivier Robert struct vmeunit *vme; 414c0b746e5SOllivier Robert struct refclockproc *pp; 415c0b746e5SOllivier Robert time_t tloc; 416c0b746e5SOllivier Robert struct tm *tadr; 417c0b746e5SOllivier Robert 418c0b746e5SOllivier Robert pp = peer->procptr; 4192b15cb3dSCy Schubert vme = pp->unitptr; /* Here is the structure */ 420c0b746e5SOllivier Robert 421c0b746e5SOllivier Robert tptr = &vme->vmedata; 422c0b746e5SOllivier Robert if ((tptr = get_datumtime(tptr)) == NULL ) { 423c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 424c0b746e5SOllivier Robert return; 425c0b746e5SOllivier Robert } 426c0b746e5SOllivier Robert 427c0b746e5SOllivier Robert get_systime(&pp->lastrec); 428c0b746e5SOllivier Robert pp->polls++; 429c0b746e5SOllivier Robert vme->lasttime = current_time; 430c0b746e5SOllivier Robert 431c0b746e5SOllivier Robert /* 432c0b746e5SOllivier Robert * Get VME time and convert to timestamp format. 433c0b746e5SOllivier Robert * The year must come from the system clock. 434c0b746e5SOllivier Robert */ 435c0b746e5SOllivier Robert 436c0b746e5SOllivier Robert time(&tloc); 437c0b746e5SOllivier Robert tadr = gmtime(&tloc); 438c0b746e5SOllivier Robert tptr->year = (unsigned short)(tadr->tm_year + 1900); 439c0b746e5SOllivier Robert 4402b15cb3dSCy Schubert snprintf(pp->a_lastcode, 4412b15cb3dSCy Schubert sizeof(pp->a_lastcode), 442c0b746e5SOllivier Robert "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d", 443c0b746e5SOllivier Robert tptr->day, 444c0b746e5SOllivier Robert tptr->hr, 445c0b746e5SOllivier Robert tptr->mn, 446c0b746e5SOllivier Robert tptr->sec, 447c0b746e5SOllivier Robert tptr->frac, 448c0b746e5SOllivier Robert tptr->status); 449c0b746e5SOllivier Robert 450c0b746e5SOllivier Robert pp->lencode = (u_short) strlen(pp->a_lastcode); 451c0b746e5SOllivier Robert 452c0b746e5SOllivier Robert pp->day = tptr->day; 453c0b746e5SOllivier Robert pp->hour = tptr->hr; 454c0b746e5SOllivier Robert pp->minute = tptr->mn; 455c0b746e5SOllivier Robert pp->second = tptr->sec; 456ea906c41SOllivier Robert pp->nsec = tptr->frac; 457c0b746e5SOllivier Robert 458c0b746e5SOllivier Robert #ifdef DEBUG 459c0b746e5SOllivier Robert if (debug) 460c0b746e5SOllivier Robert printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", 461c0b746e5SOllivier Robert pp->day, pp->hour, pp->minute, pp->second, 462ea906c41SOllivier Robert pp->nsec, tptr->status); 463c0b746e5SOllivier Robert #endif 464c0b746e5SOllivier Robert if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ 465c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 466c0b746e5SOllivier Robert return; 467c0b746e5SOllivier Robert } 468c0b746e5SOllivier Robert 469c0b746e5SOllivier Robert /* 470c0b746e5SOllivier Robert * Now, compute the reference time value. Use the heavy 471c0b746e5SOllivier Robert * machinery for the seconds and the millisecond field for the 472c0b746e5SOllivier Robert * fraction when present. If an error in conversion to internal 473c0b746e5SOllivier Robert * format is found, the program declares bad data and exits. 474c0b746e5SOllivier Robert * Note that this code does not yet know how to do the years and 475c0b746e5SOllivier Robert * relies on the clock-calendar chip for sanity. 476c0b746e5SOllivier Robert */ 477c0b746e5SOllivier Robert if (!refclock_process(pp)) { 478c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 479c0b746e5SOllivier Robert return; 480c0b746e5SOllivier Robert } 4819c2daa00SOllivier Robert pp->lastref = pp->lastrec; 482c0b746e5SOllivier Robert refclock_receive(peer); 4839c2daa00SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 484c0b746e5SOllivier Robert } 485c0b746e5SOllivier Robert 486c0b746e5SOllivier Robert struct vmedate * 487c0b746e5SOllivier Robert get_datumtime(struct vmedate *time_vme) 488c0b746e5SOllivier Robert { 489c0b746e5SOllivier Robert char cbuf[7]; 490c0b746e5SOllivier Robert struct btfp_time vts; 491ea906c41SOllivier Robert uint32_t btm[2]; 492ea906c41SOllivier Robert uint8_t dmy; 4932b15cb3dSCy Schubert struct stfp_time stfpm; 494c0b746e5SOllivier Robert 4952b15cb3dSCy Schubert if (time_vme == NULL) 4962b15cb3dSCy Schubert time_vme = emalloc(sizeof(*time_vme)); 497c0b746e5SOllivier Robert 498ea906c41SOllivier Robert switch (tfp_type) { 499ea906c41SOllivier Robert case 1: /* BSD, PCI, 2 32bit time words */ 500ea906c41SOllivier Robert if (ioctl(fd_vme, READTIME, &btm)) { 501ea906c41SOllivier Robert msyslog(LOG_ERR, "get_bc63x error: %m"); 502ea906c41SOllivier Robert return(NULL); 503ea906c41SOllivier Robert } 504ea906c41SOllivier Robert tvme_fill(time_vme, btm); 505ea906c41SOllivier Robert break; 506ea906c41SOllivier Robert 507ea906c41SOllivier Robert case 2: /* Linux/Windows, PCI, 2 32bit time words */ 508052d159aSCy Schubert if (safeReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) { 509c0b746e5SOllivier Robert msyslog(LOG_ERR, "get_datumtime error: %m"); 510ea906c41SOllivier Robert return(NULL); 511ea906c41SOllivier Robert } 512ea906c41SOllivier Robert tvme_fill(time_vme, btm); 513ea906c41SOllivier Robert break; 514c0b746e5SOllivier Robert 5152b15cb3dSCy Schubert case 3: /** solaris **/ 5162b15cb3dSCy Schubert memset(&stfpm,0,sizeof(stfpm)); 5172b15cb3dSCy Schubert 5182b15cb3dSCy Schubert /* we need the time in decimal format */ 5192b15cb3dSCy Schubert /* Here we rudely assume that we are the only user of the driver. 5202b15cb3dSCy Schubert * Other programs will have to set their own time format before reading 5212b15cb3dSCy Schubert * the time. 5222b15cb3dSCy Schubert */ 5232b15cb3dSCy Schubert if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){ 5242b15cb3dSCy Schubert msyslog(LOG_ERR, "Could not set time format"); 5252b15cb3dSCy Schubert return (NULL); 5262b15cb3dSCy Schubert } 5272b15cb3dSCy Schubert /* read the time */ 5282b15cb3dSCy Schubert if (ioctl(fd_vme, READTIME, &stfpm)) { 5292b15cb3dSCy Schubert msyslog(LOG_ERR, "ioctl error: %m"); 5302b15cb3dSCy Schubert return(NULL); 5312b15cb3dSCy Schubert } 5322b15cb3dSCy Schubert stfp_time2tvme(time_vme, &stfpm); 5332b15cb3dSCy Schubert break; 5342b15cb3dSCy Schubert 535ea906c41SOllivier Robert default: /* legacy bancomm card */ 536c0b746e5SOllivier Robert 537ea906c41SOllivier Robert if (ioctl(fd_vme, READTIME, &vts)) { 5382b15cb3dSCy Schubert msyslog(LOG_ERR, 5392b15cb3dSCy Schubert "get_datumtime error: %m"); 540ea906c41SOllivier Robert return(NULL); 541ea906c41SOllivier Robert } 542c0b746e5SOllivier Robert /* Get day */ 5432b15cb3dSCy Schubert snprintf(cbuf, sizeof(cbuf), "%3.3x", 5442b15cb3dSCy Schubert ((vts.btfp_time[ 0 ] & 0x000f) << 8) + 545c0b746e5SOllivier Robert ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 546c0b746e5SOllivier Robert time_vme->day = (unsigned short)atoi(cbuf); 547c0b746e5SOllivier Robert 548c0b746e5SOllivier Robert /* Get hour */ 5492b15cb3dSCy Schubert snprintf(cbuf, sizeof(cbuf), "%2.2x", 5502b15cb3dSCy Schubert vts.btfp_time[ 1 ] & 0x00ff); 551c0b746e5SOllivier Robert time_vme->hr = (unsigned short)atoi(cbuf); 552c0b746e5SOllivier Robert 553c0b746e5SOllivier Robert /* Get minutes */ 5542b15cb3dSCy Schubert snprintf(cbuf, sizeof(cbuf), "%2.2x", 5552b15cb3dSCy Schubert (vts.btfp_time[ 2 ] & 0xff00) >> 8); 556c0b746e5SOllivier Robert time_vme->mn = (unsigned short)atoi(cbuf); 557c0b746e5SOllivier Robert 558c0b746e5SOllivier Robert /* Get seconds */ 5592b15cb3dSCy Schubert snprintf(cbuf, sizeof(cbuf), "%2.2x", 5602b15cb3dSCy Schubert vts.btfp_time[ 2 ] & 0x00ff); 561c0b746e5SOllivier Robert time_vme->sec = (unsigned short)atoi(cbuf); 562c0b746e5SOllivier Robert 563ea906c41SOllivier Robert /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so 564ea906c41SOllivier Robert we can use the TVTOTSF function later on...*/ 565c0b746e5SOllivier Robert 5662b15cb3dSCy Schubert snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x", 5672b15cb3dSCy Schubert vts.btfp_time[ 3 ], 568c0b746e5SOllivier Robert vts.btfp_time[ 4 ] >> 8); 569c0b746e5SOllivier Robert time_vme->frac = (u_long) atoi(cbuf); 570c0b746e5SOllivier Robert 571c0b746e5SOllivier Robert /* Get status bit */ 572ea906c41SOllivier Robert time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4; 573ea906c41SOllivier Robert 574ea906c41SOllivier Robert break; 575c0b746e5SOllivier Robert } 576ea906c41SOllivier Robert 577ea906c41SOllivier Robert if (time_vme->status) 578ea906c41SOllivier Robert return ((void *)NULL); 579c0b746e5SOllivier Robert else 580c0b746e5SOllivier Robert return (time_vme); 581c0b746e5SOllivier Robert } 582ea906c41SOllivier Robert /* Assign values to time_vme struct. Mostly for readability */ 583ea906c41SOllivier Robert void 584ea906c41SOllivier Robert tvme_fill(struct vmedate *time_vme, uint32_t btm[2]) 585ea906c41SOllivier Robert { 586ea906c41SOllivier Robert struct tm maj; 587052d159aSCy Schubert time_t dmaj; 588052d159aSCy Schubert uint32_t dmin; 589ea906c41SOllivier Robert 590052d159aSCy Schubert dmaj = btm[1]; /* syntax sugar & expansion */ 591052d159aSCy Schubert dmin = btm[0]; /* just syntax sugar */ 592ea906c41SOllivier Robert 593ea906c41SOllivier Robert gmtime_r(&dmaj, &maj); 594ea906c41SOllivier Robert time_vme->day = maj.tm_yday+1; 595ea906c41SOllivier Robert time_vme->hr = maj.tm_hour; 596ea906c41SOllivier Robert time_vme->mn = maj.tm_min; 597ea906c41SOllivier Robert time_vme->sec = maj.tm_sec; 598ea906c41SOllivier Robert time_vme->frac = (dmin & 0x000fffff) * 1000; 599ea906c41SOllivier Robert time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100; 600ea906c41SOllivier Robert time_vme->status = (dmin & 0x01000000) >> 24; 601ea906c41SOllivier Robert return; 602ea906c41SOllivier Robert } 603c0b746e5SOllivier Robert 6042b15cb3dSCy Schubert 6052b15cb3dSCy Schubert /* Assign values to time_vme struct. Mostly for readability */ 6062b15cb3dSCy Schubert void 6072b15cb3dSCy Schubert stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp) 6082b15cb3dSCy Schubert { 6092b15cb3dSCy Schubert 6102b15cb3dSCy Schubert time_vme->day = stfp->tm.tm_yday+1; 6112b15cb3dSCy Schubert time_vme->hr = stfp->tm.tm_hour; 6122b15cb3dSCy Schubert time_vme->mn = stfp->tm.tm_min; 6132b15cb3dSCy Schubert time_vme->sec = stfp->tm.tm_sec; 6142b15cb3dSCy Schubert time_vme->frac = stfp->usec*1000; 6152b15cb3dSCy Schubert time_vme->frac += stfp->hnsec * 100; 6162b15cb3dSCy Schubert time_vme->status = stfp->status; 6172b15cb3dSCy Schubert return; 6182b15cb3dSCy Schubert } 619c0b746e5SOllivier Robert #else 620*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT 621c0b746e5SOllivier Robert #endif /* REFCLOCK */ 622