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