ntp: keep the time from an NTP server

The switch has no battery backed clock, only a seconds counter that starts
at zero on every boot. An SNTP client keeps UTC as the time of the last
answer plus the seconds counted since: "ntp server <name|ip>", "ntp interval
<minutes>", "ntp timezone <+hh:mm>", "ntp dst off|eu|us", "ntp on|off",
"ntp" and "time". A server given by name goes through the DNS resolver, an
answer from an unsynchronised server is ignored, and a failed attempt is
repeated after 30 seconds.

The local time adds the time zone offset and, while the European or United
States rule says so, one hour. The date is split with a shift and subtract
division and a walk over the years, so no 32-bit division from the library
ends up in the common area.

The System page gets the NTP settings and shows the local time.
This commit is contained in:
DrDoof committed 2026-09-29 22:40:59 +02:00
1 parent 67b8c718fb
commit 3cd6d60e0b
11 files changed
+618 -8

No files matched your search

+2
View File
@@ -71,6 +71,8 @@ SRCS = \
sfp.c \
dns.c \
dns_cli.c \
ntp.c \
ntp_cli.c \
syslog.c \
udp_apps.c
+3 -5
View File
@@ -20,6 +20,7 @@
#include "dhcp.h"
#include "syslog.h"
#include "dns.h"
#include "ntp.h"
#include "uip/uip.h"
#include "version.h"
@@ -1805,6 +1806,8 @@ void cmd_parser(void) __banked
parse_syslog();
} else if (cmd_compare(0, "dns")) {
dns_parse();
} else if (cmd_compare(0, "ntp") || cmd_compare(0, "time")) {
ntp_parse();
} else if (cmd_compare(0, "ip")) {
if (cmd_compare(1, "dhcp")) {
dhcp_start();
@@ -1951,11 +1954,6 @@ void cmd_parser(void) __banked
parse_temp();
} else if (cmd_compare(0, "version")) {
print_sw_version();
} else if (cmd_compare(0, "time")) {
print_string(" Tick counter: "); print_long(ticks); print_string(" Sec Counter: ");
reg_read_m(RTL837X_REG_SEC_COUNTER);
print_sfr_data();
write_char('\n');
} else if (cmd_compare(0, "history")) {
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
__xdata uint8_t found_begin = 0;
+33
View File
@@ -0,0 +1,33 @@
# Time
The switch has no battery backed clock. It counts seconds from power on, and
it can take the date and time from an NTP server once the network is up. The
time is lost at every restart and set again at the first synchronisation.
```
ntp server <name|ip> # default pool.ntp.org
ntp interval <1-1440> # minutes between two synchronisations, default 60
ntp timezone <+hh:mm> # offset from UTC, -12:00 to +14:00
ntp dst off|eu|us # daylight saving rule
ntp on
ntp off
ntp # settings and time
time # local date and time
```
The client speaks SNTP version 4 and synchronises at once when turned on,
then after every interval. A server given by name is resolved through the DNS
resolver before every synchronisation. An answer from an unsynchronised
server is ignored, and a failed attempt is repeated after 30 seconds.
The local time is UTC plus the time zone offset, plus one hour while daylight
saving time applies:
* `eu`: from 01:00 UTC on the last Sunday of March to 01:00 UTC on the last
Sunday of October;
* `us`: from 02:00 local time on the second Sunday of March to 02:00 local
time on the first Sunday of November.
The same settings, with the current local time and the last synchronisation,
are in the Time card of the System page. Settings made there or on the
console are kept only once the configuration is saved.
+59 -3
View File
@@ -83,7 +83,14 @@ sy_ip_err:"Invalid IP / netmask / gateway",sy_host_err:"Hostname: 1-23 printable
sy_net_q:"Apply network settings?",sy_net_d:"The management IP changes to {ip}: this page will need to be reopened there.",
sy_ip_changed:"IP changed, reconnect at http://{ip}/",sy_dhcp_q:"Switch to DHCP?",
sy_dhcp_d:"The switch requests an address via DHCP. You must find its new IP to reconnect.",
sy_sysip_err:"Invalid syslog server IP",sy_dns1:"DNS server",sy_dns2:"Second DNS",sy_dns_err:"Invalid DNS server IP",sy_dns_dhcp:"from DHCP: {ip}",sy_sysport_err:"Syslog port must be 1-65535",
sy_sysip_err:"Invalid syslog server IP",sy_dns1:"DNS server",sy_dns2:"Second DNS",sy_dns_err:"Invalid DNS server IP",sy_dns_dhcp:"from DHCP: {ip}",
sy_time:"Time",sy_ntp:"NTP",sy_ntpsrv:"NTP server",sy_ntpsrv_p:"host name or IPv4 address",sy_ntpint:"Poll interval [min]",
sy_tz:"Time zone",sy_dst:"Daylight saving",sy_dst_eu:"Europe",
sy_dst_us:"United States",sy_now:"Local time",
sy_sync:"Last synchronisation",sy_ntpaddr:"Server address",sy_nosync:"not synchronised",sy_waiting:"waiting for the server",
sy_ago:"{n} min ago",sy_ntp_err:"Enter the host name or IPv4 address of the NTP server",sy_ntpint_err:"Poll interval must be 1-1440",
sy_time_note:"The switch has no battery backed clock: the time is lost at every restart and set again from the NTP server once the network is up. Save to flash to keep the settings.",
sy_sysport_err:"Syslog port must be 1-65535",
sy_pw_len:"Password: 1-20 characters",sy_pw_space:"Password cannot contain spaces",sy_pw_match:"Passwords do not match",
sy_pw_q:"Change admin password?",sy_pw_d:"Takes effect immediately for new logins. Save to flash to persist.",
sy_reboot_q:"Reboot the switch?",sy_reboot_d:"There are UNSAVED changes and they will be lost. Save to flash first if you want to keep them.",
@@ -659,6 +666,8 @@ var CONF_CMDS=[
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/,
/^session\s+\d{1,5}$/,
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
/^ntp\s+(on|off)$/,/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,/^ntp\s+interval\s+\d{1,4}$/,
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,/^ntp\s+dst\s+(off|eu|us)$/,
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/,
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
@@ -1871,11 +1880,58 @@ $("cfgreload").addEventListener("click",cfgReload);
$("cfgwrite").addEventListener("click",function(){
writeConfig($("cfgedit").value,t("cw_title"));
});
tabHooks.system={enter:sysLoad};
var ntpBase=null,ntpTimer=null;
function tzText(m){var a=Math.abs(m);return(m<0?"-":"+")+String(Math.floor(a/60)).padStart(2,"0")+":"+String(a%60).padStart(2,"0")}
function ntpTick(){
if(!ntpBase)return;
var d=new Date(ntpBase.ms+(Date.now()-ntpBase.at));
function p(n){return String(n).padStart(2,"0")}
$("sy-sync").textContent=t("sy_ago",{n:ntpBase.sync+Math.floor((Date.now()-ntpBase.at)/60000)});
$("sy-now").textContent=d.getUTCFullYear()+"-"+p(d.getUTCMonth()+1)+"-"+p(d.getUTCDate())+" "
+p(d.getUTCHours())+":"+p(d.getUTCMinutes())+":"+p(d.getUTCSeconds())+" UTC"+ntpBase.tz;
}
function ntpLoad(){
return api("/cmd",{method:"POST",body:"ntp"}).then(function(r){
var m=r.body.match(/NTP (on|off), server (\S*), address ([\d.]+), interval (\d+) min, timezone ([+-]\d\d:\d\d), dst (\w+)/);
if(!m)return;
var x=r.body.match(/Time (\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d) UTC([+-]\d\d:\d\d)( DST)?, synchronised (\d+) min ago/);
$("sy-ntpaddr").textContent=m[3]==="0.0.0.0"?"-":m[3];
if(x){
ntpBase={at:Date.now(),ms:Date.UTC(+x[1],+x[2]-1,+x[3],+x[4],+x[5],+x[6]),tz:x[7]+(x[8]?" DST":""),sync:+x[9]};
ntpTick();
}else{
ntpBase=null;
$("sy-now").textContent=t("sy_nosync");
$("sy-sync").textContent=m[1]==="on"?t("sy_waiting"):"-";
}
if($("sy-ntpcard").dataset.dirty)return;
$("sy-ntp").checked=m[1]==="on";$("sy-ntpsrv").value=m[2];$("sy-ntpint").value=m[4];
$("sy-tz").value=m[5];$("sy-dst").value=m[6];
}).catch(function(){});
}
[-720,-660,-600,-570,-540,-480,-420,-360,-300,-240,-210,-180,-120,-60,0,60,120,180,210,240,270,300,330,345,
360,390,420,480,525,540,570,600,630,660,720,765,780,840].forEach(function(z){
$("sy-tz").appendChild(h("option",{value:tzText(z),text:"UTC"+tzText(z)}));
});
["change","input"].forEach(function(ev){$("sy-ntpcard").addEventListener(ev,function(){this.dataset.dirty="1"})});
$("sy-ntpapply").addEventListener("click",function(){
var srv=$("sy-ntpsrv").value.trim(),iv=$("sy-ntpint").value.trim();
if(!/^[A-Za-z0-9.-]{1,47}$/.test(srv)){toast(t("sy_ntp_err"),"err");return;}
if(!(+iv>=1&&+iv<=1440)){toast(t("sy_ntpint_err"),"err");return;}
delete $("sy-ntpcard").dataset.dirty;
postCmds(["ntp server "+srv,"ntp interval "+iv,"ntp timezone "+$("sy-tz").value,"ntp dst "+$("sy-dst").value,
"ntp "+($("sy-ntp").checked?"on":"off")]).then(ntpLoad).catch(function(){});
});
$("sy-ntprefresh").addEventListener("click",ntpLoad);
tabHooks.system={
enter:function(){sysLoad();ntpLoad();clearInterval(ntpTimer);ntpTimer=setInterval(ntpTick,1000)},
leave:function(){clearInterval(ntpTimer)},
};
var CONF_OVERWRITE=[
/^ip\b/,/^gw\b/,/^netmask\b/,/^hostname\b/,
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^dns\s+server\b/,/^passwd\b/,/^session\b/,
/^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/,
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
/^pvid\s+\d{1,2}\b/,
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
@@ -1885,7 +1941,7 @@ var CONF_OVERWRITE=[
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
/^igmp\b/,/^mtu\s+\d{1,2}\b/,
];
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
function mergeConf(base,texts){
var conf=base.slice();
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
+16
View File
@@ -382,6 +382,22 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<input class="in" id="sy-sesstmo" style="width:80px" type="number" min="1" max="65535"></label>
<p class="small mut" data-i18n="sy_services_note">Service state reflects the startup config; runtime state is not readable.</p>
</div>
<div class="card" id="sy-ntpcard"><h2 data-i18n="sy_time">Time</h2>
<label class="row" style="justify-content:space-between"><span data-i18n="sy_ntp">NTP</span>
<span class="switch"><input type="checkbox" id="sy-ntp"><i></i></span></label>
<label class="row"><span class="lb" data-i18n="sy_ntpsrv">NTP server</span><input class="in" id="sy-ntpsrv" maxlength="47" data-i18n-p="sy_ntpsrv_p"></label>
<label class="row"><span class="lb" data-i18n="sy_ntpint">Poll interval [min]</span><input class="in sm" id="sy-ntpint" type="number" min="1" max="1440"></label>
<label class="row"><span class="lb" data-i18n="sy_tz">Time zone</span><select class="in" id="sy-tz"></select></label>
<label class="row"><span class="lb" data-i18n="sy_dst">Daylight saving</span><select class="in" id="sy-dst"><option value="off" data-i18n="c_offc">Off</option><option value="eu" data-i18n="sy_dst_eu">Europe</option><option value="us" data-i18n="sy_dst_us">United States</option></select></label>
<div style="display:flex;gap:10px;margin-top:8px">
<button class="ctl pri" id="sy-ntpapply" data-i18n="c_apply">Apply</button>
<button class="ctl" id="sy-ntprefresh" data-i18n="c_refresh">Refresh</button>
</div>
<label class="row" style="margin-top:10px"><span class="lb" data-i18n="sy_now">Local time</span><span class="mono" id="sy-now">-</span></label>
<label class="row"><span class="lb" data-i18n="sy_sync">Last synchronisation</span><span id="sy-sync">-</span></label>
<label class="row"><span class="lb" data-i18n="sy_ntpaddr">Server address</span><span class="mono" id="sy-ntpaddr">-</span></label>
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
</div>
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
+341
View File
@@ -0,0 +1,341 @@
/*
* SNTP client and local time for the RTL837x platform
* This code is in the Public Domain
*/
#include <stdint.h>
#include "rtl837x_common.h"
#include "rtl837x_regs.h"
#include "cmd_parser.h"
#include "uip/uip.h"
#include "dns.h"
#include "ntp.h"
#pragma codeseg BANK3
#pragma constseg BANK3
#define NTP_PORT 123
#define NTP_TRIES 3
#define NTP_TIMEOUT (3 * SYS_TICK_HZ)
#define NTP_RETRY 30
#define NTP_UNIX_EPOCH 2208988800UL
#define NTP_IDLE 0
#define NTP_RESOLVE 1
#define NTP_SEND 2
#define NTP_WAIT 3
extern __xdata uint8_t sfr_data[4];
extern volatile __xdata uint32_t ticks;
__xdata struct ntp_state ntp_state;
__xdata uip_ipaddr_t ntp_ip;
__xdata uint32_t ntp_now;
__xdata uint32_t ntp_t;
__xdata uint16_t ntp_year;
__xdata uint8_t ntp_mon, ntp_mday, ntp_hour, ntp_min, ntp_sec, ntp_wday, ntp_mlen;
/* days in each month of a common year */
static __code const uint8_t ntp_mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static __code const char ntp_default_server[] = "pool.ntp.org";
static uint32_t ntp_uptime(void) __reentrant
{
reg_read_m(RTL837X_REG_SEC_COUNTER);
((__xdata uint8_t *)&ntp_now)[0] = sfr_data[3];
((__xdata uint8_t *)&ntp_now)[1] = sfr_data[2];
((__xdata uint8_t *)&ntp_now)[2] = sfr_data[1];
((__xdata uint8_t *)&ntp_now)[3] = sfr_data[0];
return ntp_now;
}
static uint8_t ntp_divmod(uint8_t d) __reentrant
{
uint8_t i, r = 0;
for (i = 0; i < 32; i++) {
r = (r << 1) | (((uint8_t *)&ntp_t)[3] >> 7);
ntp_t <<= 1;
if (r >= d) {
r -= d;
ntp_t |= 1;
}
}
return r;
}
static uint8_t ntp_leap(uint16_t y) __reentrant
{
return !(y % 4) && ((y % 100) || !(y % 400));
}
static void ntp_split(uint32_t t) __reentrant
{
uint16_t days;
ntp_t = t;
ntp_sec = ntp_divmod(60);
ntp_min = ntp_divmod(60);
ntp_hour = ntp_divmod(24);
days = ntp_t;
ntp_wday = (days + 4) % 7;
for (ntp_year = 1970; days >= 365 + ntp_leap(ntp_year); ntp_year++)
days -= 365 + ntp_leap(ntp_year);
for (ntp_mon = 1; ; ntp_mon++) {
ntp_mlen = ntp_mdays[ntp_mon - 1] + (ntp_mon == 2 && ntp_leap(ntp_year));
if (days < ntp_mlen)
break;
days -= ntp_mlen;
}
ntp_mday = days + 1;
}
static uint8_t ntp_sunday(uint8_t n) __reentrant
{
uint8_t first = (uint16_t)(ntp_wday + 36 - ntp_mday) % 7U;
first = 1 + (uint16_t)(7 - first) % 7U;
if (n)
return first + 7 * (n - 1);
return first + 7 * ((uint16_t)(ntp_mlen - first) / 7U);
}
static uint8_t ntp_in_dst(uint8_t sm, uint8_t sn, uint8_t sh, uint8_t em, uint8_t en, uint8_t eh) __reentrant
{
uint8_t day;
if (ntp_mon < sm || ntp_mon > em)
return 0;
if (ntp_mon > sm && ntp_mon < em)
return 1;
if (ntp_mon == sm) {
day = ntp_sunday(sn);
return ntp_mday > day || (ntp_mday == day && ntp_hour >= sh);
}
day = ntp_sunday(en);
return ntp_mday < day || (ntp_mday == day && ntp_hour < eh);
}
static void ntp_print2(uint8_t v) __reentrant
{
write_char('0' + v / 10);
write_char('0' + v % 10);
}
static void ntp_print_offset(int16_t off) __reentrant
{
uint16_t a = off < 0 ? -off : off;
write_char(off < 0 ? '-' : '+');
ntp_print2(a / 60);
write_char(':');
ntp_print2(a % 60);
}
static void ntp_connect(__xdata uint8_t *a) __reentrant
{
if (ntp_state.conn)
uip_udp_remove(ntp_state.conn);
uip_ipaddr(ntp_ip, a[0], a[1], a[2], a[3]);
ntp_state.conn = uip_udp_new(&ntp_ip, HTONS(NTP_PORT));
if (!ntp_state.conn)
print_string("NTP: no free UDP connection\n");
}
static void ntp_retry(void) __reentrant
{
ntp_state.phase = NTP_IDLE;
ntp_state.next = ntp_uptime() + NTP_RETRY;
}
static void ntp_request(void) __reentrant
{
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
uint8_t i;
for (i = 1; i < 48; i++)
p[i] = 0;
p[0] = 0x23;
uip_udp_send(48);
}
static void ntp_receive(void) __reentrant
{
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
if (ntp_state.phase != NTP_WAIT || uip_datalen() < 48)
return;
if ((p[0] & 7) != 4 || (p[0] & 0xc0) == 0xc0 || !p[1] || p[1] > 15) {
ntp_retry();
return;
}
((__xdata uint8_t *)&ntp_state.utc)[0] = p[43];
((__xdata uint8_t *)&ntp_state.utc)[1] = p[42];
((__xdata uint8_t *)&ntp_state.utc)[2] = p[41];
((__xdata uint8_t *)&ntp_state.utc)[3] = p[40];
ntp_state.utc -= NTP_UNIX_EPOCH;
ntp_state.at = ntp_uptime();
ntp_state.stratum = p[1];
if (!ntp_state.synced) {
ntp_state.synced = 1;
print_string("NTP: time set from ");
print_ip(ntp_state.addr);
write_char('\n');
}
ntp_state.phase = NTP_IDLE;
ntp_state.next = ntp_state.at + (uint32_t)ntp_state.interval * 60;
}
void ntp_init(void) __banked
{
for (ntp_mon = 0; ntp_mon < sizeof(ntp_default_server); ntp_mon++)
ntp_state.server[ntp_mon] = ntp_default_server[ntp_mon];
ntp_state.enabled = 0;
ntp_state.interval = 60;
ntp_state.offset = 0;
ntp_state.dst = NTP_DST_OFF;
ntp_state.phase = NTP_IDLE;
ntp_state.synced = 0;
ntp_state.conn = 0;
}
void ntp_start(void) __banked
{
ntp_state.enabled = 1;
ntp_state.phase = NTP_IDLE;
ntp_state.next = 0;
ntp_state.addr[0] = ntp_state.addr[1] = ntp_state.addr[2] = ntp_state.addr[3] = 0;
ntp_connect(ntp_state.addr);
}
void ntp_stop(void) __banked
{
ntp_state.enabled = 0;
if (ntp_state.conn) {
uip_udp_remove(ntp_state.conn);
ntp_state.conn = 0;
}
}
void ntp_show_time(void) __banked __reentrant
{
int16_t off = ntp_state.offset;
uint8_t dst = 0;
uint32_t t;
if (!ntp_state.synced) {
print_string("Time not synchronised\n");
return;
}
t = ntp_state.utc + (ntp_uptime() - ntp_state.at);
if (ntp_state.dst == NTP_DST_EU) {
ntp_split(t);
dst = ntp_in_dst(3, 0, 1, 10, 0, 1);
} else if (ntp_state.dst == NTP_DST_US) {
ntp_split(t + (int32_t)off * 60);
dst = ntp_in_dst(3, 2, 2, 11, 1, 1);
}
if (dst)
off += 60;
ntp_split(t + (int32_t)off * 60);
print_string("Time ");
itoa_short(ntp_year);
write_char('-');
ntp_print2(ntp_mon);
write_char('-');
ntp_print2(ntp_mday);
write_char(' ');
ntp_print2(ntp_hour);
write_char(':');
ntp_print2(ntp_min);
write_char(':');
ntp_print2(ntp_sec);
print_string(" UTC");
ntp_print_offset(off);
print_string(dst ? " DST, synchronised " : ", synchronised ");
ntp_t = ntp_uptime() - ntp_state.at;
ntp_divmod(60);
itoa_short(ntp_t);
print_string(" min ago, stratum ");
itoa_short(ntp_state.stratum);
write_char('\n');
}
void ntp_show(void) __banked __reentrant
{
int16_t off = ntp_state.offset;
print_string(ntp_state.enabled ? "NTP on, server " : "NTP off, server ");
print_string_x(ntp_state.server);
print_string(", address ");
print_ip(ntp_state.addr);
print_string(", interval ");
itoa_short(ntp_state.interval);
print_string(" min, timezone ");
ntp_print_offset(off);
print_string(ntp_state.dst == NTP_DST_EU ? ", dst eu\n" : ntp_state.dst == NTP_DST_US ? ", dst us\n" : ", dst off\n");
ntp_show_time();
}
void ntp_callback(uint16_t lport) __banked __reentrant
{
if (!ntp_state.conn || lport != ntp_state.conn->lport)
return;
if (uip_newdata()) {
ntp_receive();
return;
}
switch (ntp_state.phase) {
case NTP_IDLE:
if (ntp_uptime() < ntp_state.next || dns_state.status == DNS_PENDING)
return;
memcpy(dns_state.name, ntp_state.server, DNS_NAME_LEN);
dns_state.verbose = 0;
dns_lookup();
ntp_state.phase = NTP_RESOLVE;
return;
case NTP_RESOLVE:
if (dns_state.status == DNS_PENDING)
return;
if (dns_state.status != DNS_DONE) {
ntp_retry();
return;
}
memcpy(ntp_state.addr, dns_state.addr, 4);
ntp_state.tries = 0;
ntp_state.phase = NTP_SEND;
ntp_connect(ntp_state.addr);
return;
case NTP_SEND:
ntp_state.sent = ticks;
ntp_state.phase = NTP_WAIT;
ntp_request();
return;
case NTP_WAIT:
if (ticks - ntp_state.sent < NTP_TIMEOUT)
return;
if (++ntp_state.tries < NTP_TRIES)
ntp_state.phase = NTP_SEND;
else
ntp_retry();
}
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef _NTP_H_
#define _NTP_H_
#include <stdint.h>
#include "dns.h"
#define NTP_DST_OFF 0
#define NTP_DST_EU 1
#define NTP_DST_US 2
struct uip_udp_conn;
struct ntp_state {
uint8_t enabled;
char server[DNS_NAME_LEN]; /* host name or dotted address */
uint8_t addr[4]; /* address of the server last used */
uint16_t interval; /* minutes between two synchronisations */
int16_t offset; /* time zone, minutes east of UTC */
uint8_t dst; /* NTP_DST_* */
uint8_t phase;
uint8_t tries;
uint8_t synced;
uint8_t stratum;
uint32_t utc; /* seconds since 1970 UTC at the last synchronisation */
uint32_t at; /* uptime seconds at the last synchronisation */
uint32_t next; /* uptime seconds of the next synchronisation */
uint32_t sent; /* ticks when the request went out */
struct uip_udp_conn *conn;
};
extern __xdata struct ntp_state ntp_state;
void ntp_init(void) __banked;
void ntp_start(void) __banked;
void ntp_stop(void) __banked;
void ntp_show(void) __banked __reentrant;
void ntp_show_time(void) __banked __reentrant;
void ntp_callback(uint16_t lport) __banked __reentrant;
void ntp_parse(void) __banked __reentrant;
#endif
+119
View File
@@ -0,0 +1,119 @@
/*
* Command line interface of the NTP client for the RTL837x platform
* This code is in the Public Domain
*/
#include <stdint.h>
#include "rtl837x_common.h"
#include "cmd_parser.h"
#include "ntp.h"
#pragma codeseg BANK2
#pragma constseg BANK2
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
extern __xdata uint8_t cmd_words_len;
extern __xdata uint8_t cmd_words_b[15];
extern __xdata uint16_t atoi_results_short;
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
uint8_t atoi_short(uint8_t idx);
static uint8_t ntp_cli_server(void) __reentrant
{
__xdata uint8_t *s = &cmd_buffer[cmd_words_b[2]];
uint8_t n;
for (n = 0; s[n] && s[n] != ' '; n++)
if (n == DNS_NAME_LEN - 1)
return 0;
memcpy(ntp_state.server, s, n);
ntp_state.server[n] = 0;
return n;
}
static uint8_t ntp_cli_timezone(void) __reentrant
{
__xdata uint8_t *s = &cmd_buffer[cmd_words_b[2]];
uint8_t neg = 0, h = 0, m = 0, n = 0;
uint16_t v;
if (*s == '+' || *s == '-')
neg = *s++ == '-';
while (*s >= '0' && *s <= '9') {
h = h * 10 + (*s++ - '0');
if (++n > 2)
return 0;
}
if (!n)
return 0;
if (*s == ':') {
s++;
if (s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9')
return 0;
m = (s[0] - '0') * 10 + s[1] - '0';
s += 2;
}
if ((*s && *s != ' ') || (m && m != 30 && m != 45))
return 0;
v = (uint16_t)h * 60 + m;
if (v > (neg ? 720 : 840))
return 0;
ntp_state.offset = neg ? -(int16_t)v : (int16_t)v;
return 1;
}
void ntp_parse(void) __banked __reentrant
{
if (cmd_compare(0, "time")) {
ntp_show_time();
return;
}
if (cmd_words_len < 2) {
ntp_show();
return;
}
if (cmd_compare(1, "on")) {
ntp_start();
return;
}
if (cmd_compare(1, "off")) {
ntp_stop();
return;
}
if (cmd_words_len < 3)
goto err;
if (cmd_compare(1, "server")) {
if (!ntp_cli_server())
goto err;
if (ntp_state.enabled)
ntp_start();
return;
}
if (cmd_compare(1, "interval")) {
if (!atoi_short(cmd_words_b[2]) || !atoi_results_short || atoi_results_short > 1440)
goto err;
ntp_state.interval = atoi_results_short;
return;
}
if (cmd_compare(1, "timezone")) {
if (!ntp_cli_timezone())
goto err;
return;
}
if (cmd_compare(1, "dst")) {
if (cmd_compare(2, "off"))
ntp_state.dst = NTP_DST_OFF;
else if (cmd_compare(2, "eu"))
ntp_state.dst = NTP_DST_EU;
else if (cmd_compare(2, "us"))
ntp_state.dst = NTP_DST_US;
else
goto err;
return;
}
err:
print_string("Error: ntp [on|off|server <name>|interval <1-1440>|timezone <+hh:mm>|dst off|eu|us]\n");
}
+2
View File
@@ -26,6 +26,7 @@
#include "phy.h"
#include "syslog.h"
#include "dns.h"
#include "ntp.h"
#include "httpd/page_impl.h"
#include "boot.h"
#include "sfp.h"
@@ -1758,6 +1759,7 @@ void main(void)
syslog_init();
dns_init();
ntp_init();
#ifdef DEBUG
// This register seems to work on the RTL8373 only if also the SDS
+1
View File
@@ -7,4 +7,5 @@ void udp_callbacks(void)
dhcp_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
syslog_callback(uip_udp_conn->lport); // let the application decide if this is for it or not
dns_callback(uip_udp_conn->lport);
ntp_callback(uip_udp_conn->lport);
}
+1
View File
@@ -4,6 +4,7 @@
#include "dhcp.h"
#include "syslog.h"
#include "dns.h"
#include "ntp.h"
void udp_callbacks(void);