diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2015-12-11 15:40:12 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-01-07 08:46:09 +0100 |
commit | f98b02365ff32646475b1c39e3affd9502d27280 (patch) | |
tree | 30e0656f324896068ab684b2702d068237f12ce5 /net/sntp.c | |
parent | e25911524369db851d3d2f1d2f048e6d85a221b6 (diff) | |
download | barebox-f98b02365ff32646475b1c39e3affd9502d27280.tar.gz barebox-f98b02365ff32646475b1c39e3affd9502d27280.tar.xz |
net: Add SNTP support
This adds support for retrieving the time via Simple Network Time
Protocol (SNTP). No fancy features are supported, only plainly getting
the current time.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'net/sntp.c')
-rw-r--r-- | net/sntp.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/net/sntp.c b/net/sntp.c new file mode 100644 index 0000000000..577c859616 --- /dev/null +++ b/net/sntp.c @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <common.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <asm-generic/div64.h> +#include <command.h> +#include <clock.h> +#include <net.h> +#include <sntp.h> +#include <errno.h> +#include <environment.h> +#include <linux/err.h> + +#define SNTP_PORT 123 +#define TIMEOUT 1 + +#define VERSION 4 /* version number */ + +#define M_RSVD 0 /* reserved */ +#define M_SACT 1 /* symmetric active */ +#define M_PASV 2 /* symmetric passive */ +#define M_CLNT 3 /* client */ +#define M_SERV 4 /* server */ +#define M_BCST 5 /* broadcast server */ +#define M_BCLN 6 /* broadcast client */ + +typedef uint64_t tstamp; /* NTP timestamp format */ +typedef uint32_t tdist; /* NTP short format */ + +struct ntp_packet { +#ifdef __LITTLE_ENDIAN /* reversed */ + unsigned int mode:3; /* mode */ + unsigned int version:3; /* version number */ + unsigned int leap:2; /* leap indicator */ +#else /* forward */ + unsigned int leap:2; /* leap indicator */ + unsigned int version:3; /* version number */ + unsigned int mode:3; /* mode */ +#endif + uint8_t stratum; /* stratum */ + uint8_t poll; /* poll interval */ + int8_t precision; /* precision */ + tdist rootdelay; /* root delay */ + tdist rootdisp; /* root dispersion */ + uint32_t refid; /* reference ID */ + tstamp reftime; /* reference time */ + tstamp org; /* origin timestamp */ + tstamp rec; /* receive timestamp */ + tstamp xmt; /* transmit timestamp */ +}; + +static IPaddr_t net_sntp_ip; + +#define SNTP_STATE_INIT 0 +#define SNTP_STATE_SUCCESS 1 + +static int sntp_state; + +static struct net_connection *sntp_con; + +static s64 curr_timestamp; + +static int sntp_send(void) +{ + struct ntp_packet *ntp = net_udp_get_payload(sntp_con); + + memset(ntp, 0, sizeof(struct ntp_packet)); + + ntp->version = VERSION; + ntp->mode = M_CLNT; + + return net_udp_send(sntp_con, sizeof(struct ntp_packet)); +} + +static void sntp_handler(void *ctx, char *pkt, unsigned len) +{ + IPaddr_t ip_addr; + struct iphdr *ip = net_eth_to_iphdr(pkt); + struct ntp_packet *ntp = + (struct ntp_packet *)net_eth_to_udp_payload(pkt); + + ip_addr = net_read_ip((void *)&ip->saddr); + if (ip_addr != net_sntp_ip) + return; + + len = net_eth_to_udplen(pkt); + if (len < sizeof(struct ntp_packet)) + return; + + pr_debug("received SNTP response\n"); + + if (ntp->version != VERSION) + return; + + if (ntp->mode != M_SERV) + return; + + curr_timestamp = (get_unaligned_be64(&ntp->xmt) >> 32) - 2208988800UL; + + sntp_state = SNTP_STATE_SUCCESS; +} + +s64 sntp(const char *server) +{ + int ret, repeat = 5; + u64 sntp_start; + + if (!server) + server = getenv("global.dhcp.ntpserver"); + if (!server) + return -EINVAL; + + net_sntp_ip = resolv(server); + if (!net_sntp_ip) { + printf("unknown host %s\n", server); + return 1; + } + + sntp_con = net_udp_new(net_sntp_ip, SNTP_PORT, sntp_handler, NULL); + if (IS_ERR(sntp_con)) { + ret = PTR_ERR(sntp_con); + goto out; + } + + sntp_start = get_time_ns(); + ret = sntp_send(); + if (ret) + goto out_unreg; + + sntp_state = SNTP_STATE_INIT; + + while (sntp_state == SNTP_STATE_INIT) { + if (ctrlc()) { + ret = -EINTR; + break; + } + + net_poll(); + + if (is_timeout(sntp_start, 1 * SECOND)) { + sntp_start = get_time_ns(); + ret = sntp_send(); + if (ret) + goto out_unreg; + repeat--; + if (!repeat) { + ret = -ETIMEDOUT; + goto out_unreg; + } + } + } + + net_unregister(sntp_con); + + return curr_timestamp; + +out_unreg: + net_unregister(sntp_con); +out: + return ret; +} |