1 /*
2 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "freertos/FreeRTOS.h"
8 #include "freertos/event_groups.h"
9 #include "esp_event.h"
10 #include "esp_log.h"
11 #include "esp_check.h"
12 #include "esp_netif.h"
13 #include "esp_netif_sntp.h"
14 #include "esp_sntp.h"
15
16 // Remove compat macro and include lwip API
17 #undef SNTP_OPMODE_POLL
18 #include "lwip/apps/sntp.h"
19
20 static const char *TAG = "esp_netif_sntp";
21
22 typedef struct sntp_storage {
23 esp_sntp_time_cb_t sync_cb;
24 SemaphoreHandle_t sync_sem;
25 ip_event_t ip_event_to_renew;
26 size_t index_of_first_server;
27 size_t num_of_servers;
28 char* servers[];
29 } sntp_storage_t;
30
31 static sntp_storage_t *s_storage = NULL;
32
sync_time_cb(struct timeval * tv)33 static void sync_time_cb(struct timeval *tv)
34 {
35 if (s_storage && s_storage->sync_sem) {
36 xSemaphoreGive(s_storage->sync_sem);
37 }
38 if (s_storage && s_storage->sync_cb) {
39 s_storage->sync_cb(tv);
40 }
41 }
42
sntp_init_api(void * ctx)43 static esp_err_t sntp_init_api(void *ctx)
44 {
45 esp_err_t ret = ESP_OK;
46
47 esp_sntp_config_t * config = ctx;
48 ESP_GOTO_ON_FALSE(config->num_of_servers <= SNTP_MAX_SERVERS, ESP_ERR_INVALID_ARG, err, TAG, "Tried to configure more servers than enabled in lwip. Please update CONFIG_SNTP_MAX_SERVERS");
49
50 sntp_set_time_sync_notification_cb(sync_time_cb);
51 sntp_setoperatingmode(SNTP_OPMODE_POLL);
52
53 for (int i = 0; i < config->num_of_servers; ++i) {
54 sntp_setservername(i, config->servers[i]);
55 }
56
57 if (config->server_from_dhcp) {
58 #if LWIP_DHCP_GET_NTP_SRV
59 sntp_servermode_dhcp(1);
60 #else
61 ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "Tried to configure SNTP server from DHCP, while disabled. Please enable CONFIG_LWIP_DHCP_GET_NTP_SRV");
62 #endif
63 }
64
65 if (config->smooth_sync) {
66 sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
67 }
68
69 if (config->start) {
70 sntp_init();
71 }
72 err:
73 return ret;
74 }
75
renew_servers_api(void * ctx)76 static esp_err_t renew_servers_api(void *ctx)
77 {
78 if (s_storage && s_storage->num_of_servers) {
79 for (int i = 0; i < s_storage->num_of_servers; ++i) {
80 sntp_setservername(i + s_storage->index_of_first_server, s_storage->servers[i]);
81 }
82 }
83 return ESP_OK;
84 }
85
esp_netif_sntp_renew_servers(void * handler_args,esp_event_base_t base,int32_t event_id,void * event_data)86 void esp_netif_sntp_renew_servers(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
87 {
88 esp_netif_tcpip_exec(renew_servers_api, NULL);
89 }
90
esp_netif_sntp_init(const esp_sntp_config_t * config)91 esp_err_t esp_netif_sntp_init(const esp_sntp_config_t * config)
92 {
93 esp_err_t ret = ESP_OK;
94 s_storage = calloc(1, sizeof(sntp_storage_t) + // allocate space for servers only if we are supposed to refresh the settings
95 (config->renew_servers_after_new_IP ? config->num_of_servers * sizeof(char*) : 0));
96 ESP_GOTO_ON_FALSE(s_storage != NULL, ESP_ERR_NO_MEM, err, TAG, "Failed to allocate SNTP storage");
97 if (config->wait_for_sync) {
98 s_storage->sync_sem = xSemaphoreCreateBinary();
99 ESP_GOTO_ON_FALSE(s_storage->sync_sem != NULL, ESP_ERR_NO_MEM, err, TAG, "Failed to SNTP sync semaphore");
100 }
101 if (config->renew_servers_after_new_IP && config->num_of_servers > 0) {
102 s_storage->num_of_servers = config->num_of_servers;
103 s_storage->index_of_first_server = config->index_of_first_server;
104 // store the server names, as we'd have to refresh the config after IP event
105 for (int i = 0; i < config->num_of_servers; ++i) {
106 s_storage->servers[i] = strdup(config->servers[i]);
107 }
108
109 ESP_GOTO_ON_ERROR(esp_event_handler_register(IP_EVENT, config->ip_event_to_renew, esp_netif_sntp_renew_servers, NULL),
110 err, TAG, "Failed to register IP event");
111 s_storage->ip_event_to_renew = config->ip_event_to_renew;
112
113 }
114 if (config->sync_cb) {
115 s_storage->sync_cb = config->sync_cb;
116 }
117
118 ESP_GOTO_ON_ERROR(esp_netif_tcpip_exec(sntp_init_api, (void*)config), err, TAG, "Failed initialize SNTP service");
119
120 return ret;
121
122 err:
123 esp_netif_sntp_deinit();
124 return ret;
125 }
126
sntp_stop_api(void * ctx)127 static esp_err_t sntp_stop_api(void *ctx)
128 {
129 sntp_stop();
130 return ESP_OK;
131 }
132
esp_netif_sntp_deinit(void)133 void esp_netif_sntp_deinit(void)
134 {
135 if (s_storage) {
136 sntp_storage_t *storage = s_storage;
137 s_storage = NULL;
138 sntp_set_time_sync_notification_cb(NULL);
139 esp_netif_tcpip_exec(sntp_stop_api, NULL);
140 if (storage->num_of_servers) {
141 for (int i = 0; i < storage->num_of_servers; ++i) {
142 free(storage->servers[i]);
143 }
144 esp_event_handler_unregister(IP_EVENT, storage->ip_event_to_renew, esp_netif_sntp_renew_servers);
145 }
146 if (storage->sync_sem) {
147 vSemaphoreDelete(storage->sync_sem);
148 }
149 free(storage);
150
151 }
152 }
153
esp_netif_sntp_sync_wait(TickType_t tout)154 esp_err_t esp_netif_sntp_sync_wait(TickType_t tout)
155 {
156 if (s_storage == NULL || s_storage->sync_sem == NULL) {
157 return ESP_ERR_INVALID_STATE;
158 }
159 if (xQueueSemaphoreTake(s_storage->sync_sem, tout) != pdTRUE) {
160 return ESP_ERR_TIMEOUT;
161 }
162 if (sntp_get_sync_mode() == SNTP_SYNC_MODE_SMOOTH &&
163 sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) {
164 return ESP_ERR_NOT_FINISHED;
165 }
166 return ESP_OK;
167 }
168
sntp_start_api(void * ctx)169 static esp_err_t sntp_start_api(void* ctx)
170 {
171 sntp_stop();
172 sntp_init();
173 return ESP_OK;
174 }
175
esp_netif_sntp_start(void)176 esp_err_t esp_netif_sntp_start(void)
177 {
178 return esp_netif_tcpip_exec(sntp_start_api, NULL);
179 }
180