summaryrefslogtreecommitdiffstats
path: root/lib/rtems_bsdnet_ntp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rtems_bsdnet_ntp.c')
-rw-r--r--lib/rtems_bsdnet_ntp.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/lib/rtems_bsdnet_ntp.c b/lib/rtems_bsdnet_ntp.c
new file mode 100644
index 0000000..e675759
--- /dev/null
+++ b/lib/rtems_bsdnet_ntp.c
@@ -0,0 +1,219 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * Synchronize with an NTP server
+ *
+ * This program may be distributed and used for any purpose.
+ * I ask only that you:
+ * 1. Leave this author information intact.
+ * 2. Document any changes you make.
+ *
+ * W. Eric Norum
+ * Canadian Light Source
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@cls.usask.ca
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h> /* close */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/error.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <rtems/bsdnet/servers.h>
+
+/*
+ * RTEMS base: 1988, January 1
+ * UNIX base: 1970, January 1
+ * NTP base: 1900, January 1
+ */
+#define UNIX_BASE_TO_NTP_BASE (uint32_t)(((70UL*365UL)+17UL) * (24UL*60UL*60UL))
+
+struct ntpPacket {
+ struct ntpPacketSmall ntp;
+ char authenticator[96];
+};
+
+static int
+processPacket (struct ntpPacketSmall *p, int state, void *unused)
+{
+ time_t tbuf;
+ struct tm *lt;
+ rtems_time_of_day rt;
+ rtems_interval ticks_per_second;
+
+ if ( state )
+ return 0;
+
+ ticks_per_second = rtems_clock_get_ticks_per_second();
+ tbuf = ntohl (p->transmit_timestamp.integer) - UNIX_BASE_TO_NTP_BASE - rtems_bsdnet_timeoffset;
+ lt = gmtime (&tbuf);
+ rt.year = lt->tm_year + 1900;
+ rt.month = lt->tm_mon + 1;
+ rt.day = lt->tm_mday;
+ rt.hour = lt->tm_hour;
+ rt.minute = lt->tm_min;
+ rt.second = lt->tm_sec;
+ rt.ticks = ntohl (p->transmit_timestamp.fraction) / (ULONG_MAX / ticks_per_second);
+ if (rt.ticks >= ticks_per_second)
+ rt.ticks = ticks_per_second - 1;
+ rtems_clock_set (&rt);
+ return 0;
+}
+
+static int
+getServerTimespec(struct ntpPacketSmall *p, int state, void *usr_data)
+{
+struct timespec *ts = usr_data;
+unsigned long long tmp;
+
+ if ( 0 == state ) {
+ ts->tv_sec = ntohl( p->transmit_timestamp.integer );
+ ts->tv_sec -= rtems_bsdnet_timeoffset + UNIX_BASE_TO_NTP_BASE;
+
+ tmp = 1000000000 * (unsigned long long)ntohl(p->transmit_timestamp.fraction);
+
+ ts->tv_nsec = (unsigned long) (tmp>>32);
+ }
+ return 0;
+}
+
+int rtems_bsdnet_ntp_retry_count = 5;
+int rtems_bsdnet_ntp_timeout_secs = 5;
+int rtems_bsdnet_ntp_bcast_timeout_secs = 80;
+
+static int
+tryServer (int i, int s, rtems_bsdnet_ntp_callback_t callback, void *usr_data)
+{
+ int l = 0;
+ struct timeval tv;
+ socklen_t farlen;
+ struct sockaddr_in farAddr;
+ struct ntpPacketSmall packet;
+
+ if (i < 0)
+ tv.tv_sec = rtems_bsdnet_ntp_bcast_timeout_secs;
+ else
+ tv.tv_sec = rtems_bsdnet_ntp_timeout_secs;
+ tv.tv_usec = 0;
+ if (setsockopt (s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv) < 0) {
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket receive timeout: %s\n", strerror (errno));
+ close (s);
+ return -1;
+ }
+ if (i >= 0) {
+ memset (&farAddr, 0, sizeof farAddr);
+ farAddr.sin_family = AF_INET;
+ farAddr.sin_port = htons (123);
+ farAddr.sin_addr = rtems_bsdnet_ntpserver[i];
+ memset (&packet, 0, sizeof packet);
+ packet.li_vn_mode = (3 << 3) | 3; /* NTP version 3, client */
+ if ( callback( &packet, 1, usr_data ) )
+ return -1;
+ l = sendto (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, sizeof farAddr);
+ if (l != sizeof packet) {
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't send: %s\n", strerror (errno));
+ return -1;
+ }
+ } else {
+ if ( callback( &packet, -1, usr_data ) )
+ return -1;
+ }
+ farlen = sizeof farAddr;
+ i = recvfrom (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, &farlen);
+ if (i == 0)
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Unexpected EOF");
+ if (i < 0) {
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+ return -1;
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't receive: %s\n", strerror (errno));
+ }
+
+ if ( i >= sizeof packet &&
+ (((packet.li_vn_mode & (0x7 << 3)) == (3 << 3)) ||
+ ((packet.li_vn_mode & (0x7 << 3)) == (4 << 3))) &&
+ ((packet.transmit_timestamp.integer != 0) || (packet.transmit_timestamp.fraction != 0)) &&
+ 0 == callback( &packet, 0 , usr_data) )
+ return 0;
+
+ return -1;
+}
+
+int rtems_bsdnet_get_ntp(int sock, rtems_bsdnet_ntp_callback_t callback, void *usr_data)
+{
+int s = -1;
+int i;
+int retry;
+struct sockaddr_in myAddr;
+int reuseFlag;
+int ret;
+
+ if ( !callback )
+ callback = getServerTimespec;
+
+ if ( sock < 0 ) {
+ s = socket (AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't create socket: %s\n", strerror (errno));
+ return -1;
+ }
+ reuseFlag = 1;
+ if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseFlag, sizeof reuseFlag) < 0) {
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket reuse: %s\n", strerror (errno));
+ close (s);
+ return -1;
+ }
+ memset (&myAddr, 0, sizeof myAddr);
+ myAddr.sin_family = AF_INET;
+ myAddr.sin_port = htons (123);
+ myAddr.sin_addr.s_addr = htonl (INADDR_ANY);
+ if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) {
+ fprintf (stderr, "rtems_bsdnet_get_ntp() Can't bind socket: %s\n", strerror (errno));
+ close (s);
+ return -1;
+ }
+ sock = s;
+ }
+ ret = -1;
+ for (retry = 0 ; (ret == -1) && (retry < rtems_bsdnet_ntp_retry_count) ; retry++) {
+ /*
+ * If there's no server we just have to wait
+ * and hope that there's an NTP broadcast
+ * server out there somewhere.
+ */
+ if (rtems_bsdnet_ntpserver_count <= 0) {
+ ret = tryServer (-1, sock, callback, usr_data);
+ }
+ else {
+ for (i = 0 ; (ret == -1) && (i < rtems_bsdnet_ntpserver_count) ; i++) {
+ ret = tryServer (i, sock, callback, usr_data);
+ }
+ }
+ }
+ if ( s >= 0 )
+ close (s);
+ return ret;
+}
+
+int
+rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority)
+{
+ if (interval != 0) {
+ fprintf (stderr, "Daemon-mode note yet supported.\n");
+ errno = EINVAL;
+ return -1;
+ }
+ return rtems_bsdnet_get_ntp( -1, processPacket, 0);
+}