// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "sdkconfig.h" #if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) #include "esp32-hal-hosted.h" #include "esp32-hal-log.h" #include "esp32-hal.h" #include "pins_arduino.h" #include "esp_hosted.h" #include "esp_hosted_transport_config.h" // extern esp_err_t esp_hosted_init(); // extern esp_err_t esp_hosted_deinit(); static bool hosted_initialized = false; static bool hosted_ble_active = false; static bool hosted_wifi_active = false; static sdio_pin_config_t sdio_pin_config = { #ifdef BOARD_HAS_SDIO_ESP_HOSTED .pin_clk = BOARD_SDIO_ESP_HOSTED_CLK, .pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD, .pin_d0 = BOARD_SDIO_ESP_HOSTED_D0, .pin_d1 = BOARD_SDIO_ESP_HOSTED_D1, .pin_d2 = BOARD_SDIO_ESP_HOSTED_D2, .pin_d3 = BOARD_SDIO_ESP_HOSTED_D3, .pin_reset = BOARD_SDIO_ESP_HOSTED_RESET #else .pin_clk = CONFIG_ESP_SDIO_PIN_CLK, .pin_cmd = CONFIG_ESP_SDIO_PIN_CMD, .pin_d0 = CONFIG_ESP_SDIO_PIN_D0, .pin_d1 = CONFIG_ESP_SDIO_PIN_D1, .pin_d2 = CONFIG_ESP_SDIO_PIN_D2, .pin_d3 = CONFIG_ESP_SDIO_PIN_D3, .pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE #endif }; static esp_hosted_coprocessor_fwver_t slave_version_struct = {.major1 = 0, .minor1 = 0, .patch1 = 0}; static esp_hosted_coprocessor_fwver_t host_version_struct = { .major1 = ESP_HOSTED_VERSION_MAJOR_1, .minor1 = ESP_HOSTED_VERSION_MINOR_1, .patch1 = ESP_HOSTED_VERSION_PATCH_1 }; static bool hostedInit(); static bool hostedDeinit(); void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { *major = host_version_struct.major1; *minor = host_version_struct.minor1; *patch = host_version_struct.patch1; } void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { *major = slave_version_struct.major1; *minor = slave_version_struct.minor1; *patch = slave_version_struct.patch1; } bool hostedHasUpdate() { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } uint32_t host_version = ESP_HOSTED_VERSION_VAL(host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1); uint32_t slave_version = 0; esp_err_t ret = esp_hosted_get_coprocessor_fwversion(&slave_version_struct); if (ret != ESP_OK) { log_e("Could not get slave firmware version: %s", esp_err_to_name(ret)); } else { slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); } log_i("Host firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1); log_i("Slave firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); // compare major.minor only // slave_version &= 0xFFFFFF00; // host_version &= 0xFFFFFF00; if (host_version == slave_version) { log_i("Versions Match!"); } else if (host_version > slave_version) { log_w("Version on Host is NEWER than version on co-processor"); log_w("Update URL: %s", hostedGetUpdateURL()); return true; } else { log_w("Version on Host is OLDER than version on co-processor"); } return false; } char *hostedGetUpdateURL() { // https://espressif.github.io/arduino-esp32/hosted/esp32c6-v1.2.3.bin static char url[92] = {0}; snprintf( url, 92, "https://espressif.github.io/arduino-esp32/hosted/%s-v%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".bin", CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET, host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1 ); return url; } bool hostedBeginUpdate() { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } esp_err_t err = esp_hosted_slave_ota_begin(); if (err != ESP_OK) { log_e("Failed to begin Update: %s", esp_err_to_name(err)); } return err == ESP_OK; } bool hostedWriteUpdate(uint8_t *buf, uint32_t len) { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } esp_err_t err = esp_hosted_slave_ota_write(buf, len); if (err != ESP_OK) { log_e("Failed to write Update: %s", esp_err_to_name(err)); } return err == ESP_OK; } bool hostedEndUpdate() { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } esp_err_t err = esp_hosted_slave_ota_end(); if (err != ESP_OK) { log_e("Failed to end Update: %s", esp_err_to_name(err)); } return err == ESP_OK; } bool hostedActivateUpdate() { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } // Activate can fail on older firmwares and that is not critical uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0); if (slave_version < min_version) { // Silence messages caused by earlier versions esp_log_level_set("rpc_core", ESP_LOG_NONE); } esp_err_t err = esp_hosted_slave_ota_activate(); // Any further communication will result in logged errors esp_log_level_set("sdmmc_io", ESP_LOG_NONE); esp_log_level_set("H_SDIO_DRV", ESP_LOG_NONE); if (err != ESP_OK && slave_version >= min_version) { log_e("Failed to activate Update: %s", esp_err_to_name(err)); return false; } return true; } static bool hostedInit() { if (!hosted_initialized) { log_i("Initializing ESP-Hosted"); log_d( "SDIO pins: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", sdio_pin_config.pin_clk, sdio_pin_config.pin_cmd, sdio_pin_config.pin_d0, sdio_pin_config.pin_d1, sdio_pin_config.pin_d2, sdio_pin_config.pin_d3, sdio_pin_config.pin_reset ); hosted_initialized = true; struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); conf.pin_clk.pin = sdio_pin_config.pin_clk; conf.pin_cmd.pin = sdio_pin_config.pin_cmd; conf.pin_d0.pin = sdio_pin_config.pin_d0; conf.pin_d1.pin = sdio_pin_config.pin_d1; conf.pin_d2.pin = sdio_pin_config.pin_d2; conf.pin_d3.pin = sdio_pin_config.pin_d3; conf.pin_reset.pin = sdio_pin_config.pin_reset; esp_err_t err = esp_hosted_sdio_set_config(&conf); if (err != ESP_OK) { //&& err != ESP_ERR_NOT_ALLOWED) { // uncomment when second init is fixed log_e("esp_hosted_sdio_set_config failed: %s", esp_err_to_name(err)); return false; } err = esp_hosted_init(); if (err != ESP_OK) { log_e("esp_hosted_init failed: %s", esp_err_to_name(err)); hosted_initialized = false; return false; } log_i("ESP-Hosted initialized!"); err = esp_hosted_connect_to_slave(); if (err != ESP_OK) { log_e("esp_hosted_connect_to_slave failed: %s", esp_err_to_name(err)); hosted_initialized = false; return false; } hostedHasUpdate(); return true; } // Attach pins to PeriMan here // Slave chip model is CONFIG_IDF_SLAVE_TARGET // sdio_pin_config.pin_clk // sdio_pin_config.pin_cmd // sdio_pin_config.pin_d0 // sdio_pin_config.pin_d1 // sdio_pin_config.pin_d2 // sdio_pin_config.pin_d3 // sdio_pin_config.pin_reset return true; } static bool hostedDeinit() { if (!hosted_initialized) { log_e("ESP-Hosted is not initialized"); return false; } if (esp_hosted_deinit() != ESP_OK) { log_e("esp_hosted_deinit failed!"); return false; } hosted_initialized = false; return true; } bool hostedInitBLE() { log_i("Initializing ESP-Hosted for BLE"); if (!hostedInit()) { return false; } uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0); if (slave_version >= min_version) { esp_err_t err = esp_hosted_bt_controller_init(); if (err != ESP_OK) { log_e("esp_hosted_bt_controller_init failed: %s", esp_err_to_name(err)); return false; } err = esp_hosted_bt_controller_enable(); if (err != ESP_OK) { log_e("esp_hosted_bt_controller_enable failed: %s", esp_err_to_name(err)); return false; } } hosted_ble_active = true; return true; } bool hostedInitWiFi() { log_i("Initializing ESP-Hosted for WiFi"); hosted_wifi_active = true; return hostedInit(); } bool hostedDeinitBLE() { log_i("Deinitializing ESP-Hosted for BLE"); uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0); if (slave_version >= min_version) { esp_err_t err = esp_hosted_bt_controller_disable(); if (err != ESP_OK) { log_e("esp_hosted_bt_controller_disable failed: %s", esp_err_to_name(err)); return false; } err = esp_hosted_bt_controller_deinit(false); if (err != ESP_OK) { log_e("esp_hosted_bt_controller_deinit failed: %s", esp_err_to_name(err)); return false; } } hosted_ble_active = false; if (!hosted_wifi_active) { return hostedDeinit(); } else { log_i("ESP-Hosted is still being used by Wi-Fi. Skipping deinit."); return true; } } bool hostedDeinitWiFi() { log_i("Deinitializing ESP-Hosted for WiFi"); hosted_wifi_active = false; if (!hosted_ble_active) { return hostedDeinit(); } else { log_i("ESP-Hosted is still being used by BLE. Skipping deinit."); return true; } } bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) { log_e("All SDIO pins must be defined"); return false; } if (hosted_initialized) { int8_t current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst; hostedGetPins(¤t_clk, ¤t_cmd, ¤t_d0, ¤t_d1, ¤t_d2, ¤t_d3, ¤t_rst); log_e("SDIO pins must be set before ESP-Hosted is initialized"); log_e( "Current pins used: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst ); return false; } sdio_pin_config.pin_clk = clk; sdio_pin_config.pin_cmd = cmd; sdio_pin_config.pin_d0 = d0; sdio_pin_config.pin_d1 = d1; sdio_pin_config.pin_d2 = d2; sdio_pin_config.pin_d3 = d3; sdio_pin_config.pin_reset = rst; return true; } void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst) { *clk = sdio_pin_config.pin_clk; *cmd = sdio_pin_config.pin_cmd; *d0 = sdio_pin_config.pin_d0; *d1 = sdio_pin_config.pin_d1; *d2 = sdio_pin_config.pin_d2; *d3 = sdio_pin_config.pin_d3; *rst = sdio_pin_config.pin_reset; } bool hostedIsBLEActive() { return hosted_ble_active; } bool hostedIsWiFiActive() { return hosted_wifi_active; } bool hostedIsInitialized() { return hosted_initialized; } #endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */