diff --git a/Makefile b/Makefile index d7fbf131..cf8f7a9a 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,8 @@ SRCS = \ sfp.c \ dns.c \ dns_cli.c \ + ntp.c \ + ntp_cli.c \ syslog.c \ udp_apps.c diff --git a/cmd_parser.c b/cmd_parser.c index 93a5af7c..8aeba4f2 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -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; diff --git a/doc/ntp.md b/doc/ntp.md new file mode 100644 index 00000000..129a2230 --- /dev/null +++ b/doc/ntp.md @@ -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 # 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. diff --git a/html/app.js b/html/app.js index da8374c1..9cb06558 100644 --- a/html/app.js +++ b/html/app.js @@ -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)})} diff --git a/html/index.html b/html/index.html index 0afb30bc..05a73509 100644 --- a/html/index.html +++ b/html/index.html @@ -382,6 +382,22 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}

Service state reflects the startup config; runtime state is not readable.

+

Time

+ + + + + +
+ + +
+ + + +

The switch has no battery backed clock.

+

Admin password

diff --git a/ntp.c b/ntp.c new file mode 100644 index 00000000..9a61bb73 --- /dev/null +++ b/ntp.c @@ -0,0 +1,341 @@ +/* + * SNTP client and local time for the RTL837x platform + * This code is in the Public Domain + */ + +#include +#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(); + } +} diff --git a/ntp.h b/ntp.h new file mode 100644 index 00000000..f99af83e --- /dev/null +++ b/ntp.h @@ -0,0 +1,41 @@ +#ifndef _NTP_H_ +#define _NTP_H_ + +#include +#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 diff --git a/ntp_cli.c b/ntp_cli.c new file mode 100644 index 00000000..e3840c46 --- /dev/null +++ b/ntp_cli.c @@ -0,0 +1,119 @@ +/* + * Command line interface of the NTP client for the RTL837x platform + * This code is in the Public Domain + */ + +#include +#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 |interval <1-1440>|timezone <+hh:mm>|dst off|eu|us]\n"); +} diff --git a/rtlplayground.c b/rtlplayground.c index 604abfae..825175c9 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -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 diff --git a/udp_apps.c b/udp_apps.c index 4292a105..a5304c6f 100644 --- a/udp_apps.c +++ b/udp_apps.c @@ -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); } \ No newline at end of file diff --git a/udp_apps.h b/udp_apps.h index de9d6a18..6908fafa 100644 --- a/udp_apps.h +++ b/udp_apps.h @@ -4,6 +4,7 @@ #include "dhcp.h" #include "syslog.h" #include "dns.h" +#include "ntp.h" void udp_callbacks(void);