/* SPDX-License-Identifier: GPL-3.0-only */ /* mDNS runtime service; its lifecycle is owned by wifi_manager. */ #include "mdns_service.h" #include #include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "mdns.h" static SemaphoreHandle_t s_mutex; static mdns_config_t s_config; static uint32_t s_config_generation; static bool s_component_initialized; static bool s_initialization_failed; static bool s_announced; static esp_err_t s_last_error; static void lock_service(void) { (void)xSemaphoreTake(s_mutex, portMAX_DELAY); } static void unlock_service(void) { (void)xSemaphoreGive(s_mutex); } static void make_hostname(const mdns_config_t *config, char *hostname, size_t size) { (void)snprintf(hostname, size, "sak-%s", config->suffix); } esp_err_t mdns_service_init(const mdns_config_t *config) { if (mdns_config_validate(config) != ESP_OK) { return ESP_ERR_INVALID_ARG; } if (s_mutex != NULL) { return ESP_ERR_INVALID_STATE; } s_mutex = xSemaphoreCreateMutex(); if (s_mutex == NULL) { return ESP_ERR_NO_MEM; } s_config = *config; s_config_generation = 1; s_last_error = ESP_OK; return ESP_OK; } esp_err_t mdns_service_get_config(mdns_config_t *config) { if (config == NULL || s_mutex == NULL) { return ESP_ERR_INVALID_STATE; } lock_service(); *config = s_config; unlock_service(); return ESP_OK; } esp_err_t mdns_service_set_config(const mdns_config_t *config) { if (s_mutex == NULL || mdns_config_validate(config) != ESP_OK) { return ESP_ERR_INVALID_ARG; } lock_service(); if (s_config_generation == UINT32_MAX) { unlock_service(); return ESP_ERR_INVALID_STATE; } s_config = *config; ++s_config_generation; unlock_service(); return ESP_OK; } static void snapshot_locked(mdns_service_snapshot_t *snapshot) { memset(snapshot, 0, sizeof(*snapshot)); snapshot->config_generation = s_config_generation; snapshot->initialized = true; snapshot->announced = s_announced; memcpy(snapshot->suffix, s_config.suffix, s_config.suffix_len); make_hostname(&s_config, snapshot->hostname, sizeof(snapshot->hostname)); snapshot->last_error = s_last_error; } esp_err_t mdns_service_get_snapshot(mdns_service_snapshot_t *snapshot) { if (!snapshot || !s_mutex) return ESP_ERR_INVALID_STATE; lock_service(); snapshot_locked(snapshot); unlock_service(); return ESP_OK; } esp_err_t mdns_service_get_settings(mdns_service_snapshot_t *snapshot) { if (!snapshot) return ESP_ERR_INVALID_ARG; memset(snapshot, 0, sizeof(*snapshot)); if (!s_mutex) return ESP_ERR_INVALID_STATE; if (xSemaphoreTake(s_mutex, 0) != pdTRUE) return ESP_ERR_TIMEOUT; snapshot_locked(snapshot); unlock_service(); return ESP_OK; } esp_err_t mdns_service_update_current(uint32_t generation, mdns_settings_action_t action, const mdns_config_t *config, bool *stored) { if (!stored || action > MDNS_SETTINGS_DEFAULTS || action < MDNS_SETTINGS_SET || (action == MDNS_SETTINGS_SET && mdns_config_validate(config) != ESP_OK)) return ESP_ERR_INVALID_ARG; *stored = true; if (!s_mutex) return ESP_ERR_INVALID_STATE; lock_service(); if (!generation || generation != s_config_generation) { unlock_service(); return ESP_ERR_NOT_FOUND; } esp_err_t error = ESP_OK; mdns_config_t candidate = s_config; if (action == MDNS_SETTINGS_SAVE) error = mdns_config_save(&s_config); else if (s_config_generation == UINT32_MAX) error = ESP_ERR_INVALID_STATE; else { if (action == MDNS_SETTINGS_SET) candidate = *config; else if (action == MDNS_SETTINGS_LOAD) error = mdns_config_load(&candidate, stored); else mdns_config_defaults(&candidate); if (error == ESP_OK) error = mdns_config_validate(&candidate); if (error == ESP_OK) { s_config = candidate; ++s_config_generation; } } unlock_service(); return error; } esp_err_t mdns_service_start(void) { if (s_mutex == NULL) { return ESP_ERR_INVALID_STATE; } lock_service(); if (s_component_initialized) { s_announced = true; unlock_service(); /* A suffix staged while offline must reach the already-created responder * when the next STA IP arrives, even if its reannounce command ran offline. */ return mdns_service_reannounce(); } if (s_initialization_failed) { esp_err_t error = s_last_error; unlock_service(); return error; } mdns_config_t config = s_config; unlock_service(); esp_err_t error = mdns_init(); if (error == ESP_OK) { char hostname[MDNS_CONFIG_SUFFIX_MAX_LEN + 5U] = {0}; make_hostname(&config, hostname, sizeof(hostname)); error = mdns_hostname_set(hostname); if (error == ESP_OK) { error = mdns_instance_name_set("ESP32 Serial Swiss Army Knife"); } if (error != ESP_OK) { mdns_free(); } } lock_service(); s_component_initialized = error == ESP_OK; s_initialization_failed = error != ESP_OK; s_announced = error == ESP_OK; s_last_error = error; unlock_service(); return error; } void mdns_service_stop(void) { if (s_mutex == NULL) { return; } lock_service(); s_announced = false; unlock_service(); } esp_err_t mdns_service_reannounce(void) { if (s_mutex == NULL) { return ESP_ERR_INVALID_STATE; } lock_service(); if (!s_component_initialized) { esp_err_t error = s_initialization_failed ? s_last_error : ESP_ERR_INVALID_STATE; unlock_service(); return error; } mdns_config_t config = s_config; unlock_service(); char hostname[MDNS_CONFIG_SUFFIX_MAX_LEN + 5U] = {0}; make_hostname(&config, hostname, sizeof(hostname)); esp_err_t error = mdns_hostname_set(hostname); lock_service(); s_last_error = error; unlock_service(); return error; }