Гугл сломался?
Вот моя реализация клиента SNTP, может быть что-то полезное почерпнёте:
CODE
/**
* @file sntp.c
* @brief SNTP client / clock
*/
#include "sntp.h"
#include "event.h"
#include "systime.h"
#include "mcf5223xif.h"
#include "lwip/udp.h"
#include "assert_static.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define SENDFAIL_TIMEOUT 5000 /* 5 seconds */
#define SENT_TIMEOUT 60000 /* 1 minute */
#define BADREPLY_TIMEOUT 60000 /* 1 minute */
#define VALID_TIMEOUT (8 * 3600000) /* 8 hours */
#define SECSPERMIN 60L
#define MINSPERHOUR 60L
#define HOURSPERDAY 24L
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
#define DAYSPERWEEK 7
#define MONSPERYEAR 12
#define YEAR_BASE 1900
#define EPOCH_WDAY 1
#define EPOCH_YEARS_SINCE_LEAP 2
#define EPOCH_YEARS_SINCE_CENTURY 70
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
enum sntp_state
{
SNTP_INIT, SNTP_SENDFAIL, SNTP_SENT, SNTP_BADREPLY, SNTP_VALID
};
struct sntp_packet
{
uint8_t status;
uint8_t stratum;
uint8_t ppoll;
uint8_t precision;
uint32_t distance;
uint32_t dispersion;
uint32_t refid;
uint64_t reftime;
uint64_t org;
uint64_t rec;
uint64_t xmt;
};
static bool time_valid = false;
static enum sntp_state state = SNTP_INIT;
static int32_t last_time, link_time;
static struct udp_pcb* upcb;
static struct ip_addr server;
static uint_fast16_t port = 123;
static uint64_t startup_time; /* timestamp in UTC */
static int_fast16_t tz;
static uint64_t
ms2ts(uint64_t ms)
{
return ((ms / 1000) << 32) | (((ms % 1000) / 4) << 24);
}
static void recv(void *arg,
struct udp_pcb *upcb,
struct pbuf *p,
struct ip_addr *addr,
u16_t port)
{
if (p->len == sizeof(struct sntp_packet))
{
int i;
struct sntp_packet *ptr;
assert(p->len == p->tot_len); /* don't accept chained pbuf */
ptr = p->payload;
i = (ptr->status >> 3) & 7;
if ((i < 1) || (i > 4)) /* SNTP version 1..4 */
{
goto out;
}
i = ptr->status & 7;
if ((i != 4) && (i != 5)) /* mode 4 or 5: server or broadcast */
{
goto out;
}
if (ptr->stratum == 0)
{
goto out;
}
if (ptr->xmt == 0)
{
goto out;
}
startup_time = ptr->xmt - ms2ts(systime_ms64());
if (!time_valid)
{
time_valid = true;
event_trigger(EVENT_SNTPSYNC);
}
state = SNTP_VALID;
last_time = systime_ms32();
}
out:
pbuf_free(p);
}
void
sntp_enable(bool enable)
{
assert_static(BYTE_ORDER == BIG_ENDIAN);
if (enable)
{
if (upcb == 0)
{
err_t ret;
upcb = udp_new();
assert(upcb != 0);
ret = udp_bind(upcb, IP_ADDR_ANY, port);
if (ret != ERR_OK)
{
udp_remove(upcb);
upcb = 0;
}
else
{
udp_recv(upcb, recv, 0);
}
state = SNTP_INIT;
}
}
else if (upcb != 0)
{
udp_remove(upcb);
upcb = 0;
}
}
bool
sntp_isenabled(void)
{
return upcb != 0;
}
void
sntp_setserveraddr(const struct ip_addr *addr)
{
ip_addr_copy(server, *addr);
}
void
sntp_getserveraddr(struct ip_addr *addr)
{
ip_addr_copy(*addr, server);
}
void
sntp_setserverport(uint_fast16_t port_arg)
{
port = port_arg;
if (sntp_isenabled())
{
sntp_enable(false);
sntp_enable(true);
}
}
uint_fast16_t
sntp_getserverport(void)
{
return port;
}
void
sntp_settz(int_fast16_t tz_arg)
{
tz = tz_arg;
}
int_fast16_t
sntp_gettz(void)
{
return tz;
}
static void
send_request(void)
{
struct sntp_packet packet;
struct pbuf* psend;
memset(&packet, 0, sizeof(packet));
packet.status = (3 << 3) /* SNTP vesion 3 */
| (3 << 0); /* Mode: client */
psend = pbuf_alloc(PBUF_RAW, sizeof(packet), PBUF_REF);
if (psend != 0)
{
psend->payload = &packet;
state = (udp_sendto(upcb, psend, &server, port) == ERR_OK)
? SNTP_SENT : SNTP_SENDFAIL;
pbuf_free(psend);
}
last_time = systime_ms32();
}
void
sntp_poll(void)
{
if (mcf5223xif_link() == false)
{
link_time = systime_ms32();
}
else if (upcb != 0)
{
int32_t now, timeout;
now = systime_ms32();
if (abs(now - link_time) > 2000)
{
switch (state)
{
case SNTP_INIT: send_request(); return;
case SNTP_SENDFAIL: timeout = SENDFAIL_TIMEOUT; break;
case SNTP_SENT: timeout = SENT_TIMEOUT; break;
case SNTP_BADREPLY: timeout = BADREPLY_TIMEOUT; break;
case SNTP_VALID: timeout = VALID_TIMEOUT; break;
}
if (now - last_time > timeout)
{
send_request();
}
}
}
}
bool
sntp_valid(void)
{
return time_valid;
}
uint32_t
sntp_timestamp(void)
{
return time_valid ? (startup_time + ms2ts(systime_ms64())) >> 32 : 0;
}
static const uint8_t mon_lengths[2][MONSPERYEAR] =
{
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
} ;
static const int year_lengths[2] = { 365, 366 } ;
void
sntp_seconds2tm(uint32_t sec32, struct tm* res)
{
long days, rem;
int y;
int yleap;
const uint8_t *ip;
uint64_t sec;
sec = sec32 + 60 * tz;
if (((sec32 & 0x80000000UL) == 0) && (sec32 != 0))
{
sec += ((uint64_t)1 << 32);
}
days = sec / SECSPERDAY;
rem = sec % SECSPERDAY;
/* compute hour, min, and sec */
res->tm_hour = (int) (rem / SECSPERHOUR);
rem %= SECSPERHOUR;
res->tm_min = (int) (rem / SECSPERMIN);
res->tm_sec = (int) (rem % SECSPERMIN);
/* compute day of week */
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
{
res->tm_wday += DAYSPERWEEK;
}
/* compute year & day of year */
y = YEAR_BASE;
if (days >= 40542) /* days in years 1900..2010 inclusive */
{
days -= 40542;
y += 111;
}
if (days >= 0)
{
for (;;)
{
yleap = isleap(y);
if (days < year_lengths[yleap])
{
break;
}
y++;
days -= year_lengths[yleap];
}
}
else
{
do
{
--y;
yleap = isleap(y);
days += year_lengths[yleap];
}
while (days < 0);
}
res->tm_year = y - YEAR_BASE;
res->tm_yday = days;
ip = mon_lengths[yleap];
for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
{
days -= ip[res->tm_mon];
}
res->tm_mday = days + 1;
}
void
sntp_gettimestr(uint32_t timestamp, char *str)
{
if (timestamp != 0)
{
struct tm time;
sntp_seconds2tm(timestamp, &time);
sprintf(str, "%02d.%02d.%d %02d:%02d:%02d",
time.tm_mday,
time.tm_mon + 1,
time.tm_year + 1900,
time.tm_hour,
time.tm_min,
time.tm_sec);
}
else
{
strcpy(str, "00.00.0000 00:00:00");
}
}