1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (C) 2025 Linaro Ltd. */
3 
4 #include <command.h>
5 #include <console.h>
6 #include <dm/device.h>
7 #include <env.h>
8 #include <lwip/apps/sntp.h>
9 #include <lwip/timeouts.h>
10 #include <net.h>
11 
12 U_BOOT_CMD(sntp, 2, 1, do_sntp, "synchronize RTC via network",
13 	   "[NTPServerNameOrIp]");
14 
15 #define SNTP_TIMEOUT 10000
16 
17 static enum done_state {
18 	NOT_DONE = 0,
19 	SUCCESS,
20 	ABORTED,
21 	TIMED_OUT
22 } sntp_state;
23 
no_response(void * arg)24 static void no_response(void *arg)
25 {
26 	sntp_state = TIMED_OUT;
27 }
28 
29 /* Called by lwIP via the SNTP_SET_SYSTEM_TIME() macro */
sntp_set_system_time(uint32_t seconds)30 void sntp_set_system_time(uint32_t seconds)
31 {
32 	char *toff;
33 	int net_ntp_time_offset = 0;
34 
35 	toff = env_get("timeoffset");
36 	if (toff)
37 		net_ntp_time_offset = simple_strtol(toff, NULL, 10);
38 
39 	net_sntp_set_rtc(seconds + net_ntp_time_offset);
40 	sntp_state = SUCCESS;
41 }
42 
ntp_server_known(void)43 static bool ntp_server_known(void)
44 {
45 	int i;
46 
47 	for (i = 0; i < SNTP_MAX_SERVERS; i++) {
48 		const ip_addr_t *ip = sntp_getserver(i);
49 
50 		if (ip && ip->addr)
51 			return true;
52 	}
53 
54 	return false;
55 }
56 
sntp_loop(struct udevice * udev,ip_addr_t * srvip)57 static int sntp_loop(struct udevice *udev, ip_addr_t *srvip)
58 {
59 	struct netif *netif;
60 
61 	netif = net_lwip_new_netif(udev);
62 	if (!netif)
63 		return -1;
64 
65 	sntp_state = NOT_DONE;
66 
67 	sntp_setoperatingmode(SNTP_OPMODE_POLL);
68 	sntp_servermode_dhcp(CONFIG_IS_ENABLED(CMD_DHCP));
69 	if (srvip) {
70 		sntp_setserver(0, srvip);
71 	} else {
72 		if (!ntp_server_known()) {
73 			log_err("error: ntpserverip not set\n");
74 			return -1;
75 		}
76 	}
77 	sntp_init();
78 
79 	sys_timeout(SNTP_TIMEOUT, no_response, NULL);
80 	while (sntp_state == NOT_DONE) {
81 		net_lwip_rx(udev, netif);
82 		if (ctrlc()) {
83 			printf("\nAbort\n");
84 			sntp_state = ABORTED;
85 			break;
86 		}
87 	}
88 	sys_untimeout(no_response, NULL);
89 
90 	sntp_stop();
91 	net_lwip_remove_netif(netif);
92 
93 	if (sntp_state == SUCCESS)
94 		return 0;
95 
96 	return -1;
97 }
98 
do_sntp(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])99 int do_sntp(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
100 {
101 	ip_addr_t *srvip;
102 	char *server;
103 	ip_addr_t ipaddr;
104 
105 	switch (argc) {
106 	case 1:
107 		srvip = NULL;
108 		server = env_get("ntpserverip");
109 		if (server) {
110 			if (!ipaddr_aton(server, &ipaddr)) {
111 				printf("ntpserverip is invalid\n");
112 				return CMD_RET_FAILURE;
113 			}
114 			srvip = &ipaddr;
115 		}
116 		break;
117 	case 2:
118 		if (net_lwip_dns_resolve(argv[1], &ipaddr))
119 			return CMD_RET_FAILURE;
120 		srvip = &ipaddr;
121 		break;
122 	default:
123 		return CMD_RET_USAGE;
124 	}
125 
126 	if (net_lwip_eth_start() < 0)
127 		return CMD_RET_FAILURE;
128 
129 	if (sntp_loop(eth_get_dev(), srvip) < 0)
130 		return CMD_RET_FAILURE;
131 
132 	return CMD_RET_SUCCESS;
133 }
134