From dcb35856b296effcf945c6e531fe688e27d54354 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Thu, 27 Feb 2025 14:47:58 +0800 Subject: [PATCH 01/44] fix(slave/compiler): Optimization to be performance, -O2 instead of -Og --- slave/sdkconfig.defaults | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slave/sdkconfig.defaults b/slave/sdkconfig.defaults index 9313e28e..2ff48b15 100644 --- a/slave/sdkconfig.defaults +++ b/slave/sdkconfig.defaults @@ -8,3 +8,5 @@ CONFIG_PARTITION_TABLE_TWO_OTA=y # OS CONFIG_FREERTOS_HZ=1000 + +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y From 4593fb4155b9d4c12df21d992ff9e642012983db Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 25 Feb 2025 15:27:42 +0800 Subject: [PATCH 02/44] feat(h2_c5_board) Add C5 sub board for P4 host, add H2 as host - add GPIOs for Handshake and Data Ready for C5 as co-processor. Other SPI GPIO on C5 subboard are default values - for P4 Host, define GPIOs used to communicate with C5 subboard - tested with standard SPI and SPI-HD (4 data lines) - add GPIO settings for H2 as host - tested with standard SPI and SPI-HD (2 data lines) - remapped C5_SPI_CLK to CS_WAKE_UP - bumped version --- Kconfig | 48 +++++++++++++++++++++++++++++------- idf_component.yml | 2 +- slave/main/Kconfig.projbuild | 6 ++++- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/Kconfig b/Kconfig index 005c49e7..9509d7d5 100644 --- a/Kconfig +++ b/Kconfig @@ -189,8 +189,10 @@ menu "ESP-Hosted config" depends on ESP_HOSTED_SPI_HSPI int "GPIO pin for Host MOSI" default 14 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 23 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 13 if IDF_TARGET_ESP32 default 11 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 5 if IDF_TARGET_ESP32H2 default 7 help SPI controller Host MOSI @@ -199,8 +201,10 @@ menu "ESP-Hosted config" depends on ESP_HOSTED_SPI_HSPI int "GPIO pin for Host MISO" default 15 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 22 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 12 if IDF_TARGET_ESP32 default 13 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 0 if IDF_TARGET_ESP32H2 default 2 help SPI controller Host MISO @@ -209,8 +213,10 @@ menu "ESP-Hosted config" depends on ESP_HOSTED_SPI_HSPI int "GPIO pin for Host CLK" default 18 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 33 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 14 if IDF_TARGET_ESP32 default 12 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 4 if IDF_TARGET_ESP32H2 default 6 help SPI controller Host CLK @@ -219,8 +225,10 @@ menu "ESP-Hosted config" depends on ESP_HOSTED_SPI_HSPI int "GPIO pin for Host CS" default 19 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 4 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 15 if IDF_TARGET_ESP32 default 10 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 1 if IDF_TARGET_ESP32H2 default 10 help SPI controller Host CS @@ -276,8 +284,10 @@ menu "ESP-Hosted config" config ESP_HOSTED_SPI_GPIO_HANDSHAKE int "GPIO pin for handshake" default 16 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 21 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 3 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 default 17 if IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32S2 + default 22 if IDF_TARGET_ESP32H2 default 26 help GPIO pin to use for handshake with other spi controller @@ -285,6 +295,8 @@ menu "ESP-Hosted config" config ESP_HOSTED_SPI_GPIO_DATA_READY int "GPIO pin for data ready interrupt" default 17 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 32 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 12 if IDF_TARGET_ESP32H2 default 4 help GPIO pin for indicating host that SPI slave has data to be read by host @@ -292,6 +304,8 @@ menu "ESP-Hosted config" config ESP_HOSTED_SPI_GPIO_RESET_SLAVE int "GPIO pin for Reseting slave ESP" default 54 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 53 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 10 if IDF_TARGET_ESP32H2 default 5 help GPIO pin for Resetting ESP SPI slave device. Should be connected to RST/EN of ESP SPI slave device. @@ -322,7 +336,7 @@ ESP32XX_SPI_CLK_FREQ_RANGE_MAX := 40 config ESP_HOSTED_SPI_FREQ_ESP32XX depends on SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 int "SPI Clock Freq" - default 30 if SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 + default 40 if SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 range $(ESP32XX_SPI_CLK_FREQ_RANGE_MIN) $(ESP32XX_SPI_CLK_FREQ_RANGE_MAX) help "Optimize SPI CLK by increasing till host practically can support" @@ -570,7 +584,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_CS int "GPIO pin for Host CS" default 10 if IDF_TARGET_ESP32S3 - default 19 if IDF_TARGET_ESP32P4 + default 4 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 19 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 1 if IDF_TARGET_ESP32H2 default 15 help SPI Half-duplex controller Host CS @@ -578,7 +594,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_CLK int "GPIO pin for Host CLK" default 12 if IDF_TARGET_ESP32S3 - default 18 if IDF_TARGET_ESP32P4 + default 33 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 18 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 4 if IDF_TARGET_ESP32H2 default 18 help SPI Half-duplex controller Host CLK @@ -586,7 +604,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_D0 int "GPIO pin for Host D0" default 11 if IDF_TARGET_ESP32S3 - default 14 if IDF_TARGET_ESP32P4 + default 23 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 14 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 5 if IDF_TARGET_ESP32H2 default 2 help SPI Half-duplex controller Host D0 @@ -594,7 +614,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_D1 int "GPIO pin for Host D1" default 13 if IDF_TARGET_ESP32S3 - default 15 if IDF_TARGET_ESP32P4 + default 22 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 15 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 0 if IDF_TARGET_ESP32H2 default 4 help SPI Half-duplex controller Host D1 @@ -603,7 +625,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SPI_HD_PRIV_INTERFACE_4_DATA_LINES int "GPIO pin for Host D2" default 14 if IDF_TARGET_ESP32S3 - default 16 if IDF_TARGET_ESP32P4 + default 20 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 16 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 2 if IDF_TARGET_ESP32H2 default 12 help SPI Half-duplex controller Host D2 @@ -612,7 +636,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SPI_HD_PRIV_INTERFACE_4_DATA_LINES int "GPIO pin for Host D3" default 9 if IDF_TARGET_ESP32S3 - default 17 if IDF_TARGET_ESP32P4 + default 21 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 17 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 3 if IDF_TARGET_ESP32H2 default 13 help SPI Half-duplex controller Host D3 @@ -620,7 +646,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_DATA_READY int "GPIO pin for data ready interrupt" default 4 if IDF_TARGET_ESP32S3 - default 6 if IDF_TARGET_ESP32P4 + default 32 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 6 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 12 if IDF_TARGET_ESP32H2 default 8 help GPIO pin for indicating host that slave has data to be read by host @@ -628,7 +656,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SPI_HD_GPIO_RESET_SLAVE int "GPIO pin for Reseting slave ESP" default 5 if IDF_TARGET_ESP32S3 + default 53 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 default 54 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 10 if IDF_TARGET_ESP32H2 default 5 help GPIO pin for Resetting ESP slave device. Should be connected to RST/EN of ESP SPI slave device. @@ -651,7 +681,7 @@ ESP32XX_SPI_HD_CLK_FREQ_RANGE_MAX := 40 config ESP_HOSTED_SPI_HD_FREQ_ESP32XX depends on SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 int "SPI HD Clock Freq (MHz)" - default 10 if SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 + default 40 if SLAVE_IDF_TARGET_ESP32C2 || SLAVE_IDF_TARGET_ESP32C3 || SLAVE_IDF_TARGET_ESP32S2 || SLAVE_IDF_TARGET_ESP32S3 || SLAVE_IDF_TARGET_ESP32C5 range $(ESP32XX_SPI_HD_CLK_FREQ_RANGE_MIN) $(ESP32XX_SPI_HD_CLK_FREQ_RANGE_MAX) help "Optimize CLK by increasing till host practically can support" diff --git a/idf_component.yml b/idf_component.yml index 1076fe38..ed9122d4 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.3.2" +version: "1.3.3" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index e0a42add..0ebdd9fb 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -12,7 +12,6 @@ menu "Example Configuration" config ESP_HOST_DEV_BOARD_P4_FUNC_BOARD bool "ESP32-P4-Function-EV-Board" - depends on IDF_TARGET_ESP32C6 endchoice @@ -129,6 +128,7 @@ menu "Example Configuration" depends on SPI_HSPI int "Slave GPIO pin for Host CLK" default 19 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD + default 3 if IDF_TARGET_ESP32C5 default 14 if IDF_TARGET_ESP32 default 12 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 default 6 @@ -196,6 +196,7 @@ menu "Example Configuration" config ESP_SPI_GPIO_HANDSHAKE int "Slave GPIO pin for handshake" default 22 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD + default 4 if IDF_TARGET_ESP32C5 default 3 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 default 17 if IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32S2 default 26 @@ -205,6 +206,7 @@ menu "Example Configuration" config ESP_SPI_GPIO_DATA_READY int "Slave GPIO pin for data ready interrupt" default 23 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD + default 0 if IDF_TARGET_ESP32C5 default 13 if IDF_TARGET_ESP32C5 default 4 help @@ -414,6 +416,7 @@ menu "Example Configuration" config ESP_SPI_HD_GPIO_CLK int "Slave GPIO pin for Host CLK" default 19 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD + default 3 if IDF_TARGET_ESP32C5 default 6 help SPI HD controller Host CS @@ -451,6 +454,7 @@ menu "Example Configuration" config ESP_SPI_HD_GPIO_DATA_READY int "Slave GPIO pin for Data Ready" default 2 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD + default 0 if IDF_TARGET_ESP32C5 default 13 if IDF_TARGET_ESP32C5 default 11 help From 0a92d91053286a8523a78d39d29a50c9241908c9 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Fri, 7 Mar 2025 16:49:17 +0800 Subject: [PATCH 03/44] enhance(transport_config_reset) Add Reset pin config to transport config - add reset pin config to transport configs - add API to get reset pin config - get and use reset pin config in `reset_slave()` - bumped version --- .../api/include/esp_hosted_transport_config.h | 10 ++++++ host/drivers/transport/transport_drv.c | 17 ++++++--- host/port/src/esp_hosted_transport_config.c | 36 ++++++++++++++++++- idf_component.yml | 2 +- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/host/api/include/esp_hosted_transport_config.h b/host/api/include/esp_hosted_transport_config.h index 25b4e449..17f31ee2 100644 --- a/host/api/include/esp_hosted_transport_config.h +++ b/host/api/include/esp_hosted_transport_config.h @@ -34,6 +34,7 @@ struct esp_hosted_sdio_config { gpio_pin_t pin_d1; gpio_pin_t pin_d2; gpio_pin_t pin_d3; + gpio_pin_t pin_reset; uint8_t rx_mode; bool block_mode; bool iomux_enable; @@ -51,6 +52,7 @@ struct esp_hosted_spi_hd_config { gpio_pin_t pin_d1; gpio_pin_t pin_d2; gpio_pin_t pin_d3; + gpio_pin_t pin_reset; /* SPI HD configuration */ uint32_t clk_mhz; @@ -71,6 +73,7 @@ struct esp_hosted_spi_config { gpio_pin_t pin_cs; gpio_pin_t pin_handshake; gpio_pin_t pin_data_ready; + gpio_pin_t pin_reset; /* SPI Full Duplex configuration */ uint16_t tx_queue_size; @@ -86,6 +89,7 @@ struct esp_hosted_uart_config { /* UART pins */ gpio_pin_t pin_tx; gpio_pin_t pin_rx; + gpio_pin_t pin_reset; /* UART configuration */ uint8_t num_data_bits; @@ -121,6 +125,7 @@ struct esp_hosted_transport_config { .pin_d1 = {.port = NULL, .pin = H_SDIO_PIN_D1}, \ .pin_d2 = {.port = NULL, .pin = H_SDIO_PIN_D2}, \ .pin_d3 = {.port = NULL, .pin = H_SDIO_PIN_D3}, \ + .pin_reset = {.port = NULL, .pin = H_GPIO_PIN_RESET_Pin }, \ .rx_mode = H_SDIO_HOST_RX_MODE, \ .block_mode = H_SDIO_TX_BLOCK_ONLY_XFER && H_SDIO_RX_BLOCK_ONLY_XFER, \ .iomux_enable = false, \ @@ -149,6 +154,7 @@ struct esp_hosted_transport_config { .pin_d1 = {.port = NULL, .pin = H_SPI_HD_PIN_D1}, \ .pin_d2 = {.port = NULL, .pin = H_SPI_HD_PIN_D2}, \ .pin_d3 = {.port = NULL, .pin = H_SPI_HD_PIN_D3}, \ + .pin_reset = {.port = NULL, .pin = H_GPIO_PIN_RESET_Pin }, \ .clk_mhz = H_SPI_HD_CLK_MHZ, \ .mode = H_SPI_HD_MODE, \ .tx_queue_size = H_SPI_HD_TX_QUEUE_SIZE, \ @@ -169,6 +175,7 @@ struct esp_hosted_transport_config { .pin_cs = {.port = NULL, .pin = H_GPIO_CS_Pin}, \ .pin_handshake = {.port = NULL, .pin = H_GPIO_HANDSHAKE_Pin}, \ .pin_data_ready = {.port = NULL, .pin = H_GPIO_DATA_READY_Pin}, \ + .pin_reset = {.port = NULL, .pin = H_GPIO_PIN_RESET_Pin }, \ .tx_queue_size = H_SPI_TX_Q, \ .rx_queue_size = H_SPI_RX_Q, \ .mode = H_SPI_MODE, \ @@ -182,6 +189,7 @@ struct esp_hosted_transport_config { .port = H_UART_PORT, \ .pin_tx = {.port = NULL, .pin = H_UART_TX_PIN}, \ .pin_rx = {.port = NULL, .pin = H_UART_RX_PIN}, \ + .pin_reset = {.port = NULL, .pin = H_GPIO_PIN_RESET_Pin }, \ .num_data_bits = H_UART_NUM_DATA_BITS, \ .parity = H_UART_PARITY, \ .stop_bits = H_UART_STOP_BITS, \ @@ -197,6 +205,8 @@ struct esp_hosted_transport_config { /* Configuration get/set functions */ esp_hosted_transport_err_t esp_hosted_transport_set_default_config(void); esp_hosted_transport_err_t esp_hosted_transport_get_config(struct esp_hosted_transport_config **config); +esp_hosted_transport_err_t esp_hosted_transport_get_reset_config(gpio_pin_t *pin_config); + bool esp_hosted_transport_is_config_valid(void); #if H_TRANSPORT_SDIO == H_TRANSPORT_IN_USE diff --git a/host/drivers/transport/transport_drv.c b/host/drivers/transport/transport_drv.c index bc35d503..3039d2ee 100644 --- a/host/drivers/transport/transport_drv.c +++ b/host/drivers/transport/transport_drv.c @@ -20,6 +20,7 @@ #include "transport_drv.h" #include "esp_hosted_transport.h" #include "esp_hosted_transport_init.h" +#include "esp_hosted_transport_config.h" #include "stats.h" #include "esp_log.h" #include "esp_hosted_log.h" @@ -62,14 +63,20 @@ uint8_t is_transport_tx_ready(void) static void reset_slave(void) { - ESP_LOGI(TAG, "Reset slave using GPIO[%u]", H_GPIO_PIN_RESET_Pin); - g_h.funcs->_h_config_gpio(H_GPIO_PIN_RESET_Port, H_GPIO_PIN_RESET_Pin, H_GPIO_MODE_DEF_OUTPUT); + gpio_pin_t reset_pin = { 0 }; + if (ESP_TRANSPORT_OK != esp_hosted_transport_get_reset_config(&reset_pin)) { + ESP_LOGE(TAG, "Unable to get RESET config for transport"); + return; + } + + ESP_LOGI(TAG, "Reset slave using GPIO[%u]", reset_pin.pin); + g_h.funcs->_h_config_gpio(H_GPIO_PIN_RESET_Port, reset_pin.pin, H_GPIO_MODE_DEF_OUTPUT); - g_h.funcs->_h_write_gpio(H_GPIO_PIN_RESET_Port, H_GPIO_PIN_RESET_Pin, H_RESET_VAL_ACTIVE); + g_h.funcs->_h_write_gpio(reset_pin.port, reset_pin.pin, H_RESET_VAL_ACTIVE); g_h.funcs->_h_msleep(50); - g_h.funcs->_h_write_gpio(H_GPIO_PIN_RESET_Port, H_GPIO_PIN_RESET_Pin, H_RESET_VAL_INACTIVE); + g_h.funcs->_h_write_gpio(reset_pin.port, reset_pin.pin, H_RESET_VAL_INACTIVE); g_h.funcs->_h_msleep(50); - g_h.funcs->_h_write_gpio(H_GPIO_PIN_RESET_Port, H_GPIO_PIN_RESET_Pin, H_RESET_VAL_ACTIVE); + g_h.funcs->_h_write_gpio(reset_pin.port, reset_pin.pin, H_RESET_VAL_ACTIVE); /* stop spi transactions short time to avoid slave sync issues */ g_h.funcs->_h_sleep(1); diff --git a/host/port/src/esp_hosted_transport_config.c b/host/port/src/esp_hosted_transport_config.c index c9b1f8f5..0c843bc3 100644 --- a/host/port/src/esp_hosted_transport_config.c +++ b/host/port/src/esp_hosted_transport_config.c @@ -11,7 +11,7 @@ static const char *TAG = "esp_hosted_transport_config"; /* Static configurations */ -static struct esp_hosted_transport_config s_transport_config; +static struct esp_hosted_transport_config s_transport_config = { 0 }; /* Flags to track if configs were set */ static bool esp_hosted_transport_config_set; @@ -48,6 +48,40 @@ esp_hosted_transport_err_t esp_hosted_transport_get_config(struct esp_hosted_tra return ESP_TRANSPORT_OK; } +esp_hosted_transport_err_t esp_hosted_transport_get_reset_config(gpio_pin_t *pin_config) +{ + if (!pin_config) { + return ESP_TRANSPORT_ERR_INVALID_ARG; + } + + switch(s_transport_config.transport_in_use) { + case H_TRANSPORT_SDIO: + pin_config->port = s_transport_config.u.sdio.pin_reset.port; + pin_config->pin = s_transport_config.u.sdio.pin_reset.pin; + break; + case H_TRANSPORT_SPI_HD: + pin_config->port = s_transport_config.u.spi_hd.pin_reset.port; + pin_config->pin = s_transport_config.u.spi_hd.pin_reset.pin; + break; + case H_TRANSPORT_SPI: + pin_config->port = s_transport_config.u.spi.pin_reset.port; + pin_config->pin = s_transport_config.u.spi.pin_reset.pin; + break; + case H_TRANSPORT_UART: + pin_config->port = s_transport_config.u.uart.pin_reset.port; + pin_config->pin = s_transport_config.u.uart.pin_reset.pin; + break; + case H_TRANSPORT_NONE: // drop through to default case + default: + // transport config not yet initialised. Use default Reset pin config + pin_config->port = NULL; + pin_config->pin = H_GPIO_PIN_RESET_Pin; + break; + } + + return ESP_TRANSPORT_OK; +} + #if H_TRANSPORT_IN_USE == H_TRANSPORT_SDIO /* SDIO functions */ esp_hosted_transport_err_t esp_hosted_sdio_get_config(struct esp_hosted_sdio_config **config) diff --git a/idf_component.yml b/idf_component.yml index ed9122d4..0ffefbcd 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.3.3" +version: "1.3.4" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 4170b37a9f2375ec0311726c3a3d566f3576ed53 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 19 Mar 2025 11:30:05 +0800 Subject: [PATCH 04/44] workaround(sdio_host): restart host if sdio slave is unresponsive --- host/drivers/transport/sdio/sdio_drv.c | 27 ++++++++++++++++++++------ host/hosted_os_abstraction.h | 2 ++ host/port/include/sdio_wrapper.h | 1 + host/port/src/os_wrapper.c | 12 +++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/host/drivers/transport/sdio/sdio_drv.c b/host/drivers/transport/sdio/sdio_drv.c index 0f991dab..363c89eb 100644 --- a/host/drivers/transport/sdio/sdio_drv.c +++ b/host/drivers/transport/sdio/sdio_drv.c @@ -48,8 +48,8 @@ static const char TAG[] = "H_SDIO_DRV"; // max number of time to try to read write buffer available reg #define MAX_WRITE_BUF_RETRIES 50 -// max number of times to try to write data to slave device -#define MAX_WRITE_RETRIES 2 +/* Actual data sdio_write max retry */ +#define MAX_SDIO_WRITE_RETRY 2 // this locks the sdio transaction at the driver level, instead of at the HAL layer #define USE_DRIVER_LOCK @@ -307,12 +307,23 @@ static int sdio_is_write_buffer_available(uint32_t buf_needed) { static uint32_t buf_available = 0; uint8_t retry = MAX_WRITE_BUF_RETRIES; + uint32_t max_retry_sdio_not_responding = 2; /*If buffer needed are less than buffer available then only read for available buffer number from slave*/ if (buf_available < buf_needed) { while (retry) { - sdio_get_tx_buffer_num(&buf_available, ACQUIRE_LOCK); + if (sdio_get_tx_buffer_num(&buf_available, ACQUIRE_LOCK) == + ESP_HOSTED_SDIO_UNRESPONSIVE_CODE) { + max_retry_sdio_not_responding--; + /* restart the host to avoid the sdio locked out state */ + + if (!max_retry_sdio_not_responding) { + ESP_LOGE(TAG, "%s: SDIO slave unresponsive, restart host", __func__); + g_h.funcs->_h_restart_host(); + } + continue; + } if (buf_available < buf_needed) { @@ -468,12 +479,14 @@ static void sdio_write_task(void const* pvParameters) ESP_LOGE(TAG, "%s: %d: Failed to send data: %d %ld %ld", __func__, retries, ret, len_to_send, data_left); retries++; - if (retries < MAX_WRITE_RETRIES) { + if (retries < MAX_SDIO_WRITE_RETRY) { ESP_LOGD(TAG, "retry"); continue; } else { - ESP_LOGE(TAG, "abort sending of data"); - goto unlock_done; + SDIO_DRV_UNLOCK(); + ESP_LOGE(TAG, "Unrecoverable host sdio state, reset host mcu"); + g_h.funcs->_h_restart_host(); + goto done; } } @@ -815,6 +828,8 @@ static void sdio_read_task(void const* pvParameters) ESP_LOGE(TAG, "failed to read interrupt register"); SDIO_DRV_UNLOCK(); + ESP_LOGI(TAG, "Host is reseting itself, to avoid any sdio race condition"); + g_h.funcs->_h_restart_host(); continue; } #endif diff --git a/host/hosted_os_abstraction.h b/host/hosted_os_abstraction.h index 6ba1ddd1..8d2b17a9 100644 --- a/host/hosted_os_abstraction.h +++ b/host/hosted_os_abstraction.h @@ -103,6 +103,8 @@ typedef struct { /* 57 */ int (*_h_uart_read)(void *ctx, uint8_t *data, uint16_t size); /* 58 */ int (*_h_uart_write)(void *ctx, uint8_t *data, uint16_t size); #endif + +/* 59 */ int (*_h_restart_host)(void); } hosted_osi_funcs_t; struct hosted_config_t { diff --git a/host/port/include/sdio_wrapper.h b/host/port/include/sdio_wrapper.h index eccf243d..9491841f 100644 --- a/host/port/include/sdio_wrapper.h +++ b/host/port/include/sdio_wrapper.h @@ -22,6 +22,7 @@ #include "sdmmc_cmd.h" #define MAX_TRANSPORT_BUFFER_SIZE MAX_SDIO_BUFFER_SIZE +#define ESP_HOSTED_SDIO_UNRESPONSIVE_CODE 0x107 /* Hosted init function to init the SDIO host * returns a pointer to the sdio context */ diff --git a/host/port/src/os_wrapper.c b/host/port/src/os_wrapper.c index 009bfd9f..8506d53c 100644 --- a/host/port/src/os_wrapper.c +++ b/host/port/src/os_wrapper.c @@ -22,6 +22,7 @@ #include "freertos/portmacro.h" #include "esp_macros.h" #include "esp_hosted_config.h" +#include "esp_wifi.h" /* Wi-Fi headers are reused at ESP-Hosted */ #include "esp_wifi_crypto_types.h" @@ -47,7 +48,7 @@ DEFINE_LOG_TAG(os_wrapper_esp); struct mempool * nw_mp_g = NULL; -wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs; +const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs; wifi_osi_funcs_t g_wifi_osi_funcs; //ESP_EVENT_DECLARE_BASE(WIFI_EVENT); @@ -778,6 +779,14 @@ void hosted_log_write(int level, va_end(list); } +int hosted_restart_host(void) +{ + ESP_LOGI(TAG, "Restarting host"); + esp_unregister_shutdown_handler((shutdown_handler_t)esp_wifi_stop); + esp_restart(); + return 0; +} + /* newlib hooks */ hosted_osi_funcs_t g_hosted_osi_funcs = { @@ -851,4 +860,5 @@ hosted_osi_funcs_t g_hosted_osi_funcs = { ._h_uart_read = hosted_uart_read , ._h_uart_write = hosted_uart_write , #endif + ._h_restart_host = hosted_restart_host , }; From 98f44dbbfd1a67d2bc26e81e5f73879cbce1e9b4 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 19 Mar 2025 14:41:37 +0800 Subject: [PATCH 05/44] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d46807a3..a2e61d5b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ before_script: build_idf_v5.3: extends: .build_template - image: espressif/idf:release-v5.3 + image: ${CI_DOCKER_REGISTRY}/esp-env-v5.3 parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] @@ -42,7 +42,7 @@ build_idf_v5.3: build_idf_v5.4: extends: .build_template - image: espressif/idf:release-v5.4 + image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4 parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] @@ -50,7 +50,7 @@ build_idf_v5.4: build_idf_v5.5: extends: .build_template - image: espressif/idf:latest + image: ${CI_DOCKER_REGISTRY}/esp-env-latest parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] From 0ece814e5a7f8238271c6a8507860c01b0cf56b5 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 19 Mar 2025 14:43:53 +0800 Subject: [PATCH 06/44] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a2e61d5b..532a7841 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ before_script: stage: build tags: - build + image: ${CI_DOCKER_REGISTRY}/esp-env-v5.3 artifacts: paths: - "${IDF_PATH}/${IDF_EXAMPLE_PATH}/build*/build_log.txt" @@ -34,7 +35,6 @@ before_script: build_idf_v5.3: extends: .build_template - image: ${CI_DOCKER_REGISTRY}/esp-env-v5.3 parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] From 9e091754dcc28cc3e338ba1809b184ea00c7c1d4 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 19 Mar 2025 14:50:17 +0800 Subject: [PATCH 07/44] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 532a7841..5cfe88a1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,14 +1,26 @@ stages: + - pre_check - build before_script: - git submodule update --init --recursive +check_version: + stage: pre_check + image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4:1 + script: + echo "Hello for MCU" + rules: + - when: always + tags: + - build + + .build_template: stage: build tags: - build - image: ${CI_DOCKER_REGISTRY}/esp-env-v5.3 + image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4:1 artifacts: paths: - "${IDF_PATH}/${IDF_EXAMPLE_PATH}/build*/build_log.txt" From 6d3cb01756579109c61f7475fe05e198f0f8d93e Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 19 Mar 2025 17:41:15 +0800 Subject: [PATCH 08/44] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5cfe88a1..d46807a3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,26 +1,13 @@ stages: - - pre_check - build before_script: - git submodule update --init --recursive -check_version: - stage: pre_check - image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4:1 - script: - echo "Hello for MCU" - rules: - - when: always - tags: - - build - - .build_template: stage: build tags: - build - image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4:1 artifacts: paths: - "${IDF_PATH}/${IDF_EXAMPLE_PATH}/build*/build_log.txt" @@ -47,6 +34,7 @@ check_version: build_idf_v5.3: extends: .build_template + image: espressif/idf:release-v5.3 parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] @@ -54,7 +42,7 @@ build_idf_v5.3: build_idf_v5.4: extends: .build_template - image: ${CI_DOCKER_REGISTRY}/esp-env-v5.4 + image: espressif/idf:release-v5.4 parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] @@ -62,7 +50,7 @@ build_idf_v5.4: build_idf_v5.5: extends: .build_template - image: ${CI_DOCKER_REGISTRY}/esp-env-latest + image: espressif/idf:latest parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] From 19e7be781e40de528138fd3d4fe1bde278ccb143 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 27 Mar 2025 10:33:39 +0800 Subject: [PATCH 09/44] bugfix(stats) Fix build failure when raw throughput test is enabled - added required header files --- host/utils/stats.c | 1 + idf_component.yml | 2 +- slave/main/stats.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/host/utils/stats.c b/host/utils/stats.c index 88544042..3917fc37 100644 --- a/host/utils/stats.c +++ b/host/utils/stats.c @@ -23,6 +23,7 @@ #include "transport_drv.h" #endif #include "esp_log.h" +#include "esp_hosted_transport_init.h" // use mempool and zero copy for Tx #include "mempool.h" diff --git a/idf_component.yml b/idf_component.yml index 0ffefbcd..909043b2 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.3.4" +version: "1.3.5" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/stats.c b/slave/main/stats.c index 202de30d..260c4db9 100644 --- a/slave/main/stats.c +++ b/slave/main/stats.c @@ -17,6 +17,7 @@ #include "stats.h" #include #include "esp_log.h" +#include "esp_hosted_transport_init.h" #if TEST_RAW_TP || ESP_PKT_STATS static const char TAG[] = "stats"; @@ -269,4 +270,3 @@ uint8_t debug_get_raw_tp_conf(void) { #endif return raw_tp_cap; } - From 9755f7141968ab6b3733119c44eba758d4b14087 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 27 Mar 2025 15:39:04 +0800 Subject: [PATCH 10/44] enhance(deassert_hs_after_cs) Deassert HS and start new transaction after SPI CS deasserted - added config to deassert Handshake only after CS is deasserted, and wait for the next CS assertion to start a transaction - on some MCUs, deasserting the SPI CS signal can be delayed after the end of a SPI transaction - when this happens, the slave may mis-detect this as the start of a new SPI transaction (since CS is still low), resulting in data loss - removed extra GPIO pin config for C5 --- idf_component.yml | 2 +- slave/main/Kconfig.projbuild | 11 +++++++- slave/main/spi_slave_api.c | 53 ++++++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/idf_component.yml b/idf_component.yml index 909043b2..39ea8038 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.3.5" +version: "1.4.0" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index 0ebdd9fb..7a7e48e5 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -219,6 +219,16 @@ menu "Example Configuration" Host uses this pin to reset the slave ESP. To re-use ESP 'RST' or 'EN' GPIO, set value to -1 endmenu + config ESP_SPI_DEASSERT_HS_ON_CS + bool "Deassert Handshake when SPI CS is deasserted" + default y + help + Deassert Handshake and prepare a new SPI transaction only after + CS has been deasserted. This helps prevent data loss with MCUs + that delay deasserting CS after the end of a SPI transaction + by prematurely starting a new slave SPI transaction + since CS is detected by the slave as still asserted. + config ESP_SPI_TX_Q_SIZE int "ESP to Host SPI queue size" default 10 if IDF_TARGET_ESP32 @@ -455,7 +465,6 @@ menu "Example Configuration" int "Slave GPIO pin for Data Ready" default 2 if ESP_HOST_DEV_BOARD_P4_FUNC_BOARD default 0 if IDF_TARGET_ESP32C5 - default 13 if IDF_TARGET_ESP32C5 default 11 help Slave GPIO pin for indicating host that SPI slave has data to be read by host diff --git a/slave/main/spi_slave_api.c b/slave/main/spi_slave_api.c index b51903e8..d00eaed4 100644 --- a/slave/main/spi_slave_api.c +++ b/slave/main/spi_slave_api.c @@ -50,6 +50,13 @@ static const char TAG[] = "SPI_DRIVER"; #define SPI_RX_QUEUE_SIZE CONFIG_ESP_SPI_RX_Q_SIZE #define SPI_TX_QUEUE_SIZE CONFIG_ESP_SPI_TX_Q_SIZE +// de-assert HS signal on CS, instead of at end of transaction +#if defined(CONFIG_ESP_SPI_DEASSERT_HS_ON_CS) +#define HS_DEASSERT_ON_CS (1) +#else +#define HS_DEASSERT_ON_CS (0) +#endif + /* By default both Handshake and Data Ready used Active High, * unless configured otherwise. * For Active low, set value as 0 */ @@ -80,7 +87,11 @@ static const char TAG[] = "SPI_DRIVER"; #define GPIO_MASK_DATA_READY (1ULL << GPIO_DATA_READY) #define GPIO_MASK_HANDSHAKE (1ULL << GPIO_HANDSHAKE) +#if HS_DEASSERT_ON_CS +#define H_CS_INTR_TO_CLEAR_HS GPIO_INTR_ANYEDGE +#else #define H_CS_INTR_TO_CLEAR_HS GPIO_INTR_NEGEDGE +#endif #if H_HANDSHAKE_ACTIVE_HIGH #define H_HS_VAL_ACTIVE GPIO_OUT_W1TS_REG @@ -106,6 +117,9 @@ static interface_context_t context; static interface_handle_t if_handle_g; static SemaphoreHandle_t spi_tx_sem; static SemaphoreHandle_t spi_rx_sem; +#if HS_DEASSERT_ON_CS +static SemaphoreHandle_t wait_cs_deassert_sem; +#endif static QueueHandle_t spi_rx_queue[MAX_PRIORITY_QUEUES]; static QueueHandle_t spi_tx_queue[MAX_PRIORITY_QUEUES]; #if DUMMY_TRANS_DESIGN @@ -380,9 +394,10 @@ static void IRAM_ATTR spi_post_trans_cb(spi_slave_transaction_t *trans) xSemaphoreGive(spi_sema); } #endif +#if !HS_DEASSERT_ON_CS /* Clear handshake line */ reset_handshake_gpio(); - +#endif } static uint8_t * get_next_tx_buffer(uint32_t *len) @@ -649,8 +664,8 @@ static void spi_transaction_post_process_task(void* pvParameters) /* Await transmission result, after any kind of transmission a new packet * (dummy or real) must be placed in SPI slave */ - spi_slave_get_trans_result(ESP_SPI_CONTROLLER, &spi_trans, - portMAX_DELAY); + ESP_ERROR_CHECK(spi_slave_get_trans_result(ESP_SPI_CONTROLLER, &spi_trans, + portMAX_DELAY)); #if DUMMY_TRANS_DESIGN if (spi_trans->tx_buffer) { header = (struct esp_payload_header *) spi_trans->tx_buffer; @@ -677,6 +692,17 @@ static void spi_transaction_post_process_task(void* pvParameters) if (ret == pdTRUE) queue_dummy_transaction(); #else + +#if HS_DEASSERT_ON_CS + /* Wait until CS has been deasserted before we queue a new transaction. + * + * Some MCUs delay deasserting CS at the end of a transaction. + * If we queue a new transaction without waiting for CS to deassert, + * the slave SPI can start (since CS is still asserted), and data is lost + * as host is not expecting any data. + */ + xSemaphoreTake(wait_cs_deassert_sem, portMAX_DELAY); +#endif /* Queue new transaction to get ready as soon as possible */ queue_next_transaction(); assert(spi_trans); @@ -713,7 +739,19 @@ static void spi_transaction_post_process_task(void* pvParameters) static void IRAM_ATTR gpio_disable_hs_isr_handler(void* arg) { +#if HS_DEASSERT_ON_CS + int level = gpio_get_level(GPIO_CS); + if (level == 0) { + /* CS is asserted, disable HS */ + reset_handshake_gpio(); + } else { + /* Last transaction complete, populate next one */ + if (wait_cs_deassert_sem) + xSemaphoreGive(wait_cs_deassert_sem); + } +#else reset_handshake_gpio(); +#endif } static void register_hs_disable_pin(uint32_t gpio_num) @@ -824,6 +862,11 @@ static interface_handle_t * esp_spi_init(void) gpio_set_drive_capability(GPIO_MISO, GPIO_DRIVE_CAP_3); gpio_set_pull_mode(GPIO_MISO, GPIO_PULLDOWN_ONLY); +#if HS_DEASSERT_ON_CS + wait_cs_deassert_sem = xSemaphoreCreateBinary(); + assert(wait_cs_deassert_sem!= NULL); + ret = xSemaphoreTake(wait_cs_deassert_sem, 0); +#endif memset(&if_handle_g, 0, sizeof(if_handle_g)); if_handle_g.state = INIT; @@ -930,11 +973,11 @@ static int32_t esp_spi_write(interface_handle_t *handle, interface_buffer_handle else xQueueSend(spi_tx_queue[PRIO_Q_OTHERS], &tx_buf_handle, portMAX_DELAY); - xSemaphoreGive(spi_tx_sem); - /* indicate waiting data on ready pin */ set_dataready_gpio(); + xSemaphoreGive(spi_tx_sem); + return buf_handle->payload_len; } From 53cf719427891cd8687168549eda278319920587 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Wed, 9 Apr 2025 13:42:39 +0800 Subject: [PATCH 11/44] bugfix(fg_mempool): Allocate mempool from DMA capable memory - Make sure mempool memory is allocated from DMA capable memory, even if PSRAM is enabled --- idf_component.yml | 2 +- slave/main/mempool.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/idf_component.yml b/idf_component.yml index 39ea8038..9e700a06 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.4.0" +version: "1.4.1" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/mempool.c b/slave/main/mempool.c index 5ed61662..098f2723 100644 --- a/slave/main/mempool.c +++ b/slave/main/mempool.c @@ -33,7 +33,7 @@ struct hosted_mempool * hosted_mempool_create(void *pre_allocated_mem, if (!pre_allocated_mem) { /* no pre-allocated mem, allocate new */ - heap = (uint8_t *)CALLOC(1, MEMPOOL_ALIGNED(OS_MEMPOOL_BYTES( + heap = (uint8_t *)MEM_ALLOC(MEMPOOL_ALIGNED(OS_MEMPOOL_BYTES( num_blocks,block_size))); if (!heap) { ESP_LOGE(TAG, "mempool create failed, no mem\n"); From 69f81b5bc2ff5a4817d74b25e9ab6b6c320ba2ee Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Fri, 28 Mar 2025 15:31:58 +0800 Subject: [PATCH 12/44] enhance(support_sdio_slots) Add SDIO slot configuration - Kconfig option to select slot - adjust SDIO GPIOs used based on selected SDMMC slot - added a workaround for a ESP-IDF check on SDMMC slot 0 that prevented proper operation in 1-bit SDIO mode - tested both slots on ESP32-P4 Dev Board: - slot 0 with ESP32 on a SDMMC card: OK - slot 1 with ESP32C6 on-board: OK - compiled for ESP32: OK - compiled for ESP32-S3: OK --- Kconfig | 164 +++++++++++++++++- host/api/include/esp_hosted_config.h | 16 +- .../api/include/esp_hosted_transport_config.h | 1 - host/drivers/transport/sdio/sdio_drv.c | 10 +- host/port/src/sdio_wrapper.c | 58 ++++++- 5 files changed, 232 insertions(+), 17 deletions(-) diff --git a/Kconfig b/Kconfig index 9509d7d5..2e3c225c 100644 --- a/Kconfig +++ b/Kconfig @@ -407,6 +407,46 @@ ESP32XX_SPI_CLK_FREQ_RANGE_MAX := 40 endchoice + choice + prompt "SDIO Slot To Use" + default ESP_HOSTED_SDIO_SLOT_1 + help + On the ESP32-P4 EV Board: + - Slot 0 connects to the MicroSD Card slot + - Slot 1 connects to the on-board ESP32-C6 + For the ESP32, Slot 0 is usually occupied by SPI Flash and not usable for SDIO. + For the ESP32-P4, Slot 0 is IOMUXed and GPIO values cannot be changed + + config ESP_HOSTED_SDIO_SLOT_0 + depends on IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S3 + bool "Slot 0" + + config ESP_HOSTED_SDIO_SLOT_1 + bool "Slot 1" + endchoice + + config ESP_HOSTED_SDIO_SLOT + int + default 0 if ESP_HOSTED_SDIO_SLOT_0 + default 1 if ESP_HOSTED_SDIO_SLOT_1 + + config ESP_HOSTED_SD_PWR_CTRL_LDO_INTERNAL_IO + depends on SOC_SDMMC_IO_POWER_EXTERNAL + bool "SDIO power supply comes from internal LDO IO (READ HELP!)" + default n + help + Only needed when the SDIO module is connected to specific IO pins which can be used for high-speed SDIO. + Please read the schematic first and check if the SD VDD is connected to any internal LDO output. + Unselect this option if the SDIO is powered by an external power supply. + For ESP32-P4 EV Board, SDMMC slot 0 may require internal LDO output. + + config ESP_HOSTED_SD_PWR_CTRL_LDO_IO_ID + depends on SOC_SDMMC_IO_POWER_EXTERNAL && ESP_HOSTED_SD_PWR_CTRL_LDO_INTERNAL_IO + int "LDO ID" + default 4 if IDF_TARGET_ESP32P4 + help + Please check your schematic first and input your LDO ID. + config ESP_HOSTED_SDIO_GPIO_RESET_SLAVE int "GPIO pin for Reseting slave ESP" default 54 if IDF_TARGET_ESP32P4 @@ -446,7 +486,18 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Optimize SDIO CLK by increasing till host practically can support. Clock frequency for ESP32-P4 as host <= 40MHz" - config ESP_HOSTED_SDIO_PIN_CMD +### *START* GPIO SDIO pin configurations for Slot 0 and 1 + config ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "CMD GPIO number" + default 47 if IDF_TARGET_ESP32S3 + range 44 44 if IDF_TARGET_ESP32P4 + range 15 15 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "CMD GPIO number" default 47 if IDF_TARGET_ESP32S3 default 19 if IDF_TARGET_ESP32P4 @@ -454,7 +505,17 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Value can only be configured for some targets. Displayed always for reference." - config ESP_HOSTED_SDIO_PIN_CLK + config ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "CLK GPIO number" + default 19 if IDF_TARGET_ESP32S3 + range 43 43 if IDF_TARGET_ESP32P4 + range 14 14 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "CLK GPIO number" default 19 if IDF_TARGET_ESP32S3 default 18 if IDF_TARGET_ESP32P4 @@ -462,7 +523,17 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Value can only be configured for some targets. Displayed always for reference." - config ESP_HOSTED_SDIO_PIN_D0 + config ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "D0 GPIO number" + default 13 if IDF_TARGET_ESP32S3 + range 39 39 if IDF_TARGET_ESP32P4 + range 2 2 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "D0 GPIO number" default 13 if IDF_TARGET_ESP32S3 default 14 if IDF_TARGET_ESP32P4 @@ -471,7 +542,17 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 "Value can only be configured for some targets. Displayed always for reference." if ESP_HOSTED_SDIO_4_BIT_BUS - config ESP_HOSTED_SDIO_PRIV_PIN_D1_4BIT_BUS + config ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "D1 GPIO number" + default 35 if IDF_TARGET_ESP32S3 + range 40 40 if IDF_TARGET_ESP32P4 + range 4 4 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number" default 35 if IDF_TARGET_ESP32S3 default 15 if IDF_TARGET_ESP32P4 @@ -479,7 +560,17 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Value can only be configured for some targets. Displayed always for reference." - config ESP_HOSTED_SDIO_PIN_D2 + config ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "D2 GPIO number" + default 20 if IDF_TARGET_ESP32S3 + range 41 41 if IDF_TARGET_ESP32P4 + range 12 12 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "D2 GPIO number" default 20 if IDF_TARGET_ESP32S3 default 16 if IDF_TARGET_ESP32P4 @@ -487,7 +578,17 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Value can only be configured for some targets. Displayed always for reference." - config ESP_HOSTED_SDIO_PIN_D3 + config ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "D3 GPIO number" + default 9 if IDF_TARGET_ESP32S3 + range 42 42 if IDF_TARGET_ESP32P4 + range 13 13 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + + config ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "D3 GPIO number" default 9 if IDF_TARGET_ESP32S3 default 17 if IDF_TARGET_ESP32P4 @@ -497,7 +598,16 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 endif if !ESP_HOSTED_SDIO_4_BIT_BUS - config ESP_HOSTED_SDIO_PRIV_PIN_D1_1BIT_BUS + config ESP_HOSTED_PRIV_SDIO_PIN_D1_1BIT_BUS_SLOT_0 + depends on ESP_HOSTED_SDIO_SLOT_0 + int "D1 GPIO number (Interrupt Line)" + default 35 if IDF_TARGET_ESP32S3 + range 40 40 if IDF_TARGET_ESP32P4 + range 4 4 if IDF_TARGET_ESP32 + help + "Value can only be configured for some targets. Displayed always for reference." + config ESP_HOSTED_PRIV_SDIO_PIN_D1_1BIT_BUS_SLOT_1 + depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number (Interrupt Line)" default 35 if IDF_TARGET_ESP32S3 default 15 if IDF_TARGET_ESP32P4 @@ -505,6 +615,46 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Value can only be configured for some targets. Displayed always for reference." endif +### *END* GPIO SDIO pin configurations for Slot 0 and 1 + + config ESP_HOSTED_SDIO_PIN_CMD + int + default ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + + config ESP_HOSTED_SDIO_PIN_CLK + int + default ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + + config ESP_HOSTED_SDIO_PIN_D0 + int + default ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + + if ESP_HOSTED_SDIO_4_BIT_BUS + config ESP_HOSTED_SDIO_PRIV_PIN_D1_4BIT_BUS + int + default ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + + config ESP_HOSTED_SDIO_PIN_D2 + int + default ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + + config ESP_HOSTED_SDIO_PIN_D3 + int + default ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + endif + + if !ESP_HOSTED_SDIO_4_BIT_BUS + config ESP_HOSTED_SDIO_PRIV_PIN_D1_1BIT_BUS + int + default ESP_HOSTED_PRIV_SDIO_PIN_D1_1BIT_BUS_SLOT_0 if ESP_HOSTED_SDIO_SLOT_0 + default ESP_HOSTED_PRIV_SDIO_PIN_D1_1BIT_BUS_SLOT_1 if ESP_HOSTED_SDIO_SLOT_1 + endif config ESP_HOSTED_SDIO_PIN_D1 int diff --git a/host/api/include/esp_hosted_config.h b/host/api/include/esp_hosted_config.h index f1053e1a..570a17aa 100644 --- a/host/api/include/esp_hosted_config.h +++ b/host/api/include/esp_hosted_config.h @@ -148,7 +148,7 @@ so feel free to change these if needed. #define H_SDIO_CLOCK_FREQ_KHZ CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ #define H_SDIO_BUS_WIDTH CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH -#define H_SDMMC_HOST_SLOT SDMMC_HOST_SLOT_1 +#define H_SDMMC_HOST_SLOT CONFIG_ESP_HOSTED_SDIO_SLOT #ifdef H_SDIO_SOC_USE_GPIO_MATRIX #define H_SDIO_PIN_CLK CONFIG_ESP_HOSTED_SDIO_PIN_CLK @@ -379,6 +379,20 @@ enum { #define H_TEST_RAW_TP_DIR (ESP_TEST_RAW_TP_NONE) #endif +/* ---------------------- ESP-IDF Specific Config start -------------------- */ +/* This section is for ESP-IDF specific support. + * Can be ignored on other hosts MCUs. + */ + +/* Controls whether an Internal LDO powers the SDIO connection */ +#if CONFIG_ESP_HOSTED_SD_PWR_CTRL_LDO_INTERNAL_IO +#define H_SDIO_PWR_CTRL_LDO 1 +#define H_SDIO_PWR_CTRL_LDO_ID CONFIG_ESP_HOSTED_SD_PWR_CTRL_LDO_IO_ID +#else +#define H_SDIO_PWR_CTRL_LDO 0 +#endif + +/* ---------------------- ESP-IDF Specific Config end ---------------------- */ esp_err_t esp_hosted_set_default_config(void); bool esp_hosted_is_config_valid(void); diff --git a/host/api/include/esp_hosted_transport_config.h b/host/api/include/esp_hosted_transport_config.h index 17f31ee2..fd03de33 100644 --- a/host/api/include/esp_hosted_transport_config.h +++ b/host/api/include/esp_hosted_transport_config.h @@ -131,7 +131,6 @@ struct esp_hosted_transport_config { .iomux_enable = false, \ } - #define INIT_DEFAULT_HOST_SDIO_IOMUX_CONFIG() \ (struct esp_hosted_sdio_config) { \ .clock_freq_khz = H_SDIO_CLOCK_FREQ_KHZ, \ diff --git a/host/drivers/transport/sdio/sdio_drv.c b/host/drivers/transport/sdio/sdio_drv.c index 363c89eb..1bc025f0 100644 --- a/host/drivers/transport/sdio/sdio_drv.c +++ b/host/drivers/transport/sdio/sdio_drv.c @@ -303,11 +303,14 @@ static int sdio_get_len_from_slave(uint32_t *rx_size, bool is_lock_needed) #endif +#define MAX_BUFF_FETCH_PERIODICITY 30000 + static int sdio_is_write_buffer_available(uint32_t buf_needed) { static uint32_t buf_available = 0; uint8_t retry = MAX_WRITE_BUF_RETRIES; uint32_t max_retry_sdio_not_responding = 2; + uint32_t interval_us = 400; /*If buffer needed are less than buffer available then only read for available buffer number from slave*/ @@ -330,9 +333,10 @@ static int sdio_is_write_buffer_available(uint32_t buf_needed) ESP_LOGV(TAG, "Retry get write buffers %d", retry); retry--; - if (retry < MAX_WRITE_BUF_RETRIES/2) - g_h.funcs->_h_msleep(1); - + g_h.funcs->_h_usleep(interval_us); + if (interval_us < MAX_BUFF_FETCH_PERIODICITY) { + interval_us += 400; + } continue; } break; diff --git a/host/port/src/sdio_wrapper.c b/host/port/src/sdio_wrapper.c index a5ff1f81..25fa687d 100644 --- a/host/port/src/sdio_wrapper.c +++ b/host/port/src/sdio_wrapper.c @@ -22,6 +22,13 @@ #include "esp_hosted_config.h" #include "esp_hosted_transport_config.h" +#if H_SDIO_PWR_CTRL_LDO +#include "sd_pwr_ctrl_by_on_chip_ldo.h" +#endif + +#include "soc/sdmmc_pins.h" +#include "hal/sdmmc_ll.h" + #include "esp_log.h" DEFINE_LOG_TAG(sdio_wrapper); @@ -50,6 +57,42 @@ static sdmmc_context_t context = { 0 }; static void * sdio_bus_lock; +// workarounds for known ESP-IDF SDMMC issues +static void hosted_sdio_workaround(int slot, sdmmc_slot_config_t *slot_config) +{ + if (slot == 0) { +#if !SDMMC_LL_SLOT_SUPPORT_GPIO_MATRIX(0) + /* workaround for 1-bit mode on Slot 0 with IOMUX only pins: + * set gpio pins D2, D3 to pass sdmmc_host.c->sdmmc_host_init_slot() IOMUX GPIO checking + */ + if (slot_config->width == 1) { + ESP_LOGW(TAG, "workaround: setting D2-D3 in 1 bit mode for slot %d", slot); + slot_config->d2 = SDMMC_SLOT0_IOMUX_PIN_NUM_D2; + slot_config->d3 = SDMMC_SLOT0_IOMUX_PIN_NUM_D3; + } +#endif + } +} + +static bool hosted_sdio_enable_ldo(sdmmc_host_t *config) +{ +#if H_SDIO_PWR_CTRL_LDO + // enable LDO Power for slot, if required + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = H_SDIO_PWR_CTRL_LDO_ID, + }; + sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; + + esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return false; + } + config->pwr_ctrl_handle = pwr_ctrl_handle; +#endif + return true; +} + static esp_err_t hosted_sdio_print_cis_information(sdmmc_card_t* card) { uint8_t cis_buffer[CIS_BUFFER_SIZE]; @@ -299,6 +342,8 @@ void * hosted_sdio_init(void) slot_config.d3 = context.config.pin_d3.pin; #endif + hosted_sdio_workaround(context.config.slot, &slot_config); + res = sdmmc_host_init_slot(context.config.slot, &slot_config); if (res != ESP_OK) { ESP_LOGE(TAG, "init SDMMC Host slot %d failed", H_SDMMC_HOST_SLOT); @@ -325,13 +370,16 @@ int hosted_sdio_card_init(void *ctx) struct esp_hosted_sdio_config *sdio_config = &context->config; sdmmc_host_t config = SDMMC_HOST_DEFAULT(); + config.slot = sdio_config->slot; // override default slot set + + if (!hosted_sdio_enable_ldo(&config)) { + goto fail; + } - if (sdio_config->bus_width == 4) - config.flags = SDMMC_HOST_FLAG_4BIT; - else - config.flags = SDMMC_HOST_FLAG_1BIT; config.max_freq_khz = sdio_config->clock_freq_khz; - ESP_LOGI(TAG, "SDIO master: Data-Lines: %d-bit Freq(KHz)[%u KHz]", sdio_config->bus_width==4? 4:1, + ESP_LOGI(TAG, "SDIO master: Slot %d, Data-Lines: %d-bit Freq(KHz)[%u KHz]", + config.slot, + sdio_config->bus_width==4? 4:1, config.max_freq_khz); if (sdio_config->bus_width == 4) { ESP_LOGI(TAG, "GPIOs: CLK[%u] CMD[%u] D0[%u] D1[%u] D2[%u] D3[%u] Slave_Reset[%u]", From 5854c65ba7e22bfde0862c94c81ca9f8bab1d3d0 Mon Sep 17 00:00:00 2001 From: Tim Koczwara Date: Wed, 16 Apr 2025 09:54:33 +0200 Subject: [PATCH 13/44] bugfix(reset_config) Fixes the usage of sdkconfig to switch reset active high/low - Closes https://github.com/espressif/esp-hosted-mcu/issues/44 --- host/api/include/esp_hosted_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/api/include/esp_hosted_config.h b/host/api/include/esp_hosted_config.h index 570a17aa..4c974824 100644 --- a/host/api/include/esp_hosted_config.h +++ b/host/api/include/esp_hosted_config.h @@ -331,7 +331,7 @@ enum { #define H_RESET_ACTIVE_HIGH 1 #endif -#ifdef H_RESET_ACTIVE_HIGH +#if H_RESET_ACTIVE_HIGH #define H_RESET_VAL_ACTIVE H_GPIO_HIGH #define H_RESET_VAL_INACTIVE H_GPIO_LOW #else From 6b565508d403fe437c9d2b1da0bafec6ef764d07 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 29 Apr 2025 11:14:39 +0800 Subject: [PATCH 14/44] feat(code_reorg) Moved ESP porting layer - bumped version to 2.0.0 - preparation for adding porting code for other MCUs - move code from `host/port` to `host/port/esp/freertos` - moved hosted api for OTA update into porting layer - code to fetch OTA is system dependent - updated documentation on doing OTA - tested by building for ESP32-P4, and fetching OTA to update co-processor - works as expected --- CMakeLists.txt | 17 +- docs/sdio.md | 2 +- docs/spi_full_duplex.md | 2 +- docs/spi_half_duplex.md | 2 +- docs/uart.md | 2 +- host/api/include/esp_hosted_api.h | 2 +- host/api/include/esp_hosted_ota.h | 35 +++ host/api/src/esp_hosted_api.c | 5 - host/drivers/rpc/wrap/rpc_wrap.c | 211 --------------- host/drivers/rpc/wrap/rpc_wrap.h | 4 + .../include/esp_hosted_wifi_config.h | 0 .../{ => esp/freertos}/include/os_header.h | 0 .../{ => esp/freertos}/include/os_wrapper.h | 0 .../{ => esp/freertos}/include/sdio_wrapper.h | 0 .../freertos}/include/spi_hd_wrapper.h | 0 .../{ => esp/freertos}/include/spi_wrapper.h | 0 .../{ => esp/freertos}/include/uart_wrapper.h | 0 .../freertos}/src/esp_hosted_config.c | 0 host/port/esp/freertos/src/esp_hosted_ota.c | 244 ++++++++++++++++++ .../src/esp_hosted_transport_config.c | 0 host/port/{ => esp/freertos}/src/os_wrapper.c | 0 .../{ => esp/freertos}/src/sdio_wrapper.c | 0 .../{ => esp/freertos}/src/spi_hd_wrapper.c | 0 .../port/{ => esp/freertos}/src/spi_wrapper.c | 0 .../{ => esp/freertos}/src/uart_wrapper.c | 0 idf_component.yml | 2 +- 26 files changed, 299 insertions(+), 229 deletions(-) create mode 100644 host/api/include/esp_hosted_ota.h rename host/port/{ => esp/freertos}/include/esp_hosted_wifi_config.h (100%) rename host/port/{ => esp/freertos}/include/os_header.h (100%) rename host/port/{ => esp/freertos}/include/os_wrapper.h (100%) rename host/port/{ => esp/freertos}/include/sdio_wrapper.h (100%) rename host/port/{ => esp/freertos}/include/spi_hd_wrapper.h (100%) rename host/port/{ => esp/freertos}/include/spi_wrapper.h (100%) rename host/port/{ => esp/freertos}/include/uart_wrapper.h (100%) rename host/port/{ => esp/freertos}/src/esp_hosted_config.c (100%) create mode 100644 host/port/esp/freertos/src/esp_hosted_ota.c rename host/port/{ => esp/freertos}/src/esp_hosted_transport_config.c (100%) rename host/port/{ => esp/freertos}/src/os_wrapper.c (100%) rename host/port/{ => esp/freertos}/src/sdio_wrapper.c (100%) rename host/port/{ => esp/freertos}/src/spi_hd_wrapper.c (100%) rename host/port/{ => esp/freertos}/src/spi_wrapper.c (100%) rename host/port/{ => esp/freertos}/src/uart_wrapper.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7054dfc3..41e4423f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,9 @@ if(CONFIG_ESP_HOSTED_ENABLED) list(APPEND COMPONENT_SRCS "${common_dir}/protobuf-c/protobuf-c/protobuf-c.c" "${common_dir}/proto/esp_hosted_rpc.pb-c.c" ) list(APPEND COMPONENT_ADD_INCLUDEDIRS "${common_dir}" "${common_dir}/log" "${common_dir}/rpc" "${common_dir}/transport" "${common_dir}/protobuf-c" "${common_dir}/proto" ) + # host ESP32 specific files + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/esp_hosted_ota.c") + # bt (NimBLE) ### TODO config for HCI over UART list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/drivers/bt") @@ -55,19 +58,19 @@ if(CONFIG_ESP_HOSTED_ENABLED) endif() # config files - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/port/include") - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/esp_hosted_config.c" "${host_dir}/port/src/esp_hosted_transport_config.c") + list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/port/esp/freertos/include") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/esp_hosted_config.c" "${host_dir}/port/esp/freertos/src/esp_hosted_transport_config.c") # transport port files - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/os_wrapper.c") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/os_wrapper.c") if(CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/sdio_wrapper.c") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/sdio_wrapper.c") elseif(CONFIG_ESP_HOSTED_SPI_HD_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/spi_hd_wrapper.c") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/spi_hd_wrapper.c") elseif(CONFIG_ESP_HOSTED_SPI_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/spi_wrapper.c") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/spi_wrapper.c") elseif(CONFIG_ESP_HOSTED_UART_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/src/uart_wrapper.c") + list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/uart_wrapper.c") endif() endif() diff --git a/docs/sdio.md b/docs/sdio.md index 1d48c035..4bd7c85b 100644 --- a/docs/sdio.md +++ b/docs/sdio.md @@ -288,7 +288,7 @@ You can re-use your existing web server or create a new locally for testing. Bel 5. On the **host side**, use the `esp_hosted_slave_ota` function to initiate the OTA update: ```c - #include "esp_hosted_api.h" + #include "esp_hosted.h" const char* image_url = "http://example.com/path/to/network_adapter.bin"; //web server full url esp_err_t ret = esp_hosted_slave_ota(image_url); diff --git a/docs/spi_full_duplex.md b/docs/spi_full_duplex.md index 3846e4ec..6e62b978 100644 --- a/docs/spi_full_duplex.md +++ b/docs/spi_full_duplex.md @@ -343,7 +343,7 @@ You can re-use your existing web server or create a new locally for testing. Bel 5. On the **host side**, use the `esp_hosted_slave_ota` function to initiate the OTA update: ```c - #include "esp_hosted_api.h" + #include "esp_hosted.h" const char* image_url = "http://example.com/path/to/network_adapter.bin"; //web server full url esp_err_t ret = esp_hosted_slave_ota(image_url); diff --git a/docs/spi_half_duplex.md b/docs/spi_half_duplex.md index 1b524321..64967538 100644 --- a/docs/spi_half_duplex.md +++ b/docs/spi_half_duplex.md @@ -580,7 +580,7 @@ You can re-use your existing web server or create a new locally for testing. Bel 5. On the **host side**, use the `esp_hosted_slave_ota` function to initiate the OTA update: ```c - #include "esp_hosted_api.h" + #include "esp_hosted.h" const char* image_url = "http://example.com/path/to/network_adapter.bin"; //web server full url esp_err_t ret = esp_hosted_slave_ota(image_url); diff --git a/docs/uart.md b/docs/uart.md index 725139eb..6e8ce079 100644 --- a/docs/uart.md +++ b/docs/uart.md @@ -220,7 +220,7 @@ You can re-use your existing web server or create a new locally for testing. Bel 5. On the **host side**, use the `esp_hosted_slave_ota` function to initiate the OTA update: ```c - #include "esp_hosted_api.h" + #include "esp_hosted.h" const char* image_url = "http://example.com/path/to/network_adapter.bin"; //web server full url esp_err_t ret = esp_hosted_slave_ota(image_url); diff --git a/host/api/include/esp_hosted_api.h b/host/api/include/esp_hosted_api.h index be50610d..69b83795 100644 --- a/host/api/include/esp_hosted_api.h +++ b/host/api/include/esp_hosted_api.h @@ -17,6 +17,7 @@ extern "C" { #include "esp_wifi.h" #include "esp_wifi_remote.h" #include "esp_hosted_api_types.h" +#include "esp_hosted_ota.h" /** Exported variables **/ #define ESP_HOSTED_CHANNEL_CONFIG_DEFAULT() { \ @@ -84,7 +85,6 @@ esp_err_t esp_wifi_remote_set_max_tx_power(int8_t power); esp_err_t esp_wifi_remote_get_max_tx_power(int8_t *power); esp_err_t esp_wifi_remote_sta_get_negotiated_phymode(wifi_phy_mode_t *phymode); esp_err_t esp_wifi_remote_sta_get_aid(uint16_t *aid); -esp_err_t esp_hosted_slave_ota(const char* image_url); esp_err_t esp_hosted_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info); #if H_WIFI_DUALBAND_SUPPORT diff --git a/host/api/include/esp_hosted_ota.h b/host/api/include/esp_hosted_ota.h new file mode 100644 index 00000000..2a9fffab --- /dev/null +++ b/host/api/include/esp_hosted_ota.h @@ -0,0 +1,35 @@ +/* +* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* APIs to do OTA updates of the co-processor + * + * Note: This API is platform dependent + * + * Add additional APIs as required based on how the OTA binary is to + * be fetched. + * + * Source for the API should be in host/port//... + * + * Procedure used by APIs to do OTA update: + * 1. Fetch and prepare OTA binary + * 2. Call rpc_ota_begin() to start OTA + * 3. Repeatedly call rpc_ota_write() with a continuous chunk of OTA data + * 4. Call rpc_ota_end() + * + */ + +#ifndef __ESP_HOSTED_OTA_H__ +#define __ESP_HOSTED_OTA_H__ + +#ifdef ESP_PLATFORM +// OTA API for ESP-IDF +#include "esp_err.h" + +/* Fetch OTA image from a web server (image_url) */ +esp_err_t esp_hosted_slave_ota(const char* image_url); +#endif + +#endif /*__ESP_HOSTED_OTA_H__*/ diff --git a/host/api/src/esp_hosted_api.c b/host/api/src/esp_hosted_api.c index f5a492cf..fb877f87 100644 --- a/host/api/src/esp_hosted_api.c +++ b/host/api/src/esp_hosted_api.c @@ -442,11 +442,6 @@ esp_err_t esp_wifi_remote_get_bandwidths(wifi_interface_t ifx, wifi_bandwidths_t } #endif -esp_err_t esp_hosted_slave_ota(const char* image_url) -{ - return rpc_ota(image_url); -} - esp_err_t esp_hosted_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info) { return rpc_get_coprocessor_fwversion(ver_info); diff --git a/host/drivers/rpc/wrap/rpc_wrap.c b/host/drivers/rpc/wrap/rpc_wrap.c index 2a2b59ed..ff0e2db3 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.c +++ b/host/drivers/rpc/wrap/rpc_wrap.c @@ -28,13 +28,11 @@ #include "rpc_wrap.h" #include "esp_hosted_rpc.h" #include "esp_log.h" -#include "esp_http_client.h" #include "esp_hosted_wifi_config.h" #include "esp_hosted_api.h" #include "esp_hosted_transport.h" DEFINE_LOG_TAG(rpc_wrap); -static char* OTA_TAG = "h_ota"; uint8_t restart_after_slave_ota = 0; @@ -852,215 +850,6 @@ int rpc_ota_end(void) return rpc_rsp_callback(resp); } -#if !OTA_FROM_WEB_URL -/* This assumes full slave binary is present locally */ -int rpc_ota(char* image_path) -{ - FILE* f = NULL; - char ota_chunk[CHUNK_SIZE] = {0}; - int ret = rpc_ota_begin(); - if (ret == SUCCESS) { - f = fopen(image_path,"rb"); - if (f == NULL) { - ESP_LOGE(OTA_TAG, "Failed to open file %s", image_path); - return FAILURE; - } else { - ESP_LOGV(OTA_TAG, "Success in opening %s file", image_path); - } - while (!feof(f)) { - fread(&ota_chunk, CHUNK_SIZE, 1, f); - ret = rpc_ota_write((uint8_t* )&ota_chunk, CHUNK_SIZE); - if (ret) { - ESP_LOGE(OTA_TAG, "OTA procedure failed!!"); - /* TODO: Do we need to do OTA end irrespective of success/failure? */ - rpc_ota_end(); - return FAILURE; - } - } - ret = rpc_ota_end(); - if (ret) { - return FAILURE; - } - } else { - return FAILURE; - } - ESP_LOGE(OTA_TAG, "ESP32 will restart after 5 sec"); - return SUCCESS; - ESP_LOGE(OTA_TAG, "For OTA, user need to integrate HTTP client lib and then invoke OTA"); - return FAILURE; -} -#else -uint8_t http_err = 0; -static esp_err_t http_client_event_handler(esp_http_client_event_t *evt) -{ - switch(evt->event_id) { - - case HTTP_EVENT_ERROR: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_ERROR"); - http_err = 1; - break; - case HTTP_EVENT_ON_CONNECTED: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_ON_CONNECTED"); - break; - case HTTP_EVENT_HEADER_SENT: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_HEADER_SENT"); - break; - case HTTP_EVENT_ON_HEADER: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); - break; - case HTTP_EVENT_ON_DATA: - /* Nothing to handle here */ - break; - case HTTP_EVENT_ON_FINISH: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_ON_FINISH"); - break; - case HTTP_EVENT_DISCONNECTED: - ESP_LOGI(OTA_TAG, "HTTP_EVENT_DISCONNECTED"); - break; - case HTTP_EVENT_REDIRECT: - ESP_LOGW(TAG, "HTTP_EVENT_REDIRECT"); - break; - } - - return ESP_OK; -} - -static esp_err_t _rpc_ota(const char* image_url) -{ - uint8_t *ota_chunk = NULL; - esp_err_t err = 0; - int data_read = 0; - int ota_failed = 0; - - if (image_url == NULL) { - ESP_LOGE(TAG, "Invalid image URL"); - return FAILURE; - } - - /* Initialize HTTP client configuration */ - esp_http_client_config_t config = { - .url = image_url, - .timeout_ms = 5000, - .event_handler = http_client_event_handler, - }; - - esp_http_client_handle_t client = esp_http_client_init(&config); - - ESP_LOGI(OTA_TAG, "http_open"); - if ((err = esp_http_client_open(client, 0)) != ESP_OK) { - ESP_LOGE(OTA_TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); - ESP_LOGE(OTA_TAG, "Check if URL is correct and connectable: %s", image_url); - esp_http_client_cleanup(client); - return FAILURE; - } - - if (http_err) { - ESP_LOGE(TAG, "Exiting OTA, due to http failure"); - esp_http_client_close(client); - esp_http_client_cleanup(client); - http_err = 0; - return FAILURE; - } - - ESP_LOGI(OTA_TAG, "http_fetch_headers"); - int64_t content_length = esp_http_client_fetch_headers(client); - if (content_length <= 0) { - ESP_LOGE(OTA_TAG, "HTTP client fetch headers failed"); - ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %"PRId64, - esp_http_client_get_status_code(client), - esp_http_client_get_content_length(client)); - esp_http_client_close(client); - esp_http_client_cleanup(client); - return FAILURE; - } - - ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %"PRId64, - esp_http_client_get_status_code(client), - esp_http_client_get_content_length(client)); - - ESP_LOGW(OTA_TAG, "********* Started Slave OTA *******************"); - ESP_LOGI(TAG, "*** Please wait for 5 mins to let slave OTA complete ***"); - - ESP_LOGI(OTA_TAG, "Preparing OTA"); - if ((err = rpc_ota_begin())) { - ESP_LOGW(OTA_TAG, "********* Slave OTA Begin Failed *******************"); - ESP_LOGI(OTA_TAG, "esp_ota_begin failed, error=%s", esp_err_to_name(err)); - esp_http_client_close(client); - esp_http_client_cleanup(client); - return FAILURE; - } - - ota_chunk = (uint8_t*)g_h.funcs->_h_calloc(1, CHUNK_SIZE); - if (!ota_chunk) { - ESP_LOGE(OTA_TAG, "Failed to allocate otachunk mem\n"); - err = -ENOMEM; - } - - ESP_LOGI(OTA_TAG, "Starting OTA"); - - if (!err) { - while ((data_read = esp_http_client_read(client, (char*)ota_chunk, CHUNK_SIZE)) > 0) { - - ESP_LOGV(OTA_TAG, "Read image length %d", data_read); - if ((err = rpc_ota_write(ota_chunk, data_read))) { - ESP_LOGI(OTA_TAG, "rpc_ota_write failed"); - ota_failed = err; - break; - } - } - } - - g_h.funcs->_h_free(ota_chunk); - if (err) { - ESP_LOGW(OTA_TAG, "********* Slave OTA Failed *******************"); - ESP_LOGI(OTA_TAG, "esp_ota_write failed, error=%s", esp_err_to_name(err)); - ota_failed = -1; - } - - if (data_read < 0) { - ESP_LOGE(OTA_TAG, "Error: SSL data read error"); - ota_failed = -2; - } - - if ((err = rpc_ota_end())) { - ESP_LOGW(OTA_TAG, "********* Slave OTA Failed *******************"); - ESP_LOGI(OTA_TAG, "esp_ota_end failed, error=%s", esp_err_to_name(err)); - esp_http_client_close(client); - esp_http_client_cleanup(client); - ota_failed = err; - return FAILURE; - } - - esp_http_client_cleanup(client); - if (!ota_failed) { - ESP_LOGW(OTA_TAG, "********* Slave OTA Complete *******************"); - ESP_LOGI(OTA_TAG, "OTA Successful, Slave will restart in while"); - ESP_LOGE(TAG, "Need to restart host after slave OTA is complete, to avoid sync issues"); - sleep(5); - ESP_LOGE(OTA_TAG, "********* Restarting Host **********************"); - restart_after_slave_ota = 1; - esp_restart(); - } - return ota_failed; -} - -esp_err_t rpc_ota(const char* image_url) -{ - uint8_t ota_retry = 2; - int ret = 0; - - do { - ret = _rpc_ota(image_url); - - ota_retry--; - if (ota_retry && ret) - ESP_LOGI(OTA_TAG, "OTA retry left: %u\n", ota_retry); - } while (ota_retry && ret); - - return ret; -} -#endif - esp_err_t rpc_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info) { /* implemented synchronous */ diff --git a/host/drivers/rpc/wrap/rpc_wrap.h b/host/drivers/rpc/wrap/rpc_wrap.h index ebe39125..048e572c 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.h +++ b/host/drivers/rpc/wrap/rpc_wrap.h @@ -84,6 +84,10 @@ esp_err_t rpc_wifi_sta_get_aid(uint16_t *aid); esp_err_t rpc_ota(const char* image_url); esp_err_t rpc_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info); +esp_err_t rpc_ota_begin(void); +esp_err_t rpc_ota_write(uint8_t* ota_data, uint32_t ota_data_len); +esp_err_t rpc_ota_end(void); + #if H_WIFI_DUALBAND_SUPPORT esp_err_t rpc_wifi_set_band(wifi_band_t band); esp_err_t rpc_wifi_get_band(wifi_band_t *band); diff --git a/host/port/include/esp_hosted_wifi_config.h b/host/port/esp/freertos/include/esp_hosted_wifi_config.h similarity index 100% rename from host/port/include/esp_hosted_wifi_config.h rename to host/port/esp/freertos/include/esp_hosted_wifi_config.h diff --git a/host/port/include/os_header.h b/host/port/esp/freertos/include/os_header.h similarity index 100% rename from host/port/include/os_header.h rename to host/port/esp/freertos/include/os_header.h diff --git a/host/port/include/os_wrapper.h b/host/port/esp/freertos/include/os_wrapper.h similarity index 100% rename from host/port/include/os_wrapper.h rename to host/port/esp/freertos/include/os_wrapper.h diff --git a/host/port/include/sdio_wrapper.h b/host/port/esp/freertos/include/sdio_wrapper.h similarity index 100% rename from host/port/include/sdio_wrapper.h rename to host/port/esp/freertos/include/sdio_wrapper.h diff --git a/host/port/include/spi_hd_wrapper.h b/host/port/esp/freertos/include/spi_hd_wrapper.h similarity index 100% rename from host/port/include/spi_hd_wrapper.h rename to host/port/esp/freertos/include/spi_hd_wrapper.h diff --git a/host/port/include/spi_wrapper.h b/host/port/esp/freertos/include/spi_wrapper.h similarity index 100% rename from host/port/include/spi_wrapper.h rename to host/port/esp/freertos/include/spi_wrapper.h diff --git a/host/port/include/uart_wrapper.h b/host/port/esp/freertos/include/uart_wrapper.h similarity index 100% rename from host/port/include/uart_wrapper.h rename to host/port/esp/freertos/include/uart_wrapper.h diff --git a/host/port/src/esp_hosted_config.c b/host/port/esp/freertos/src/esp_hosted_config.c similarity index 100% rename from host/port/src/esp_hosted_config.c rename to host/port/esp/freertos/src/esp_hosted_config.c diff --git a/host/port/esp/freertos/src/esp_hosted_ota.c b/host/port/esp/freertos/src/esp_hosted_ota.c new file mode 100644 index 00000000..a0aae214 --- /dev/null +++ b/host/port/esp/freertos/src/esp_hosted_ota.c @@ -0,0 +1,244 @@ +/* +* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* Current OTA method(s) supported: + * - OTA from a HTTP URL + * + * Procedure: + * 1. Prepare OTA binary + * 2. Call rpc_ota_begin() to start OTA + * 3. Repeatedly call rpc_ota_write() with a continuous chunk of OTA data + * 4. Call rpc_ota_end() + */ + +#include "esp_http_client.h" +#include "esp_log.h" + +#include "rpc_wrap.h" +#include "esp_hosted_ota.h" + +#define CHUNK_SIZE 1400 +#define OTA_FROM_WEB_URL 1 + +static char* TAG = "hosted_ota"; + +#if OTA_FROM_WEB_URL +/* Default: Chunk by chunk transfer using esp http client library */ +uint8_t http_err = 0; +static esp_err_t http_client_event_handler(esp_http_client_event_t *evt) +{ + switch(evt->event_id) { + + case HTTP_EVENT_ERROR: + ESP_LOGI(TAG, "HTTP_EVENT_ERROR"); + http_err = 1; + break; + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + case HTTP_EVENT_HEADER_SENT: + ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + case HTTP_EVENT_ON_HEADER: + ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); + break; + case HTTP_EVENT_ON_DATA: + /* Nothing to handle here */ + break; + case HTTP_EVENT_ON_FINISH: + ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH"); + break; + case HTTP_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); + break; + case HTTP_EVENT_REDIRECT: + ESP_LOGW(TAG, "HTTP_EVENT_REDIRECT"); + break; + } + + return ESP_OK; +} + +static esp_err_t _hosted_ota(const char* image_url) +{ + uint8_t *ota_chunk = NULL; + esp_err_t err = 0; + int data_read = 0; + int ota_failed = 0; + + if (image_url == NULL) { + ESP_LOGE(TAG, "Invalid image URL"); + return ESP_FAIL; + } + + /* Initialize HTTP client configuration */ + esp_http_client_config_t config = { + .url = image_url, + .timeout_ms = 5000, + .event_handler = http_client_event_handler, + }; + + esp_http_client_handle_t client = esp_http_client_init(&config); + + ESP_LOGI(TAG, "http_open"); + if ((err = esp_http_client_open(client, 0)) != ESP_OK) { + ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Check if URL is correct and connectable: %s", image_url); + esp_http_client_cleanup(client); + return ESP_FAIL; + } + + if (http_err) { + ESP_LOGE(TAG, "Exiting OTA, due to http failure"); + esp_http_client_close(client); + esp_http_client_cleanup(client); + http_err = 0; + return ESP_FAIL; + } + + ESP_LOGI(TAG, "http_fetch_headers"); + int64_t content_length = esp_http_client_fetch_headers(client); + if (content_length <= 0) { + ESP_LOGE(TAG, "HTTP client fetch headers failed"); + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %"PRId64, + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + esp_http_client_close(client); + esp_http_client_cleanup(client); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %"PRId64, + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + + ESP_LOGW(TAG, "********* Started Slave OTA *******************"); + ESP_LOGI(TAG, "*** Please wait for 5 mins to let slave OTA complete ***"); + + ESP_LOGI(TAG, "Preparing OTA"); + if ((err = rpc_ota_begin())) { + ESP_LOGW(TAG, "********* Slave OTA Begin Failed *******************"); + ESP_LOGI(TAG, "esp_ota_begin failed, error=%s", esp_err_to_name(err)); + esp_http_client_close(client); + esp_http_client_cleanup(client); + return ESP_FAIL; + } + + ota_chunk = (uint8_t*)g_h.funcs->_h_calloc(1, CHUNK_SIZE); + if (!ota_chunk) { + ESP_LOGE(TAG, "Failed to allocate otachunk mem\n"); + err = -ENOMEM; + } + + ESP_LOGI(TAG, "Starting OTA"); + + if (!err) { + while ((data_read = esp_http_client_read(client, (char*)ota_chunk, CHUNK_SIZE)) > 0) { + + ESP_LOGV(TAG, "Read image length %d", data_read); + if ((err = rpc_ota_write(ota_chunk, data_read))) { + ESP_LOGI(TAG, "rpc_ota_write failed"); + ota_failed = err; + break; + } + } + } + + g_h.funcs->_h_free(ota_chunk); + if (err) { + ESP_LOGW(TAG, "********* Slave OTA Failed *******************"); + ESP_LOGI(TAG, "esp_ota_write failed, error=%s", esp_err_to_name(err)); + ota_failed = -1; + } + + if (data_read < 0) { + ESP_LOGE(TAG, "Error: SSL data read error"); + ota_failed = -2; + } + + if ((err = rpc_ota_end())) { + ESP_LOGW(TAG, "********* Slave OTA Failed *******************"); + ESP_LOGI(TAG, "esp_ota_end failed, error=%s", esp_err_to_name(err)); + esp_http_client_close(client); + esp_http_client_cleanup(client); + ota_failed = err; + return ESP_FAIL; + } + + esp_http_client_cleanup(client); + if (!ota_failed) { + ESP_LOGW(TAG, "********* Slave OTA Complete *******************"); + ESP_LOGI(TAG, "OTA Successful, Slave will restart in while"); + ESP_LOGE(TAG, "Need to restart host after slave OTA is complete, to avoid sync issues"); + sleep(5); + ESP_LOGE(TAG, "********* Restarting Host **********************"); + esp_restart(); + } + return ota_failed; +} + +static esp_err_t esp_hosted_slave_ota_chunked(const char* image_url) +{ + uint8_t ota_retry = 2; + int ret = 0; + + do { + ret = _hosted_ota(image_url); + + ota_retry--; + if (ota_retry && ret) + ESP_LOGI(TAG, "OTA retry left: %u\n", ota_retry); + } while (ota_retry && ret); + + return ret; +} +#else +/* This assumes full slave binary is present locally */ +static esp_err_t esp_hosted_slave_ota_whole_image(const char* image_path) +{ + FILE* f = NULL; + char ota_chunk[CHUNK_SIZE] = {0}; + int ret = rpc_ota_begin(); + if (ret == ESP_OK) { + f = fopen(image_path,"rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open file %s", image_path); + return ESP_FAIL; + } else { + ESP_LOGV(TAG, "Success in opening %s file", image_path); + } + while (!feof(f)) { + fread(&ota_chunk, CHUNK_SIZE, 1, f); + ret = rpc_ota_write((uint8_t* )&ota_chunk, CHUNK_SIZE); + if (ret) { + ESP_LOGE(TAG, "OTA procedure failed!!"); + /* TODO: Do we need to do OTA end irrespective of success/failure? */ + rpc_ota_end(); + return ESP_FAIL; + } + } + ret = rpc_ota_end(); + if (ret) { + return ESP_FAIL; + } + } else { + return ESP_FAIL; + } + ESP_LOGE(TAG, "ESP32 will restart after 5 sec"); + return ESP_OK; + ESP_LOGE(TAG, "For OTA, user need to integrate HTTP client lib and then invoke OTA"); + return ESP_FAIL; +} +#endif // ENABLE_HTTP_OTA + +esp_err_t esp_hosted_slave_ota(const char* image_url) +{ +#if OTA_FROM_WEB_URL + return esp_hosted_slave_ota_chunked(image_url); +#else + return esp_hosted_slave_ota_whole_image(image_url); +#endif +} diff --git a/host/port/src/esp_hosted_transport_config.c b/host/port/esp/freertos/src/esp_hosted_transport_config.c similarity index 100% rename from host/port/src/esp_hosted_transport_config.c rename to host/port/esp/freertos/src/esp_hosted_transport_config.c diff --git a/host/port/src/os_wrapper.c b/host/port/esp/freertos/src/os_wrapper.c similarity index 100% rename from host/port/src/os_wrapper.c rename to host/port/esp/freertos/src/os_wrapper.c diff --git a/host/port/src/sdio_wrapper.c b/host/port/esp/freertos/src/sdio_wrapper.c similarity index 100% rename from host/port/src/sdio_wrapper.c rename to host/port/esp/freertos/src/sdio_wrapper.c diff --git a/host/port/src/spi_hd_wrapper.c b/host/port/esp/freertos/src/spi_hd_wrapper.c similarity index 100% rename from host/port/src/spi_hd_wrapper.c rename to host/port/esp/freertos/src/spi_hd_wrapper.c diff --git a/host/port/src/spi_wrapper.c b/host/port/esp/freertos/src/spi_wrapper.c similarity index 100% rename from host/port/src/spi_wrapper.c rename to host/port/esp/freertos/src/spi_wrapper.c diff --git a/host/port/src/uart_wrapper.c b/host/port/esp/freertos/src/uart_wrapper.c similarity index 100% rename from host/port/src/uart_wrapper.c rename to host/port/esp/freertos/src/uart_wrapper.c diff --git a/idf_component.yml b/idf_component.yml index 9e700a06..6bac73b5 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.4.1" +version: "2.0.0" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 6b76ae116874be95b5a4941eb5f8b0e37cf783bd Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Wed, 7 May 2025 13:24:44 +0800 Subject: [PATCH 15/44] bugfix(idf_master): Fix build break using ESP-IDF master - Fix a build break using latest ESP-IDF master due to changes in `wifi_sta_config_t` - conditional compile using `ESP_IDF_VERSION` for backward compatibility - Use `H_xxx` instead of `CONFIG_ESP_HOSTED_xxx` to calculate counting semaphore size - Bump version - Closes https://github.com/espressif/esp-hosted-mcu/issues/47 --- common/rpc/esp_hosted_bitmasks.h | 95 ++++++++++++++++------------ host/drivers/rpc/core/rpc_req.c | 50 ++++++++++----- host/drivers/rpc/core/rpc_rsp.c | 41 +++++++++--- host/drivers/serial/serial_drv.c | 4 +- idf_component.yml | 2 +- slave/main/slave_control.c | 105 ++++++++++++++++++++++++------- 6 files changed, 208 insertions(+), 89 deletions(-) diff --git a/common/rpc/esp_hosted_bitmasks.h b/common/rpc/esp_hosted_bitmasks.h index 1a432950..198725c0 100644 --- a/common/rpc/esp_hosted_bitmasks.h +++ b/common/rpc/esp_hosted_bitmasks.h @@ -6,29 +6,12 @@ #ifndef __ESP_HOSTED_BITMASKS__H #define __ESP_HOSTED_BITMASKS__H +#include "esp_idf_version.h" + #define H_SET_BIT(pos, val) (val|=(1<>STA_MAX_USED_BIT) - -#define WIFI_CONFIG_STA_SET_RESERVED_VAL(reserved_in,num_out) \ - (num_out|=(reserved_in << STA_MAX_USED_BIT)); - enum { WIFI_SCAN_AP_REC_phy_11b_BIT = 0, WIFI_SCAN_AP_REC_phy_11g_BIT = 1, @@ -46,10 +29,10 @@ enum { #define WIFI_SCAN_AP_RESERVED_BITMASK 0xFC00 #define WIFI_SCAN_AP_GET_RESERVED_VAL(num) \ - ((num&WIFI_SCAN_AP_RESERVED_BITMASK)>>WIFI_SCAN_AP_REC_MAX_USED_BIT) + ((num & WIFI_SCAN_AP_RESERVED_BITMASK) >> WIFI_SCAN_AP_REC_MAX_USED_BIT) #define WIFI_SCAN_AP_SET_RESERVED_VAL(reserved_in,num_out) \ - (num_out|=(reserved_in << WIFI_SCAN_AP_REC_MAX_USED_BIT)); + (num_out |= (reserved_in << WIFI_SCAN_AP_REC_MAX_USED_BIT)); enum { WIFI_STA_INFO_phy_11b_BIT = 0, @@ -64,10 +47,10 @@ enum { #define WIFI_STA_INFO_RESERVED_BITMASK 0xFFC0 #define WIFI_STA_INFO_GET_RESERVED_VAL(num) \ - ((num&WIFI_STA_INFO_RESERVED_BITMASK)>>WIFI_STA_INFO_MAX_USED_BIT) + ((num & WIFI_STA_INFO_RESERVED_BITMASK) >> WIFI_STA_INFO_MAX_USED_BIT) #define WIFI_STA_INFO_SET_RESERVED_VAL(reserved_in,num_out) \ - (num_out|=(reserved_in << WIFI_STA_INFO_MAX_USED_BIT)); + (num_out |= (reserved_in << WIFI_STA_INFO_MAX_USED_BIT)); /* WIFI HE AP Info bitmasks */ enum { @@ -79,27 +62,59 @@ enum { #define WIFI_HE_AP_INFO_BSS_COLOR_BITS 0x3F -/* WIFI HE Station Config bitmasks */ +/*** There are currently two set of bitfields in wifi_sta_config_t */ + +/* WIFI Station Config Bitfield 1 bitmasks */ enum { - WIFI_HE_STA_CONFIG_he_dcm_set_BIT = 0, - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide - WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx_BITS = 1, - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide - WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx_BITS = 3, - WIFI_HE_STA_CONFIG_he_mcs9_enabled_BIT = 5, - WIFI_HE_STA_CONFIG_he_su_beamformee_disabled_BIT = 6, - WIFI_HE_STA_CONFIG_he_trig_su_bmforming_feedback_disabled_BIT = 7, - WIFI_HE_STA_CONFIG_he_trig_mu_bmforming_partial_feedback_disabled_BIT = 8, - WIFI_HE_STA_CONFIG_he_trig_cqi_feedback_disabled_BIT = 9, - WIFI_HE_STA_CONFIG_MAX_USED_BIT = 10, + WIFI_STA_CONFIG_1_rm_enabled = 0, + WIFI_STA_CONFIG_1_btm_enabled = 1, + WIFI_STA_CONFIG_1_mbo_enabled = 2, + WIFI_STA_CONFIG_1_ft_enabled = 3, + WIFI_STA_CONFIG_1_owe_enabled = 4, + WIFI_STA_CONFIG_1_transition_disable = 5, + WIFI_STA_CONFIG_1_MAX_USED_BIT = 6, }; -#define WIFI_HE_STA_CONFIG_BITS 0xFC00 +#define WIFI_STA_CONFIG_1_RESERVED_BITMASK 0xFFFFFFC0 + +#define WIFI_STA_CONFIG_1_GET_RESERVED_VAL(num) \ + ((num & WIFI_STA_CONFIG_1_RESERVED_BITMASK) >> WIFI_STA_CONFIG_1_MAX_USED_BIT) + +#define WIFI_STA_CONFIG_1_SET_RESERVED_VAL(reserved_in, num_out) \ + (num_out |= (reserved_in << WIFI_STA_CONFIG_1_MAX_USED_BIT)); + +/* WIFI Station Config Bitfield 2 bitmasks */ +enum { + WIFI_STA_CONFIG_2_he_dcm_set_BIT = 0, + // WIFI_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide + WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS = 1, + // WIFI_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide + WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS = 3, + WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT = 5, + WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT = 6, + WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT = 7, + WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT = 8, + WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT = 9, +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + WIFI_STA_CONFIG_2_MAX_USED_BIT = 10, +#else + WIFI_STA_CONFIG_2_vht_su_beamformee_disabled = 10, + WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled = 11, + WIFI_STA_CONFIG_2_vht_mcs8_enabled = 12, + WIFI_STA_CONFIG_2_MAX_USED_BIT = 13, +#endif +}; + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) +#define WIFI_STA_CONFIG_2_RESERVED_BITMASK 0xFFFFFC00 +#else +#define WIFI_STA_CONFIG_2_RESERVED_BITMASK 0xFFFFE000 +#endif -#define WIFI_HE_STA_GET_RESERVED_VAL(num) \ - ((num&WIFI_HE_STA_CONFIG_BITS)>>WIFI_HE_STA_CONFIG_MAX_USED_BIT) +#define WIFI_STA_CONFIG_2_GET_RESERVED_VAL(num) \ + ((num & WIFI_STA_CONFIG_2_RESERVED_BITMASK) >> WIFI_STA_CONFIG_2_MAX_USED_BIT) -#define WIFI_HE_STA_SET_RESERVED_VAL(reserved_in,num_out) \ - (num_out|=(reserved_in << WIFI_HE_STA_CONFIG_MAX_USED_BIT)); +#define WIFI_STA_CONFIG_2_SET_RESERVED_VAL(reserved_in,num_out) \ + (num_out |= (reserved_in << WIFI_STA_CONFIG_2_MAX_USED_BIT)); #endif diff --git a/host/drivers/rpc/core/rpc_req.c b/host/drivers/rpc/core/rpc_req.c index 80bb6c69..22331a84 100644 --- a/host/drivers/rpc/core/rpc_req.c +++ b/host/drivers/rpc/core/rpc_req.c @@ -8,6 +8,7 @@ #include "esp_hosted_wifi_config.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" +#include "esp_idf_version.h" DEFINE_LOG_TAG(rpc_req); @@ -231,55 +232,72 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) p_c_sta->pmf_cfg->required = p_a_sta->pmf_cfg.required; if (p_a_sta->rm_enabled) - H_SET_BIT(STA_RM_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); if (p_a_sta->btm_enabled) - H_SET_BIT(STA_BTM_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); if (p_a_sta->mbo_enabled) - H_SET_BIT(STA_MBO_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); if (p_a_sta->ft_enabled) - H_SET_BIT(STA_FT_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_ft_enabled, p_c_sta->bitmask); if (p_a_sta->owe_enabled) - H_SET_BIT(STA_OWE_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); if (p_a_sta->transition_disable) - H_SET_BIT(STA_TRASITION_DISABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); - WIFI_CONFIG_STA_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); +#else + WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved1, p_c_sta->bitmask); +#endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; p_c_sta->failure_retry_cnt = p_a_sta->failure_retry_cnt; if (p_a_sta->he_dcm_set) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_dcm_set_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide if (p_a_sta->he_dcm_max_constellation_tx) - p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_tx & 0x03) << WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx_BITS); + p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_tx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS); // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide if (p_a_sta->he_dcm_max_constellation_rx) - p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_rx & 0x03) << WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx_BITS); + p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_rx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS); if (p_a_sta->he_mcs9_enabled) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); if (p_a_sta->he_su_beamformee_disabled) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); if (p_a_sta->he_trig_su_bmforming_feedback_disabled) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->he_bitmask); if (p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->he_bitmask); if (p_a_sta->he_trig_cqi_feedback_disabled) - H_SET_BIT(WIFI_HE_STA_CONFIG_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); +#else + if (p_a_sta->vht_su_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); + + if (p_a_sta->vht_mu_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); - WIFI_HE_STA_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); + if (p_a_sta->vht_mcs8_enabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); + + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); +#endif RPC_REQ_COPY_BYTES(p_c_sta->sae_h2e_identifier, p_a_sta->sae_h2e_identifier, SAE_H2E_IDENTIFIER_LEN); break; diff --git a/host/drivers/rpc/core/rpc_rsp.c b/host/drivers/rpc/core/rpc_rsp.c index cc47f0ac..54e6a293 100644 --- a/host/drivers/rpc/core/rpc_rsp.c +++ b/host/drivers/rpc/core/rpc_rsp.c @@ -7,6 +7,7 @@ #include "esp_hosted_wifi_config.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" +#include "esp_idf_version.h" DEFINE_LOG_TAG(rpc_rsp); @@ -259,16 +260,42 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; - p_a_sta->rm_enabled = H_GET_BIT(STA_RM_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->btm_enabled = H_GET_BIT(STA_BTM_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->mbo_enabled = H_GET_BIT(STA_MBO_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->ft_enabled = H_GET_BIT(STA_FT_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->owe_enabled = H_GET_BIT(STA_OWE_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->transition_disable = H_GET_BIT(STA_TRASITION_DISABLED_BIT, p_c_sta->bitmask); - p_a_sta->reserved = WIFI_CONFIG_STA_GET_RESERVED_VAL(p_c_sta->bitmask); + p_a_sta->rm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); + p_a_sta->btm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); + p_a_sta->mbo_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); + p_a_sta->ft_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_ft_enabled, p_c_sta->bitmask); + p_a_sta->owe_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); + p_a_sta->transition_disable = H_GET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#else + p_a_sta->reserved1 = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; p_a_sta->failure_retry_cnt = p_c_sta->failure_retry_cnt; + + p_a_sta->he_dcm_set = H_GET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); + + // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide + p_a_sta->he_dcm_max_constellation_tx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS) & 0x03; + // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide + p_a_sta->he_dcm_max_constellation_rx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS) & 0x03; + p_a_sta->he_mcs9_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->bitmask); + p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); + p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#else + p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); + p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); + p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); + p_a_sta->reserved2 = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#endif + break; } case WIFI_IF_AP: { diff --git a/host/drivers/serial/serial_drv.c b/host/drivers/serial/serial_drv.c index f9605b39..9ed01633 100644 --- a/host/drivers/serial/serial_drv.c +++ b/host/drivers/serial/serial_drv.c @@ -212,8 +212,8 @@ int serial_drv_close(struct serial_drv_handle_t** serial_drv_handle) int rpc_platform_init(void) { /* rpc semaphore */ - readSemaphore = g_h.funcs->_h_create_semaphore(CONFIG_ESP_HOSTED_MAX_SIMULTANEOUS_SYNC_RPC_REQUESTS + - CONFIG_ESP_HOSTED_MAX_SIMULTANEOUS_ASYNC_RPC_REQUESTS); + readSemaphore = g_h.funcs->_h_create_semaphore(H_MAX_SYNC_RPC_REQUESTS + + H_MAX_ASYNC_RPC_REQUESTS); assert(readSemaphore); /* grab the semaphore, so that task will be mandated to wait on semaphore */ diff --git a/idf_component.yml b/idf_component.yml index 6bac73b5..7a4dc134 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.0" +version: "2.0.1" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/slave_control.c b/slave/main/slave_control.c index 22020178..35323f64 100644 --- a/slave/main/slave_control.c +++ b/slave/main/slave_control.c @@ -14,6 +14,7 @@ // limitations under the License. #include + #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "esp_log.h" @@ -24,6 +25,7 @@ #include "esp_hosted_rpc.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" +#include "esp_idf_version.h" #include "coprocessor_fw_version.h" @@ -868,28 +870,39 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; - p_a_sta->rm_enabled = H_GET_BIT(STA_RM_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->btm_enabled = H_GET_BIT(STA_BTM_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->mbo_enabled = H_GET_BIT(STA_MBO_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->ft_enabled = H_GET_BIT(STA_FT_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->owe_enabled = H_GET_BIT(STA_OWE_ENABLED_BIT, p_c_sta->bitmask); - p_a_sta->transition_disable = H_GET_BIT(STA_TRASITION_DISABLED_BIT, p_c_sta->bitmask); - p_a_sta->reserved = WIFI_CONFIG_STA_GET_RESERVED_VAL(p_c_sta->bitmask); + p_a_sta->rm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); + p_a_sta->btm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); + p_a_sta->mbo_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); + p_a_sta->ft_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_ft_enabled, p_c_sta->bitmask); + p_a_sta->owe_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); + p_a_sta->transition_disable = H_GET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#else + p_a_sta->reserved1 = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; p_a_sta->failure_retry_cnt = p_c_sta->failure_retry_cnt; - p_a_sta->he_dcm_set = H_GET_BIT(WIFI_HE_STA_CONFIG_he_dcm_set_BIT, p_c_sta->he_bitmask); + p_a_sta->he_dcm_set = H_GET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide - p_a_sta->he_dcm_max_constellation_tx = (p_c_sta->he_bitmask >> WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx_BITS) & 0x03; + p_a_sta->he_dcm_max_constellation_tx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS) & 0x03; // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide - p_a_sta->he_dcm_max_constellation_rx = (p_c_sta->he_bitmask >> WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx_BITS) & 0x03; - p_a_sta->he_mcs9_enabled = H_GET_BIT(WIFI_HE_STA_CONFIG_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); - p_a_sta->he_su_beamformee_disabled = H_GET_BIT(WIFI_HE_STA_CONFIG_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); - p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_HE_STA_CONFIG_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->bitmask); - p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_HE_STA_CONFIG_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); - p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_HE_STA_CONFIG_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); - p_a_sta->he_reserved = WIFI_HE_STA_GET_RESERVED_VAL(p_c_sta->bitmask); + p_a_sta->he_dcm_max_constellation_rx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS) & 0x03; + p_a_sta->he_mcs9_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->bitmask); + p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); + p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#else + p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); + p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); + p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); + p_a_sta->reserved2 = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#endif /* Avoid using fast scan, which leads to faster SSID selection, * but faces data throughput issues when same SSID broadcasted by weaker AP @@ -967,27 +980,73 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) p_c_sta->pmf_cfg->required = p_a_sta->pmf_cfg.required; if (p_a_sta->rm_enabled) - H_SET_BIT(STA_RM_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); if (p_a_sta->btm_enabled) - H_SET_BIT(STA_BTM_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); if (p_a_sta->mbo_enabled) - H_SET_BIT(STA_MBO_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); if (p_a_sta->ft_enabled) - H_SET_BIT(STA_FT_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_ft_enabled, p_c_sta->bitmask); if (p_a_sta->owe_enabled) - H_SET_BIT(STA_OWE_ENABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); if (p_a_sta->transition_disable) - H_SET_BIT(STA_TRASITION_DISABLED_BIT, p_c_sta->bitmask); + H_SET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); - WIFI_CONFIG_STA_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); +#else + WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved1, p_c_sta->bitmask); +#endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; p_c_sta->failure_retry_cnt = p_a_sta->failure_retry_cnt; + + if (p_a_sta->he_dcm_set) + H_SET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); + + // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide + if (p_a_sta->he_dcm_max_constellation_tx) + p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_tx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS); + + // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide + if (p_a_sta->he_dcm_max_constellation_rx) + p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_rx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS); + + if (p_a_sta->he_mcs9_enabled) + H_SET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); + + if (p_a_sta->he_su_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); + + if (p_a_sta->he_trig_su_bmforming_feedback_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->he_bitmask); + + if (p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->he_bitmask); + + if (p_a_sta->he_trig_cqi_feedback_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); +#else + if (p_a_sta->vht_su_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); + + if (p_a_sta->vht_mu_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); + + if (p_a_sta->vht_mcs8_enabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); + + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); +#endif + break; } case WIFI_IF_AP: { From 6a1e8cc2f9e836151bb2cff2ad60f75846f83d42 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 13 May 2025 15:57:30 +0800 Subject: [PATCH 16/44] bugfix(rename_wifi_fn): Renamed wifi functions in RPC slave code - prefix function names in `rpc_slave_if` with `rpc_slaveif_` - to prevent function name collision with `espressif/console_cmd_wifi` - clean-up: removed unused functions --- host/drivers/rpc/slaveif/rpc_slave_if.c | 164 ++++------- host/drivers/rpc/slaveif/rpc_slave_if.h | 159 ++++------- host/drivers/rpc/wrap/rpc_wrap.c | 345 ++++-------------------- idf_component.yml | 2 +- 4 files changed, 169 insertions(+), 501 deletions(-) diff --git a/host/drivers/rpc/slaveif/rpc_slave_if.c b/host/drivers/rpc/slaveif/rpc_slave_if.c index 75c081ec..7495223e 100644 --- a/host/drivers/rpc/slaveif/rpc_slave_if.c +++ b/host/drivers/rpc/slaveif/rpc_slave_if.c @@ -40,386 +40,332 @@ int rpc_slaveif_deinit(void) } /** Control Req->Resp APIs **/ -ctrl_cmd_t * wifi_get_mac(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_mac(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_GetMACAddress); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_mac(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_mac(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_SetMacAddress); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_mode(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_mode(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_GetWifiMode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_mode(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_mode(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_SetWifiMode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_ps(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_ps(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetPs); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_ps(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_ps(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetPs); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -//ctrl_cmd_t * wifi_ap_scan_list(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_GetAPScanList); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_get_ap_config(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_GetAPConfig); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_connect_ap(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_ConnectAP); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_disconnect_ap(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_DisconnectAP); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_start_softap(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_StartSoftAP); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_get_softap_config(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_GetSoftAPConfig); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_stop_softap(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_StopSoftAP); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_get_softap_connected_station_list(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_GetSoftAPConnectedSTAList); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} -// -//ctrl_cmd_t * wifi_set_vendor_specific_ie(ctrl_cmd_t *req) -//{ -// RPC_SEND_REQ(RPC_ID__Req_SetSoftAPVendorSpecificIE); -// RPC_DECODE_RSP_IF_NOT_ASYNC(); -//} - -ctrl_cmd_t * wifi_set_max_tx_power(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_max_tx_power(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetMaxTxPower); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_max_tx_power(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_max_tx_power(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetMaxTxPower); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * config_heartbeat(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_config_heartbeat(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_ConfigHeartbeat); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * ota_begin(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_ota_begin(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_OTABegin); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * ota_write(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_ota_write(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_OTAWrite); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * ota_end(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_ota_end(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_OTAEnd); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_init(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_init(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiInit); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_deinit(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_deinit(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiDeinit); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_start(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_start(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStart); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_stop(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_stop(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStop); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_connect(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_connect(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiConnect); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_disconnect(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_disconnect(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiDisconnect); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_config(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_config(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetConfig); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_config(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_config(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetConfig); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_scan_start(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_scan_start(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiScanStart); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_scan_stop(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_scan_stop(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiScanStop); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_scan_get_ap_num(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_num(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiScanGetApNum); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_scan_get_ap_record(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_record(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiScanGetApRecord); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_scan_get_ap_records(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_records(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiScanGetApRecords); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_clear_ap_list(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_clear_ap_list(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiClearApList); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_restore(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_restore(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiRestore); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_clear_fast_connect(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_clear_fast_connect(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiClearFastConnect); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_deauth_sta(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_deauth_sta(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiDeauthSta); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_sta_get_ap_info(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_ap_info(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStaGetApInfo); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_storage(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_storage(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetStorage); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_bandwidth(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_bandwidth(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetBandwidth); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_bandwidth(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_bandwidth(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetBandwidth); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_channel(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_channel(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetChannel); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_channel(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_channel(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetChannel); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_country_code(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_country_code(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetCountryCode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_country_code(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_country_code(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetCountryCode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_country(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_country(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetCountry); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_country(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_country(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetCountry); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_ap_get_sta_list(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_ap_get_sta_list(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiApGetStaList); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_ap_get_sta_aid(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_ap_get_sta_aid(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiApGetStaAid); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_sta_get_rssi(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_rssi(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStaGetRssi); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_protocol(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_protocol(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetProtocol); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_protocol(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_protocol(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetProtocol); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_sta_get_negotiated_phymode(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_negotiated_phymode(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStaGetNegotiatedPhymode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_sta_get_aid(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_aid(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiStaGetAid); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_protocols(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_protocols(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetProtocols); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * get_coprocessor_fwversion(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_get_coprocessor_fwversion(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_GetCoprocessorFwVersion); RPC_DECODE_RSP_IF_NOT_ASYNC(); } #if H_WIFI_DUALBAND_SUPPORT -ctrl_cmd_t * wifi_get_protocols(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_protocols(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetProtocols); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_bandwidths(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_bandwidths(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetBandwidths); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_bandwidths(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_bandwidths(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetBandwidths); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_band(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_band(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetBand); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_band(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_band(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetBand); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_set_band_mode(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_set_band_mode(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiSetBandMode); RPC_DECODE_RSP_IF_NOT_ASYNC(); } -ctrl_cmd_t * wifi_get_band_mode(ctrl_cmd_t *req) +ctrl_cmd_t * rpc_slaveif_wifi_get_band_mode(ctrl_cmd_t *req) { RPC_SEND_REQ(RPC_ID__Req_WifiGetBandMode); RPC_DECODE_RSP_IF_NOT_ASYNC(); diff --git a/host/drivers/rpc/slaveif/rpc_slave_if.h b/host/drivers/rpc/slaveif/rpc_slave_if.h index 9fd09900..b5621b80 100644 --- a/host/drivers/rpc/slaveif/rpc_slave_if.h +++ b/host/drivers/rpc/slaveif/rpc_slave_if.h @@ -377,141 +377,92 @@ int rpc_slaveif_init(void); int rpc_slaveif_deinit(void); /* Get the MAC address of station or softAP interface of ESP32 */ -ctrl_cmd_t * wifi_get_mac(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_mac(ctrl_cmd_t *req); /* Set MAC address of ESP32 interface for given wifi mode */ -ctrl_cmd_t * wifi_set_mac(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_mac(ctrl_cmd_t *req); /* Get Wi-Fi mode of ESP32 */ -ctrl_cmd_t * wifi_get_mode(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_mode(ctrl_cmd_t *req); /* Set the Wi-Fi mode of ESP32 */ -ctrl_cmd_t * wifi_set_mode(ctrl_cmd_t *req); - -/* Set Wi-Fi power save mode of ESP32 */ -ctrl_cmd_t * wifi_set_power_save_mode(ctrl_cmd_t *req); - -/* Get the Wi-Fi power save mode of ESP32 */ -ctrl_cmd_t * wifi_get_power_save_mode(ctrl_cmd_t *req); - -///* Get list of available neighboring APs of ESP32 */ -//ctrl_cmd_t * wifi_ap_scan_list(ctrl_cmd_t *req); -// -///* Get the AP config to which ESP32 station is connected */ -//ctrl_cmd_t * wifi_get_ap_config(ctrl_cmd_t *req); -// -///* Set the AP config to which ESP32 station should connect to */ -//ctrl_cmd_t * wifi_connect_ap(ctrl_cmd_t *req); -// -///* Disconnect ESP32 station from AP */ -//ctrl_cmd_t * wifi_disconnect_ap(ctrl_cmd_t *req); -// -///* Set configuration of ESP32 softAP and start broadcasting */ -//ctrl_cmd_t * wifi_start_softap(ctrl_cmd_t *req); -// -///* Get configuration of ESP32 softAP */ -//ctrl_cmd_t * wifi_get_softap_config(ctrl_cmd_t *req); -// -///* Stop ESP32 softAP */ -//ctrl_cmd_t * wifi_stop_softap(ctrl_cmd_t *req); -// -///* Get list of connected stations to ESP32 softAP */ -//ctrl_cmd_t * wifi_get_softap_connected_station_list(ctrl_cmd_t *req); -// -///* Function set 802.11 Vendor-Specific Information Element. -// * It needs to get called before starting of ESP32 softAP */ -//ctrl_cmd_t * wifi_set_vendor_specific_ie(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_mode(ctrl_cmd_t *req); /* Sets maximum WiFi transmitting power at ESP32 */ -ctrl_cmd_t * wifi_set_max_tx_power(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_max_tx_power(ctrl_cmd_t *req); /* Gets maximum WiFi transmiting power at ESP32 */ -ctrl_cmd_t * wifi_get_max_tx_power(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_max_tx_power(ctrl_cmd_t *req); /* Configure heartbeat event. Be default heartbeat is not enabled. * To enable heartbeats, user need to use this API in addition * to setting event callback for heartbeat event */ -ctrl_cmd_t * config_heartbeat(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_config_heartbeat(ctrl_cmd_t *req); /* Performs an OTA begin operation for ESP32 which erases and * prepares existing flash partition for new flash writing */ -ctrl_cmd_t * ota_begin(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_ota_begin(ctrl_cmd_t *req); /* Performs an OTA write operation for ESP32, It writes bytes from `ota_data` * buffer with `ota_data_len` number of bytes to OTA partition in flash. Number * of bytes can be small than size of complete binary to be flashed. In that * case, this caller is expected to repeatedly call this function till * total size written equals size of complete binary */ -ctrl_cmd_t * ota_write(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_ota_write(ctrl_cmd_t *req); /* Performs an OTA end operation for ESP32, It validates written OTA image, * sets newly written OTA partition as boot partition for next boot, * Creates timer which reset ESP32 after 5 sec */ -ctrl_cmd_t * ota_end(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_ota_end(ctrl_cmd_t *req); /* Gets the co-processor FW Version */ -ctrl_cmd_t * get_coprocessor_fwversion(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_get_coprocessor_fwversion(ctrl_cmd_t *req); /* TODO: add descriptions */ -ctrl_cmd_t * wifi_init(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_deinit(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_start(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_stop(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_connect(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_disconnect(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_config(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_config(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_scan_start(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_scan_stop(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_scan_get_ap_num(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_scan_get_ap_record(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_scan_get_ap_records(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_clear_ap_list(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_restore(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_clear_fast_connect(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_deauth_sta(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_sta_get_ap_info(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_ps(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_ps(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_storage(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_bandwidth(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_bandwidth(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_channel(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_channel(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_country_code(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_country_code(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_country(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_country(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_ap_get_sta_list(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_ap_get_sta_aid(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_sta_get_rssi(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_protocol(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_protocol(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_sta_get_negotiated_phymode(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_sta_get_aid(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_protocols(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_protocols(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_bandwidths(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_bandwidths(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_band(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_band(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_set_band_mode(ctrl_cmd_t *req); -ctrl_cmd_t * wifi_get_band_mode(ctrl_cmd_t *req); - -/* Get the interface up for interface `iface` */ -int interface_up(int sockfd, char* iface); - -/* Get the interface down for interface `iface` */ -int interface_down(int sockfd, char* iface); - -/* Set ethernet interface MAC address `mac` to interface `iface` */ -int set_hw_addr(int sockfd, char* iface, char* mac); - -/* Create an endpoint for communication */ -int create_socket(int domain, int type, int protocol, int *sock); - -/* Close an endpoint of the communication */ -int close_socket(int sock); +ctrl_cmd_t * rpc_slaveif_wifi_init(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_deinit(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_start(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_stop(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_connect(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_disconnect(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_config(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_config(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_scan_start(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_scan_stop(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_num(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_record(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_scan_get_ap_records(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_clear_ap_list(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_restore(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_clear_fast_connect(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_deauth_sta(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_ap_info(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_ps(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_ps(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_storage(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_bandwidth(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_bandwidth(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_channel(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_channel(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_country_code(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_country_code(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_country(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_country(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_ap_get_sta_list(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_ap_get_sta_aid(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_rssi(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_protocol(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_protocol(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_negotiated_phymode(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_sta_get_aid(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_protocols(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_protocols(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_bandwidths(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_bandwidths(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_band(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_band(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_set_band_mode(ctrl_cmd_t *req); +ctrl_cmd_t * rpc_slaveif_wifi_get_band_mode(ctrl_cmd_t *req); #ifdef __cplusplus } diff --git a/host/drivers/rpc/wrap/rpc_wrap.c b/host/drivers/rpc/wrap/rpc_wrap.c index ff0e2db3..7053dae6 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.c +++ b/host/drivers/rpc/wrap/rpc_wrap.c @@ -502,7 +502,7 @@ int rpc_get_wifi_mode(void) /* register callback for reply */ req->rpc_rsp_cb = rpc_rsp_callback; - wifi_get_mode(req); + rpc_slaveif_wifi_get_mode(req); return SUCCESS; } @@ -515,7 +515,7 @@ int rpc_set_wifi_mode(wifi_mode_t mode) ctrl_cmd_t *resp = NULL; req->u.wifi_mode.mode = mode; - resp = wifi_set_mode(req); + resp = rpc_slaveif_wifi_set_mode(req); return rpc_rsp_callback(resp); } @@ -548,7 +548,7 @@ int rpc_wifi_get_mac(wifi_interface_t mode, uint8_t out_mac[6]) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); req->u.wifi_mac.mode = mode; - resp = wifi_get_mac(req); + resp = rpc_slaveif_wifi_get_mac(req); if (resp && resp->resp_event_status == SUCCESS) { @@ -572,7 +572,7 @@ int rpc_wifi_set_mac(wifi_interface_t mode, const uint8_t mac[6]) req->u.wifi_mac.mode = mode; g_h.funcs->_h_memcpy(req->u.wifi_mac.mac, mac, BSSID_BYTES_SIZE); - resp = wifi_set_mac(req); + resp = rpc_slaveif_wifi_set_mac(req); return rpc_rsp_callback(resp); } @@ -582,235 +582,6 @@ int rpc_softap_mode_get_mac_addr(uint8_t mac[6]) return rpc_wifi_get_mac(WIFI_MODE_AP, mac); } -//int rpc_async_station_mode_connect(char *ssid, char *pwd, char *bssid, -// int is_wpa3_supported, int listen_interval) -//{ -// /* implemented Asynchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// -// strcpy((char *)&req->u.hosted_ap_config.ssid, ssid); -// strcpy((char *)&req->u.hosted_ap_config.pwd, pwd); -// strcpy((char *)&req->u.hosted_ap_config.bssid, bssid); -// req->u.hosted_ap_config.is_wpa3_supported = is_wpa3_supported; -// req->u.hosted_ap_config.listen_interval = listen_interval; -// -// /* register callback for handling reply asynch-ly */ -// req->rpc_rsp_cb = rpc_rsp_callback; -// -// wifi_connect_ap(req); -// -// return SUCCESS; -//} -// -//int rpc_station_mode_connect(char *ssid, char *pwd, char *bssid, -// int is_wpa3_supported, int listen_interval) -//{ -// /* implemented Asynchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// strcpy((char *)&req->u.hosted_ap_config.ssid, ssid); -// strcpy((char *)&req->u.hosted_ap_config.pwd, pwd); -// strcpy((char *)&req->u.hosted_ap_config.bssid, bssid); -// req->u.hosted_ap_config.is_wpa3_supported = is_wpa3_supported; -// req->u.hosted_ap_config.listen_interval = listen_interval; -// -// resp = wifi_connect_ap(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_station_mode_get_info(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_get_ap_config(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_get_available_wifi(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// req->rsp_timeout_sec = 300; -// -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_ap_scan_list(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_station_mode_disconnect(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_disconnect_ap(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_softap_mode_start(char *ssid, char *pwd, int channel, -// int encryption_mode, int max_conn, int ssid_hidden, int bw) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// strncpy((char *)&req->u.wifi_softap_config.ssid, -// ssid, MAX_MAC_STR_LEN-1); -// strncpy((char *)&req->u.wifi_softap_config.pwd, -// pwd, MAX_MAC_STR_LEN-1); -// req->u.wifi_softap_config.channel = channel; -// req->u.wifi_softap_config.encryption_mode = encryption_mode; -// req->u.wifi_softap_config.max_connections = max_conn; -// req->u.wifi_softap_config.ssid_hidden = ssid_hidden; -// req->u.wifi_softap_config.bandwidth = bw; -// -// resp = wifi_start_softap(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_softap_mode_get_info(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_get_softap_config(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_softap_mode_connected_clients_info(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_get_softap_connected_station_list(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_softap_mode_stop(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// -// resp = wifi_stop_softap(req); -// -// return rpc_rsp_callback(resp); -//} - -int rpc_set_wifi_power_save_mode(int psmode) -{ - /* implemented synchronous */ - ctrl_cmd_t *req = RPC_DEFAULT_REQ(); - ctrl_cmd_t *resp = NULL; - - req->u.wifi_ps.ps_mode = psmode; - resp = wifi_set_power_save_mode(req); - - return rpc_rsp_callback(resp); -} - -int rpc_set_wifi_power_save_mode_max(void) -{ - return rpc_set_wifi_power_save_mode(WIFI_PS_MAX_MODEM); -} - -int rpc_set_wifi_power_save_mode_min(void) -{ - return rpc_set_wifi_power_save_mode(WIFI_PS_MIN_MODEM); -} - -int rpc_get_wifi_power_save_mode(void) -{ - /* implemented synchronous */ - ctrl_cmd_t *req = RPC_DEFAULT_REQ(); - ctrl_cmd_t *resp = NULL; - - resp = wifi_get_power_save_mode(req); - - return rpc_rsp_callback(resp); -} - -//int rpc_reset_vendor_specific_ie(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// char *data = "Example vendor IE data"; -// -// char *v_data = (char*)g_h.funcs->_h_calloc(1, strlen(data)); -// if (!v_data) { -// ESP_LOGE(TAG, "Failed to allocate memory \n"); -// return FAILURE; -// } -// g_h.funcs->_h_memcpy(v_data, data, strlen(data)); -// -// req->u.wifi_softap_vendor_ie.enable = false; -// req->u.wifi_softap_vendor_ie.type = WIFI_VND_IE_TYPE_BEACON; -// req->u.wifi_softap_vendor_ie.idx = WIFI_VND_IE_ID_0; -// req->u.wifi_softap_vendor_ie.vnd_ie.element_id = WIFI_VENDOR_IE_ELEMENT_ID; -// req->u.wifi_softap_vendor_ie.vnd_ie.length = strlen(data)+OFFSET; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[0] = VENDOR_OUI_0; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[1] = VENDOR_OUI_1; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[2] = VENDOR_OUI_2; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui_type = VENDOR_OUI_TYPE; -// req->u.wifi_softap_vendor_ie.vnd_ie.payload = (uint8_t *)v_data; -// //req->u.wifi_softap_vendor_ie.vnd_ie.payload_len = strlen(data); -// -// req->app_free_buff_func = g_h.funcs->_h_free; -// req->app_free_buff_hdl = v_data; -// -// resp = wifi_set_vendor_specific_ie(req); -// -// return rpc_rsp_callback(resp); -//} -// -//int rpc_set_vendor_specific_ie(void) -//{ -// /* implemented synchronous */ -// ctrl_cmd_t *req = RPC_DEFAULT_REQ(); -// ctrl_cmd_t *resp = NULL; -// char *data = "Example vendor IE data"; -// -// char *v_data = (char*)g_h.funcs->_h_calloc(1, strlen(data)); -// if (!v_data) { -// ESP_LOGE(TAG, "Failed to allocate memory \n"); -// return FAILURE; -// } -// g_h.funcs->_h_memcpy(v_data, data, strlen(data)); -// -// req->u.wifi_softap_vendor_ie.enable = true; -// req->u.wifi_softap_vendor_ie.type = WIFI_VND_IE_TYPE_BEACON; -// req->u.wifi_softap_vendor_ie.idx = WIFI_VND_IE_ID_0; -// req->u.wifi_softap_vendor_ie.vnd_ie.element_id = WIFI_VENDOR_IE_ELEMENT_ID; -// req->u.wifi_softap_vendor_ie.vnd_ie.length = strlen(data)+OFFSET; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[0] = VENDOR_OUI_0; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[1] = VENDOR_OUI_1; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui[2] = VENDOR_OUI_2; -// req->u.wifi_softap_vendor_ie.vnd_ie.vendor_oui_type = VENDOR_OUI_TYPE; -// req->u.wifi_softap_vendor_ie.vnd_ie.payload = (uint8_t *)v_data; -// //req->u.wifi_softap_vendor_ie.vnd_ie.payload_len = strlen(data); -// -// req->app_free_buff_func = g_h.funcs->_h_free; -// req->app_free_buff_hdl = v_data; -// -// resp = wifi_set_vendor_specific_ie(req); -// -// return rpc_rsp_callback(resp); -//} - int rpc_ota_begin(void) { /* implemented synchronous */ @@ -820,7 +591,7 @@ int rpc_ota_begin(void) /* OTA begin takes some time to clear the partition */ req->rsp_timeout_sec = OTA_BEGIN_RSP_TIMEOUT_SEC; - resp = ota_begin(req); + resp = rpc_slaveif_ota_begin(req); return rpc_rsp_callback(resp); } @@ -834,7 +605,7 @@ int rpc_ota_write(uint8_t* ota_data, uint32_t ota_data_len) req->u.ota_write.ota_data = ota_data; req->u.ota_write.ota_data_len = ota_data_len; - resp = ota_write(req); + resp = rpc_slaveif_ota_write(req); return rpc_rsp_callback(resp); } @@ -845,7 +616,7 @@ int rpc_ota_end(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = ota_end(req); + resp = rpc_slaveif_ota_end(req); return rpc_rsp_callback(resp); } @@ -856,7 +627,7 @@ esp_err_t rpc_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = get_coprocessor_fwversion(req); + resp = rpc_slaveif_get_coprocessor_fwversion(req); if (resp && resp->resp_event_status == SUCCESS) { ver_info->major1 = resp->u.coprocessor_fwversion.major1; ver_info->minor1 = resp->u.coprocessor_fwversion.minor1; @@ -873,7 +644,7 @@ int rpc_wifi_set_max_tx_power(int8_t in_power) ctrl_cmd_t *resp = NULL; req->u.wifi_tx_power.power = in_power; - resp = wifi_set_max_tx_power(req); + resp = rpc_slaveif_wifi_set_max_tx_power(req); return rpc_rsp_callback(resp); } @@ -884,7 +655,7 @@ int rpc_wifi_get_max_tx_power(int8_t *power) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_get_max_tx_power(req); + resp = rpc_slaveif_wifi_get_max_tx_power(req); if (resp && resp->resp_event_status == SUCCESS) { *power = resp->u.wifi_tx_power.power; } @@ -897,7 +668,7 @@ esp_err_t rpc_wifi_sta_get_negotiated_phymode(wifi_phy_mode_t *phymode) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_sta_get_negotiated_phymode(req); + resp = rpc_slaveif_wifi_sta_get_negotiated_phymode(req); if (resp && resp->resp_event_status == SUCCESS) { *phymode = resp->u.wifi_sta_get_negotiated_phymode.phymode; } @@ -910,7 +681,7 @@ esp_err_t rpc_wifi_sta_get_aid(uint16_t *aid) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_sta_get_aid(req); + resp = rpc_slaveif_wifi_sta_get_aid(req); if (resp && resp->resp_event_status == SUCCESS) { *aid = resp->u.wifi_sta_get_aid.aid; } @@ -925,7 +696,7 @@ esp_err_t rpc_wifi_set_band(wifi_band_t band) ctrl_cmd_t *resp = NULL; req->u.wifi_band = band; - resp = wifi_set_band(req); + resp = rpc_slaveif_wifi_set_band(req); return rpc_rsp_callback(resp); } @@ -936,7 +707,7 @@ esp_err_t rpc_wifi_get_band(wifi_band_t *band) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_get_band(req); + resp = rpc_slaveif_wifi_get_band(req); if (resp && resp->resp_event_status == SUCCESS) { *band = resp->u.wifi_band; } @@ -950,7 +721,7 @@ esp_err_t rpc_wifi_set_band_mode(wifi_band_mode_t band_mode) ctrl_cmd_t *resp = NULL; req->u.wifi_band_mode = band_mode; - resp = wifi_set_band_mode(req); + resp = rpc_slaveif_wifi_set_band_mode(req); return rpc_rsp_callback(resp); } @@ -961,7 +732,7 @@ esp_err_t rpc_wifi_get_band_mode(wifi_band_mode_t *band_mode) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_get_band_mode(req); + resp = rpc_slaveif_wifi_get_band_mode(req); if (resp && resp->resp_event_status == SUCCESS) { *band_mode = resp->u.wifi_band_mode; } @@ -978,7 +749,7 @@ esp_err_t rpc_wifi_set_protocols(wifi_interface_t ifx, wifi_protocols_t *protoco req->u.wifi_protocols.ghz_2g = protocols->ghz_2g; req->u.wifi_protocols.ghz_5g = protocols->ghz_5g; - resp = wifi_set_protocols(req); + resp = rpc_slaveif_wifi_set_protocols(req); return rpc_rsp_callback(resp); } @@ -990,7 +761,7 @@ esp_err_t rpc_wifi_get_protocols(wifi_interface_t ifx, wifi_protocols_t *protoco req->u.wifi_protocols.ifx = ifx; - resp = wifi_get_protocols(req); + resp = rpc_slaveif_wifi_get_protocols(req); if (resp && resp->resp_event_status == SUCCESS) { protocols->ghz_2g = resp->u.wifi_protocols.ghz_2g; protocols->ghz_5g = resp->u.wifi_protocols.ghz_5g; @@ -1008,7 +779,7 @@ esp_err_t rpc_wifi_set_bandwidths(wifi_interface_t ifx, wifi_bandwidths_t *bw) req->u.wifi_bandwidths.ghz_2g = bw->ghz_2g; req->u.wifi_bandwidths.ghz_5g = bw->ghz_5g; - resp = wifi_set_bandwidths(req); + resp = rpc_slaveif_wifi_set_bandwidths(req); return rpc_rsp_callback(resp); } @@ -1021,7 +792,7 @@ esp_err_t rpc_wifi_get_bandwidths(wifi_interface_t ifx, wifi_bandwidths_t *bw) req->u.wifi_bandwidths.ifx = ifx; - resp = wifi_get_bandwidths(req); + resp = rpc_slaveif_wifi_get_bandwidths(req); if (resp && resp->resp_event_status == SUCCESS) { bw->ghz_2g = resp->u.wifi_bandwidths.ghz_2g; bw->ghz_5g = resp->u.wifi_bandwidths.ghz_5g; @@ -1038,7 +809,7 @@ int rpc_config_heartbeat(void) req->u.e_heartbeat.enable = YES; req->u.e_heartbeat.duration = HEARTBEAT_DURATION_SEC; - resp = config_heartbeat(req); + resp = rpc_slaveif_config_heartbeat(req); return rpc_rsp_callback(resp); } @@ -1050,7 +821,7 @@ int rpc_disable_heartbeat(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); req->u.e_heartbeat.enable = NO; - resp = config_heartbeat(req); + resp = rpc_slaveif_config_heartbeat(req); return rpc_rsp_callback(resp); } @@ -1065,7 +836,7 @@ int rpc_wifi_init(const wifi_init_config_t *arg) return FAILURE; g_h.funcs->_h_memcpy(&req->u.wifi_init_config, (void*)arg, sizeof(wifi_init_config_t)); - resp = wifi_init(req); + resp = rpc_slaveif_wifi_init(req); return rpc_rsp_callback(resp); } @@ -1076,7 +847,7 @@ int rpc_wifi_deinit(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_deinit(req); + resp = rpc_slaveif_wifi_deinit(req); return rpc_rsp_callback(resp); } @@ -1094,7 +865,7 @@ int rpc_wifi_get_mode(wifi_mode_t* mode) if (!mode) return FAILURE; - resp = wifi_get_mode(req); + resp = rpc_slaveif_wifi_get_mode(req); if (resp && resp->resp_event_status == SUCCESS) { *mode = resp->u.wifi_mode.mode; @@ -1109,7 +880,7 @@ int rpc_wifi_start(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_start(req); + resp = rpc_slaveif_wifi_start(req); return rpc_rsp_callback(resp); } @@ -1122,7 +893,7 @@ int rpc_wifi_stop(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_stop(req); + resp = rpc_slaveif_wifi_stop(req); return rpc_rsp_callback(resp); } @@ -1133,7 +904,7 @@ int rpc_wifi_connect(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_connect(req); + resp = rpc_slaveif_wifi_connect(req); return rpc_rsp_callback(resp); return 0; #else @@ -1144,7 +915,7 @@ int rpc_wifi_connect(void) req->rpc_rsp_cb = rpc_rsp_callback; ESP_LOGE(TAG, "Async call registerd: %p", rpc_rsp_callback); - wifi_connect(req); + rpc_slaveif_wifi_connect(req); return SUCCESS; #endif @@ -1156,7 +927,7 @@ int rpc_wifi_disconnect(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_disconnect(req); + resp = rpc_slaveif_wifi_disconnect(req); return rpc_rsp_callback(resp); } @@ -1172,7 +943,7 @@ int rpc_wifi_set_config(wifi_interface_t interface, wifi_config_t *conf) g_h.funcs->_h_memcpy(&req->u.wifi_config.u, conf, sizeof(wifi_config_t)); req->u.wifi_config.iface = interface; - resp = wifi_set_config(req); + resp = rpc_slaveif_wifi_set_config(req); return rpc_rsp_callback(resp); } @@ -1187,7 +958,7 @@ int rpc_wifi_get_config(wifi_interface_t interface, wifi_config_t *conf) req->u.wifi_config.iface = interface; - resp = wifi_get_config(req); + resp = rpc_slaveif_wifi_get_config(req); g_h.funcs->_h_memcpy(conf, &resp->u.wifi_config.u, sizeof(wifi_config_t)); @@ -1207,7 +978,7 @@ int rpc_wifi_scan_start(const wifi_scan_config_t *config, bool block) req->u.wifi_scan_config.block = block; - resp = wifi_scan_start(req); + resp = rpc_slaveif_wifi_scan_start(req); return rpc_rsp_callback(resp); } @@ -1219,7 +990,7 @@ int rpc_wifi_scan_stop(void) ctrl_cmd_t *resp = NULL; ESP_LOGV(TAG, "scan stop"); - resp = wifi_scan_stop(req); + resp = rpc_slaveif_wifi_scan_stop(req); return rpc_rsp_callback(resp); } @@ -1232,7 +1003,7 @@ int rpc_wifi_scan_get_ap_num(uint16_t *number) if (!number) return FAILURE; - resp = wifi_scan_get_ap_num(req); + resp = rpc_slaveif_wifi_scan_get_ap_num(req); if (resp && resp->resp_event_status == SUCCESS) { *number = resp->u.wifi_scan_ap_list.number; @@ -1249,7 +1020,7 @@ int rpc_wifi_scan_get_ap_record(wifi_ap_record_t *ap_record) if (!ap_record) return FAILURE; - resp = wifi_scan_get_ap_record(req); + resp = rpc_slaveif_wifi_scan_get_ap_record(req); if (resp && resp->resp_event_status == SUCCESS) { g_h.funcs->_h_memcpy(ap_record, &resp->u.wifi_ap_record, sizeof(wifi_ap_record_t)); } @@ -1268,7 +1039,7 @@ int rpc_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records) g_h.funcs->_h_memset(ap_records, 0, (*number)*sizeof(wifi_ap_record_t)); req->u.wifi_scan_ap_list.number = *number; - resp = wifi_scan_get_ap_records(req); + resp = rpc_slaveif_wifi_scan_get_ap_records(req); if (resp && resp->resp_event_status == SUCCESS) { ESP_LOGV(TAG, "num: %u",resp->u.wifi_scan_ap_list.number); @@ -1284,7 +1055,7 @@ int rpc_wifi_clear_ap_list(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_clear_ap_list(req); + resp = rpc_slaveif_wifi_clear_ap_list(req); return rpc_rsp_callback(resp); } @@ -1295,7 +1066,7 @@ int rpc_wifi_restore(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_restore(req); + resp = rpc_slaveif_wifi_restore(req); return rpc_rsp_callback(resp); } @@ -1305,7 +1076,7 @@ int rpc_wifi_clear_fast_connect(void) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_clear_fast_connect(req); + resp = rpc_slaveif_wifi_clear_fast_connect(req); return rpc_rsp_callback(resp); } @@ -1316,7 +1087,7 @@ int rpc_wifi_deauth_sta(uint16_t aid) ctrl_cmd_t *resp = NULL; req->u.wifi_deauth_sta.aid = aid; - resp = wifi_deauth_sta(req); + resp = rpc_slaveif_wifi_deauth_sta(req); return rpc_rsp_callback(resp); } @@ -1329,7 +1100,7 @@ int rpc_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info) if (!ap_info) return FAILURE; - resp = wifi_sta_get_ap_info(req); + resp = rpc_slaveif_wifi_sta_get_ap_info(req); if (resp && resp->resp_event_status == SUCCESS) { g_h.funcs->_h_memcpy(ap_info, resp->u.wifi_scan_ap_list.out_list, @@ -1349,7 +1120,7 @@ int rpc_wifi_set_ps(wifi_ps_type_t type) req->u.wifi_ps.ps_mode = type; - resp = wifi_set_ps(req); + resp = rpc_slaveif_wifi_set_ps(req); return rpc_rsp_callback(resp); } @@ -1366,7 +1137,7 @@ int rpc_wifi_get_ps(wifi_ps_type_t *type) if (!type) return FAILURE; - resp = wifi_get_ps(req); + resp = rpc_slaveif_wifi_get_ps(req); *type = resp->u.wifi_ps.ps_mode; @@ -1380,7 +1151,7 @@ int rpc_wifi_set_storage(wifi_storage_t storage) ctrl_cmd_t *resp = NULL; req->u.wifi_storage = storage; - resp = wifi_set_storage(req); + resp = rpc_slaveif_wifi_set_storage(req); return rpc_rsp_callback(resp); } @@ -1392,7 +1163,7 @@ int rpc_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw) req->u.wifi_bandwidth.ifx = ifx; req->u.wifi_bandwidth.bw = bw; - resp = wifi_set_bandwidth(req); + resp = rpc_slaveif_wifi_set_bandwidth(req); return rpc_rsp_callback(resp); } @@ -1406,7 +1177,7 @@ int rpc_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw) return FAILURE; req->u.wifi_bandwidth.ifx = ifx; - resp = wifi_get_bandwidth(req); + resp = rpc_slaveif_wifi_get_bandwidth(req); if (resp && resp->resp_event_status == SUCCESS) { *bw = resp->u.wifi_bandwidth.bw; @@ -1422,7 +1193,7 @@ int rpc_wifi_set_channel(uint8_t primary, wifi_second_chan_t second) req->u.wifi_channel.primary = primary; req->u.wifi_channel.second = second; - resp = wifi_set_channel(req); + resp = rpc_slaveif_wifi_set_channel(req); return rpc_rsp_callback(resp); } @@ -1435,7 +1206,7 @@ int rpc_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second) if ((!primary) || (!second)) return FAILURE; - resp = wifi_get_channel(req); + resp = rpc_slaveif_wifi_get_channel(req); if (resp && resp->resp_event_status == SUCCESS) { *primary = resp->u.wifi_channel.primary; @@ -1455,7 +1226,7 @@ int rpc_wifi_set_country_code(const char *country, bool ieee80211d_enabled) memcpy(&req->u.wifi_country_code.cc[0], country, sizeof(req->u.wifi_country_code.cc)); req->u.wifi_country_code.ieee80211d_enabled = ieee80211d_enabled; - resp = wifi_set_country_code(req); + resp = rpc_slaveif_wifi_set_country_code(req); return rpc_rsp_callback(resp); } @@ -1468,7 +1239,7 @@ int rpc_wifi_get_country_code(char *country) if (!country) return FAILURE; - resp = wifi_get_country_code(req); + resp = rpc_slaveif_wifi_get_country_code(req); if (resp && resp->resp_event_status == SUCCESS) { memcpy(country, &resp->u.wifi_country_code.cc[0], sizeof(resp->u.wifi_country_code.cc)); @@ -1491,7 +1262,7 @@ int rpc_wifi_set_country(const wifi_country_t *country) req->u.wifi_country.max_tx_power = country->max_tx_power; req->u.wifi_country.policy = country->policy; - resp = wifi_set_country(req); + resp = rpc_slaveif_wifi_set_country(req); return rpc_rsp_callback(resp); } @@ -1504,7 +1275,7 @@ int rpc_wifi_get_country(wifi_country_t *country) if (!country) return FAILURE; - resp = wifi_get_country(req); + resp = rpc_slaveif_wifi_get_country(req); if (resp && resp->resp_event_status == SUCCESS) { memcpy(&country->cc[0], &resp->u.wifi_country.cc[0], sizeof(resp->u.wifi_country.cc)); country->schan = resp->u.wifi_country.schan; @@ -1524,7 +1295,7 @@ int rpc_wifi_ap_get_sta_list(wifi_sta_list_t *sta) if (!sta) return FAILURE; - resp = wifi_ap_get_sta_list(req); + resp = rpc_slaveif_wifi_ap_get_sta_list(req); if (resp && resp->resp_event_status == SUCCESS) { for (int i = 0; i < ESP_WIFI_MAX_CONN_NUM; i++) { memcpy(sta->sta[i].mac, resp->u.wifi_ap_sta_list.sta[i].mac, 6); @@ -1555,7 +1326,7 @@ int rpc_wifi_ap_get_sta_aid(const uint8_t mac[6], uint16_t *aid) memcpy(&req->u.wifi_ap_get_sta_aid.mac[0], &mac[0], MAC_SIZE_BYTES); - resp = wifi_ap_get_sta_aid(req); + resp = rpc_slaveif_wifi_ap_get_sta_aid(req); if (resp && resp->resp_event_status == SUCCESS) { *aid = resp->u.wifi_ap_get_sta_aid.aid; } @@ -1572,7 +1343,7 @@ int rpc_wifi_sta_get_rssi(int *rssi) if (!rssi) return FAILURE; - resp = wifi_sta_get_rssi(req); + resp = rpc_slaveif_wifi_sta_get_rssi(req); if (resp && resp->resp_event_status == SUCCESS) { *rssi = resp->u.wifi_sta_get_rssi.rssi; } @@ -1589,7 +1360,7 @@ int rpc_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap) req->u.wifi_protocol.ifx = ifx; req->u.wifi_protocol.protocol_bitmap = protocol_bitmap; - resp = wifi_set_protocol(req); + resp = rpc_slaveif_wifi_set_protocol(req); return rpc_rsp_callback(resp); } @@ -1602,7 +1373,7 @@ int rpc_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap) ctrl_cmd_t *req = RPC_DEFAULT_REQ(); ctrl_cmd_t *resp = NULL; - resp = wifi_get_protocol(req); + resp = rpc_slaveif_wifi_get_protocol(req); if (resp && resp->resp_event_status == SUCCESS) { *protocol_bitmap = resp->u.wifi_protocol.protocol_bitmap; } diff --git a/idf_component.yml b/idf_component.yml index 7a4dc134..a069e605 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.1" +version: "2.0.2" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From bee29d7e5496180ed750d3c022be608a4bae8faa Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 20 Mar 2025 11:09:53 +0800 Subject: [PATCH 17/44] enhance(host_stats) Print mem stats also - added code to print mem stats along with packet stats - added code to increment pkt_stats.sta_rx_out --- host/drivers/transport/sdio/sdio_drv.c | 5 +++++ host/drivers/transport/spi/spi_drv.c | 6 ++++++ host/drivers/transport/spi_hd/spi_hd_drv.c | 5 +++++ host/drivers/transport/uart/uart_drv.c | 10 ++++++++++ host/utils/stats.c | 13 ++++++++++--- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/host/drivers/transport/sdio/sdio_drv.c b/host/drivers/transport/sdio/sdio_drv.c index 1bc025f0..a73aa010 100644 --- a/host/drivers/transport/sdio/sdio_drv.c +++ b/host/drivers/transport/sdio/sdio_drv.c @@ -1017,6 +1017,11 @@ static void sdio_process_rx_task(void const* pvParameters) ESP_LOGW(TAG, "unknown type %d ", buf_handle->if_type); } +#if ESP_PKT_STATS + if (buf_handle->if_type == ESP_STA_IF) + pkt_stats.sta_rx_out++; +#endif + /* Free buffer handle */ /* When buffer offloaded to other module, that module is * responsible for freeing buffer. In case not offloaded or diff --git a/host/drivers/transport/spi/spi_drv.c b/host/drivers/transport/spi/spi_drv.c index 8e3751a1..3305a485 100644 --- a/host/drivers/transport/spi/spi_drv.c +++ b/host/drivers/transport/spi/spi_drv.c @@ -578,6 +578,12 @@ static void spi_process_rx_task(void const* pvParameters) } else { ESP_LOGW(TAG, "unknown type %d ", buf_handle->if_type); } + +#if ESP_PKT_STATS + if (buf_handle->if_type == ESP_STA_IF) + pkt_stats.sta_rx_out++; +#endif + /* Free buffer handle */ /* When buffer offloaded to other module, that module is * responsible for freeing buffer. In case not offloaded or diff --git a/host/drivers/transport/spi_hd/spi_hd_drv.c b/host/drivers/transport/spi_hd/spi_hd_drv.c index e51b9e0b..5e37471a 100644 --- a/host/drivers/transport/spi_hd/spi_hd_drv.c +++ b/host/drivers/transport/spi_hd/spi_hd_drv.c @@ -648,6 +648,11 @@ static void spi_hd_process_rx_task(void const* pvParameters) ESP_LOGW(TAG, "unknown type %d ", buf_handle->if_type); } +#if ESP_PKT_STATS + if (buf_handle->if_type == ESP_STA_IF) + pkt_stats.sta_rx_out++; +#endif + /* Free buffer handle */ /* When buffer offloaded to other module, that module is * responsible for freeing buffer. In case not offloaded or diff --git a/host/drivers/transport/uart/uart_drv.c b/host/drivers/transport/uart/uart_drv.c index a5502ed3..1708149c 100644 --- a/host/drivers/transport/uart/uart_drv.c +++ b/host/drivers/transport/uart/uart_drv.c @@ -167,6 +167,11 @@ static void h_uart_write_task(void const* pvParameters) ESP_LOGE(TAG, "failed to send uart data"); } +#if ESP_PKT_STATS + if (buf_handle.if_type == ESP_STA_IF) + pkt_stats.sta_tx_out++; +#endif + done: if (len && !buf_handle.payload_zcopy) { /* free allocated buffer, only if zerocopy is not requested */ @@ -271,6 +276,11 @@ static void h_uart_process_rx_task(void const* pvParameters) ESP_LOGW(TAG, "unknown type %d ", buf_handle->if_type); } +#if ESP_PKT_STATS + if (buf_handle->if_type == ESP_STA_IF) + pkt_stats.sta_rx_out++; +#endif + /* Free buffer handle */ /* When buffer offloaded to other module, that module is * responsible for freeing buffer. In case not offloaded or diff --git a/host/utils/stats.c b/host/utils/stats.c index 3917fc37..35cd61c8 100644 --- a/host/utils/stats.c +++ b/host/utils/stats.c @@ -209,10 +209,17 @@ struct mem_stats h_stats_g; #if ESP_PKT_STATS void stats_timer_func(void * arg) { - ESP_LOGI(TAG, "slave: sta_rx_in: %lu sta_rx_out: %lu sta_tx_in [pass: %lu drop: %lu] sta_tx_out: %lu ", + ESP_LOGI(TAG, "slave: sta_rx_in: %lu sta_rx_out: %lu sta_tx_in [pass: %lu drop: %lu] sta_tx_out: %lu, throttling %u", pkt_stats.sta_rx_in,pkt_stats.sta_rx_out, - pkt_stats.sta_tx_in_pass, pkt_stats.sta_tx_in_drop, pkt_stats.sta_tx_out); - ESP_LOGI(TAG, "wifi_tx_throttling %u", wifi_tx_throttling); + pkt_stats.sta_tx_in_pass, pkt_stats.sta_tx_in_drop, pkt_stats.sta_tx_out, + wifi_tx_throttling); + ESP_LOGI(TAG, "internal: free %d l-free %d min-free %d, psram: free %d l-free %d min-free %d", + heap_caps_get_free_size(MALLOC_CAP_8BIT) - heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL), + heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL), + heap_caps_get_free_size(MALLOC_CAP_SPIRAM), + heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM), + heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM)); } #endif From 4fd4de08e9e19796158ac9c1d96c02d4b6839999 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 15 May 2025 13:27:32 +0800 Subject: [PATCH 18/44] bugfix(ci_use_5_5): use release-5.5 image for ci build - use `image: espressif/idf:release-v5.5` for CI target `build_idf_v5.5` - allow buillding against master branch to fail --- .gitlab-ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d46807a3..f890f383 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,8 +49,17 @@ build_idf_v5.4: IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp build_idf_v5.5: + extends: .build_template + image: espressif/idf:release-v5.5 + parallel: + matrix: + - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] + IDF_EXAMPLE_PATH: [examples/protocols/mqtt/tcp, examples/wifi/iperf] + +build_idf_master: extends: .build_template image: espressif/idf:latest + allow_failure: true parallel: matrix: - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] From 7b347f7f547bcf66ec95384b9bb9609794586de4 Mon Sep 17 00:00:00 2001 From: LiPeng Date: Wed, 21 May 2025 19:37:57 +0800 Subject: [PATCH 19/44] Modify to allow C5 to use sdio interface --- Kconfig | 2 +- host/drivers/transport/sdio/sdio_reg.h | 2 +- slave/main/Kconfig.projbuild | 2 +- slave/main/interface.h | 2 +- slave/main/sdio_slave_api.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Kconfig b/Kconfig index 2e3c225c..2e45bddf 100644 --- a/Kconfig +++ b/Kconfig @@ -27,7 +27,7 @@ menu "ESP-Hosted config" # y if SDIO Transport is available, based on host and slave selection config ESP_HOSTED_PRIV_SDIO_OPTION bool - default y if (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && (SLAVE_IDF_TARGET_ESP32 || SLAVE_IDF_TARGET_ESP32C6) + default y if (IDF_TARGET_ESP32 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && (SLAVE_IDF_TARGET_ESP32 || SLAVE_IDF_TARGET_ESP32C6 || SLAVE_IDF_TARGET_ESP32C5) default n # y if SPI HD Transport is available, based on host and slave selection diff --git a/host/drivers/transport/sdio/sdio_reg.h b/host/drivers/transport/sdio/sdio_reg.h index 45e8c01a..f272d59a 100644 --- a/host/drivers/transport/sdio/sdio_reg.h +++ b/host/drivers/transport/sdio/sdio_reg.h @@ -69,7 +69,7 @@ #define ESP_HOST_INT_ENA_REG (ESP_SLAVE_SLCHOST_BASE + 0xDC) /* Host side interrupts for ESP_HOST_INT_ENA_REG */ -#if H_SLAVE_TARGET_ESP32 || H_SLAVE_TARGET_ESP32C6 +#if H_SLAVE_TARGET_ESP32 || H_SLAVE_TARGET_ESP32C6 || H_SLAVE_TARGET_ESP32C5 #define SDIO_INT_NEW_PACKET (23) #define SDIO_INT_START_THROTTLE (7) #define SDIO_INT_STOP_THROTTLE (6) diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index 7a7e48e5..68311903 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -34,7 +34,7 @@ menu "Example Configuration" config ESP_SDIO_HOST_INTERFACE bool "SDIO" - depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 + depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C5 help Enable/Disable SDIO host interface diff --git a/slave/main/interface.h b/slave/main/interface.h index dc1d4cef..755ff45a 100644 --- a/slave/main/interface.h +++ b/slave/main/interface.h @@ -22,7 +22,7 @@ #ifdef CONFIG_ESP_SDIO_HOST_INTERFACE -#if defined(CONFIG_IDF_TARGET_ESP32)||defined(CONFIG_IDF_TARGET_ESP32C6) +#if defined(CONFIG_IDF_TARGET_ESP32)||defined(CONFIG_IDF_TARGET_ESP32C6)||defined(CONFIG_IDF_TARGET_ESP32C5) #include "driver/sdio_slave.h" #else #error "SDIO is not supported for this chipset" diff --git a/slave/main/sdio_slave_api.h b/slave/main/sdio_slave_api.h index 43760931..4066ed51 100644 --- a/slave/main/sdio_slave_api.h +++ b/slave/main/sdio_slave_api.h @@ -17,7 +17,7 @@ #ifndef __SDIO_SLAVE_API_H #define __SDIO_SLAVE_API_H -#if defined CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C6 +#if defined CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C5 #else #error "SDIO is not supported for this target. Please use SPI" #endif From e225d1f54241f02fbc4e7302f0f07e7af066fca6 Mon Sep 17 00:00:00 2001 From: LiPeng Date: Wed, 21 May 2025 19:42:18 +0800 Subject: [PATCH 20/44] Modify sdio slave support judgment conditions --- slave/main/Kconfig.projbuild | 11 +++-------- slave/main/interface.h | 2 +- slave/main/sdio_slave_api.h | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index 68311903..8ff4dd8c 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -17,13 +17,8 @@ menu "Example Configuration" choice ESP_HOST_INTERFACE bool "Transport layer" - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32 - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32S2 - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32S3 - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32C2 - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32C3 - default ESP_SDIO_HOST_INTERFACE if IDF_TARGET_ESP32C6 - default ESP_SPI_HOST_INTERFACE if IDF_TARGET_ESP32C5 + default ESP_SDIO_HOST_INTERFACE if SOC_SDIO_SLAVE_SUPPORTED + default ESP_SPI_HOST_INTERFACE help Bus interface to be used for communication with the host @@ -34,7 +29,7 @@ menu "Example Configuration" config ESP_SDIO_HOST_INTERFACE bool "SDIO" - depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32C5 + depends on SOC_SDIO_SLAVE_SUPPORTED help Enable/Disable SDIO host interface diff --git a/slave/main/interface.h b/slave/main/interface.h index 755ff45a..09711247 100644 --- a/slave/main/interface.h +++ b/slave/main/interface.h @@ -22,7 +22,7 @@ #ifdef CONFIG_ESP_SDIO_HOST_INTERFACE -#if defined(CONFIG_IDF_TARGET_ESP32)||defined(CONFIG_IDF_TARGET_ESP32C6)||defined(CONFIG_IDF_TARGET_ESP32C5) +#if CONFIG_SOC_SDIO_SLAVE_SUPPORTED #include "driver/sdio_slave.h" #else #error "SDIO is not supported for this chipset" diff --git a/slave/main/sdio_slave_api.h b/slave/main/sdio_slave_api.h index 4066ed51..0e09408b 100644 --- a/slave/main/sdio_slave_api.h +++ b/slave/main/sdio_slave_api.h @@ -17,7 +17,7 @@ #ifndef __SDIO_SLAVE_API_H #define __SDIO_SLAVE_API_H -#if defined CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C5 +#if CONFIG_SOC_SDIO_SLAVE_SUPPORTED #else #error "SDIO is not supported for this target. Please use SPI" #endif From 8165d0e279e140cf17e4fc8bdb46750088f53af3 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 27 May 2025 12:54:21 +0800 Subject: [PATCH 21/44] feature(esp32c5_eco2_sdio) Added ESP32-C5 SDIO support - updated SDIO slave configuration - updated documents --- README.md | 8 ++- docs/images/PerformanceSetup-ShieldBox.png | Bin 0 -> 114919 bytes docs/sdio.md | 62 +++++++++++++++------ idf_component.yml | 2 +- slave/main/Kconfig.projbuild | 8 ++- 5 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 docs/images/PerformanceSetup-ShieldBox.png diff --git a/README.md b/README.md index 43135b72..32167ec2 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Legends: - Bluetooth uses dedicated platform, UART and Wi-Fi uses any other base transport - In other platforms, Bluetooth and Wi-Fi re-use same platform and hence use less GPIOs and less complicated - This transport combination allows Bluetooth to use dedicated uart transportt with additional 2 or 4 depending on hardware flow control. -- (S) : Sheild box reading +- (S) : Shield box reading - (O) : Over the air reading - TBD : To be determined - iperf : iperf2 with test resukts in mbps @@ -163,8 +163,8 @@ Legends: | Standard SPI | FD | 6 | jumper or PCB | Any_Slave | udp: 24 tcp: 22 | udp: 25 tcp: 22| Simplest solution for quick test | | Dual SPI | HD | 5 | jumper or PCB | Any_Slave [1] | udp: 32 tcp: 26 (O) | udp: 33 tcp: 25 (O) | Better throughput, but half duplex | | Quad SPI | HD | 7 | PCB only | Any_Slave [1] | udp: 41 tcp: 29 (O) | udp: 42 tcp: 28 (O) | Due to signal integrity, PCB is mandatory | -| SDIO 1-Bit | HD | 4 | jumper or PCB | ESP32, ESP32-C6 | TBD | TBD | Stepping stone for PCB based SDIO 4-bit | -| SDIO 4-Bit | HD | 6 | PCB only | ESP32, ESP32-C6 | udp: 79.5 tcp: 53.4 (S) | udp: 68.1 tcp: 44 (S) | Highest performance | +| SDIO 1-Bit | HD | 4 | jumper or PCB | ESP32, ESP32-C6, ESP32-C5 [3] | TBD | TBD | Stepping stone for PCB based SDIO 4-bit | +| SDIO 4-Bit | HD | 6 | PCB only | ESP32, ESP32-C6, ESP32-C5 [3] | udp: 79.5 tcp: 53.4 (S) | udp: 68.1 tcp: 44 (S) | Highest performance | | Only BT over UART | FD | 2 or 4 | jumper or PCB | Any_Slave | NA | NA | Dedicated Bluetooth over UART pins | | UART | FD | 2 | jumper or PCB | Any_Slave | udp: 0.68 tcp: 0.67 (O) | udp: 0.68 tcp: 0.60 (O) | UART dedicated for BT & Wi-Fi [2] | | Dedicated platforms | FD | Extra 2 or 4 | jumper or PCB | Any_Slave | NA | NA | UART dedicated for BT & Wi-Fi on any other transport | @@ -173,6 +173,8 @@ Legends: > - [1] Dual/Quad SPI is not supported on ESP32 > > - [2] UART is only suitable for low throughput environments +> +> - [3] Currently in BETA support for ESP32-C5 (`--preview` in ESP-IDF master branch) With jumper cables, 'Standard SPI' and 'Dual SPI' solutions are easiest to evaluate, without much of hardware dependencies. SDIO 1-Bit can be tested with jumper cables, but it needs some additional hardware config, such as installation of external pull-up registers. diff --git a/docs/images/PerformanceSetup-ShieldBox.png b/docs/images/PerformanceSetup-ShieldBox.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe02fa45a445f59435f02dd9da4b70c93aa23cf GIT binary patch literal 114919 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

(ca$C^Bmh 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 i_y22#|0~UKg-4Ee>p [!NOTE] +> Support for the ESP32-C5 is currently in BETA (`--preview` in ESP-IDF master branch) -### Co-processor connections +ESP32-P4 Function EV Board GPIO setup to use with a ESP32-C5 Test Board: + +| Signal | ESP32-P4 | +|-----------|----------| +| CLK | 33 | +| Reset Out | 53 | +| CMD | 4+[ext-pull-up](#34-pull-up-resistors) | +| DAT0 | 20+[ext-pull-up](#34-pull-up-resistors) | +| DAT1 | 23+[ext-pull-up](#34-pull-up-resistors) | +| DAT2 | 21+[ext-pull-up](#34-pull-up-resistors) | +| DAT3 | 22+[ext-pull-up](#34-pull-up-resistors) | + +Current iPerf numbers, connected to a 5G Network and running iPerf (TCP and UDP tests). -| Signal | ESP32 | ESP32-C6 | -|-------------|-------|----------| -| CLK | 14 | 19 | -| CMD | 15 | 18 | -| DAT0 | 2 | 20 | -| DAT1 | 4 | 21 | -| DAT2 | 12 | 22 | -| DAT3 | 13 | 23 | -| Reset In | EN | EN/RST | +| | Direction | Throughput (MBits/s) | +|-----|-----------|----------------------| +| UDP | PC to P4 | 60 | +| | P4 to PC | 51 | +| TCP | PC to P4 | 38 | +| | P4 to PC | 22 | +- PC to P4 + - PC -> Router -> ESP Co-processor --> SDIO --> ESP32-P4 +- P4 to PC + - ESP32-P4 --> SDIO --> ESP Co-processor -> Router ->PC + +Testing using a shield box. For open air: similar setup, just with no shield box. + +iPerf testing setup - Shield box way + +### Co-processor connections + +| Signal | ESP32 | ESP32-C6 | ESP32-C5 | +|-------------|-------|----------|----------| +| CLK | 14 | 19 | 9 | +| CMD | 15 | 18 | 10 | +| DAT0 | 2 | 20 | 8 | +| DAT1 | 4 | 21 | 7 | +| DAT2 | 12 | 22 | 14 | +| DAT3 | 13 | 23 | 13 | +| Reset In | EN | EN/RST | RST | > [!NOTE] > > A. Try to use IO_MUX pins from the datasheet for optimal performance on both sides. \ > B. These GPIO assignments are based on default Kconfig configurations. You can modify these in the menuconfig for both host and co-processor if needed. \ > C. Once ported, any other host with standard SDIO can be used. \ -> D. ESP32, ESP32-S3, and ESP32-P4 can be used as hosts; ESP32 and ESP32-C6 can be used as co-processors in SDIO mode. \ +> D. ESP32, ESP32-S3, and ESP32-P4 can be used as hosts; ESP32, ESP32-C6 and ESP32-C5 can be used as co-processors in SDIO mode. \ > E. External pull-ups are mandatory ## 5 Set-Up ESP-IDF @@ -189,8 +220,8 @@ Please follow the [ESP-IDF Get Started Guide](https://docs.espressif.com/project ## 6. Flashing the Co-processor -| Supported Co-processor Targets | ESP32 | ESP32-C6 | -| ------------------------------ | ----- | -------- | +| Supported Co-processor Targets | ESP32 | ESP32-C6 | ESP32-C5 | +| ------------------------------ | ----- | -------- | -------- | There are four steps to flash the ESP-Hosted co-processor firmware: @@ -540,4 +571,3 @@ After flashing both the co-processor and host devices, follow these steps to con - [ESP32 Hardware Design Guidelines](https://www.espressif.com/en/products/hardware/esp32/resources) - [SDIO Protocol Basics](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface) - [ESP SDIO Slave Communication](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/protocols/esp_sdio_slave_protocol.html) - diff --git a/idf_component.yml b/idf_component.yml index a069e605..b2729a31 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.2" +version: "2.0.3" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index 8ff4dd8c..f1e96a6b 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -280,12 +280,13 @@ menu "Example Configuration" bool "High Speed (40 MHz)" endchoice - + # from /components/soc//include/soc/sdio_slave_pins.h menu "Hosted SDIO GPIOs" config ESP_SDIO_PIN_CMD int "CMD GPIO number" range 15 15 if IDF_TARGET_ESP32 range 18 18 if IDF_TARGET_ESP32C6 + range 10 10 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." @@ -293,6 +294,7 @@ menu "Example Configuration" int "CLK GPIO number" range 14 14 if IDF_TARGET_ESP32 range 19 19 if IDF_TARGET_ESP32C6 + range 9 9 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." @@ -300,6 +302,7 @@ menu "Example Configuration" int "D0 GPIO number" range 2 2 if IDF_TARGET_ESP32 range 20 20 if IDF_TARGET_ESP32C6 + range 8 8 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." @@ -307,6 +310,7 @@ menu "Example Configuration" int "D1 GPIO number" range 4 4 if IDF_TARGET_ESP32 range 21 21 if IDF_TARGET_ESP32C6 + range 7 7 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." @@ -314,6 +318,7 @@ menu "Example Configuration" int "D2 GPIO number" range 12 12 if IDF_TARGET_ESP32 range 22 22 if IDF_TARGET_ESP32C6 + range 14 14 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." @@ -321,6 +326,7 @@ menu "Example Configuration" int "D3 GPIO number" range 13 13 if IDF_TARGET_ESP32 range 23 23 if IDF_TARGET_ESP32C6 + range 13 13 if IDF_TARGET_ESP32C5 help "Value cannot be configured. Displayed for reference." endmenu From cecacefe579e45969fe33087bc1ac03b56e39b59 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 15 May 2025 15:31:06 +0800 Subject: [PATCH 22/44] bugfix(bt_transport_ll_deinit): Add bt_transport_ll_deinit - prevent linker error when calling `nimble_port_deinit()` - bt_transport_ll_deinit() called via `nimble_port_deinit()` --- host/drivers/bt/vhci_drv.c | 5 +++++ idf_component.yml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/host/drivers/bt/vhci_drv.c b/host/drivers/bt/vhci_drv.c index 97e9a999..2a52cd9a 100644 --- a/host/drivers/bt/vhci_drv.c +++ b/host/drivers/bt/vhci_drv.c @@ -135,6 +135,11 @@ void ble_transport_ll_init(void) { ESP_ERROR_CHECK(transport_drv_reconfigure()); } + +void ble_transport_ll_deinit(void) +{ + // transport may still be in used for other data (serial, Wi-Fi, ...) +} #endif int ble_transport_to_ll_acl_impl(struct os_mbuf *om) diff --git a/idf_component.yml b/idf_component.yml index b2729a31..09761db5 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.3" +version: "2.0.4" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 6acaf65b29aeb510cc807cbed4b9f8630935b920 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 4 Jun 2025 14:29:57 +0800 Subject: [PATCH 23/44] fix: Kconfig GPIO pins in use for C5 and C6 to auto change minor GPIO sequnce log changes to match to doc --- Kconfig | 172 +++++++++++++++++++---- host/port/esp/freertos/src/spi_wrapper.c | 4 +- slave/main/spi_slave_api.c | 4 +- 3 files changed, 145 insertions(+), 35 deletions(-) diff --git a/Kconfig b/Kconfig index 2e45bddf..0ef74a31 100644 --- a/Kconfig +++ b/Kconfig @@ -447,14 +447,6 @@ ESP32XX_SPI_CLK_FREQ_RANGE_MAX := 40 help Please check your schematic first and input your LDO ID. - config ESP_HOSTED_SDIO_GPIO_RESET_SLAVE - int "GPIO pin for Reseting slave ESP" - default 54 if IDF_TARGET_ESP32P4 - default 42 if IDF_TARGET_ESP32S3 - default 5 - help - GPIO pin for Resetting ESP SDIO slave device. Should be connected to RST/EN of ESP SDIO slave device. - choice prompt "SDIO Bus Width" default ESP_HOSTED_SDIO_4_BIT_BUS @@ -486,6 +478,116 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 help "Optimize SDIO CLK by increasing till host practically can support. Clock frequency for ESP32-P4 as host <= 40MHz" + config ESP_HOSTED_CUSTOM_SDIO_PINS + bool "Use custom SDIO GPIO pins" + default n if IDF_TARGET_ESP32 + default n + help + Enable this to use custom GPIO pins for SDIO on ESP32-P4 and ESP32-S3. + ESP32 has fixed GPIOs for SDIO host (Do not use custom GPIOs) + ESP32-P4 slot 0 is fixed GPIOs for SDIO host (Do not use custom GPIOs) + ESP32-P4 slot 1, ESP32-S3 slot 0/1 is flexible GPIOs for SDIO host (Flexible GPIOs) + + config ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MIN + int + default 4 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 15 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MAX + int + default 4 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 15 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MIN + int + default 33 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 14 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MAX + int + default 33 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 14 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_D0_GPIO_RANGE_MIN + int + default 20 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 2 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_D0_GPIO_RANGE_MAX + int + default 20 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 2 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN + int + default 23 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 4 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX + int + default 23 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 4 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_D2_GPIO_RANGE_MIN + int + default 21 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 12 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_D2_GPIO_RANGE_MAX + int + default 21 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 12 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_D3_GPIO_RANGE_MIN + int + default 22 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 13 if IDF_TARGET_ESP32 + default 0 + + config ESP_HOSTED_SDIO_D3_GPIO_RANGE_MAX + int + default 22 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 + default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 13 if IDF_TARGET_ESP32 + default 100 + + config ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MIN + int + default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 53 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 42 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32S3 + default 0 + help + GPIO pin for Resetting ESP SDIO slave device. Should be connected to RST/EN of ESP SDIO slave device. + + config ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MAX + int + default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 + default 53 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 42 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32S3 + default 100 + ### *START* GPIO SDIO pin configurations for Slot 0 and 1 config ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_0 depends on ESP_HOSTED_SDIO_SLOT_0 @@ -500,10 +602,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "CMD GPIO number" default 47 if IDF_TARGET_ESP32S3 - default 19 if IDF_TARGET_ESP32P4 - range 15 15 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MIN ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + CMD GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. config ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_0 depends on ESP_HOSTED_SDIO_SLOT_0 @@ -518,10 +620,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "CLK GPIO number" default 19 if IDF_TARGET_ESP32S3 - default 18 if IDF_TARGET_ESP32P4 - range 14 14 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MIN ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + CLK GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. config ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_0 depends on ESP_HOSTED_SDIO_SLOT_0 @@ -536,10 +638,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D0 GPIO number" default 13 if IDF_TARGET_ESP32S3 - default 14 if IDF_TARGET_ESP32P4 - range 2 2 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_D0_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D0_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + D0 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. if ESP_HOSTED_SDIO_4_BIT_BUS config ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_0 @@ -555,10 +657,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number" default 35 if IDF_TARGET_ESP32S3 - default 15 if IDF_TARGET_ESP32P4 - range 4 4 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + D1 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. config ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_0 depends on ESP_HOSTED_SDIO_SLOT_0 @@ -573,10 +675,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D2 GPIO number" default 20 if IDF_TARGET_ESP32S3 - default 16 if IDF_TARGET_ESP32P4 - range 12 12 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_D2_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D2_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + D2 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. config ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_0 depends on ESP_HOSTED_SDIO_SLOT_0 @@ -591,10 +693,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D3 GPIO number" default 9 if IDF_TARGET_ESP32S3 - default 17 if IDF_TARGET_ESP32P4 - range 13 13 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_D3_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D3_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + D3 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. endif if !ESP_HOSTED_SDIO_4_BIT_BUS @@ -610,11 +712,19 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number (Interrupt Line)" default 35 if IDF_TARGET_ESP32S3 - default 15 if IDF_TARGET_ESP32P4 - range 4 4 if IDF_TARGET_ESP32 + range ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX help - "Value can only be configured for some targets. Displayed always for reference." + D1 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. + Enable 'Use custom SDIO GPIO pins' to disable range restrictions. endif + + config ESP_HOSTED_SDIO_GPIO_RESET_SLAVE + int "GPIO pin for Reseting slave ESP" + range ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MIN ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MAX + default 5 + help + GPIO pin for Resetting ESP SDIO slave device. Should be connected to RST/EN of ESP SDIO slave device. + ### *END* GPIO SDIO pin configurations for Slot 0 and 1 config ESP_HOSTED_SDIO_PIN_CMD @@ -991,14 +1101,14 @@ ESP32XX_SPI_HD_CLK_FREQ_RANGE_MAX := 40 ENABLE/DISABLE software UART checksum endmenu - config ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE + config ESP_HOSTED_GPIO_SLAVE_RESET_SLAVE int default ESP_HOSTED_SPI_GPIO_RESET_SLAVE if ESP_HOSTED_SPI_HOST_INTERFACE default ESP_HOSTED_SDIO_GPIO_RESET_SLAVE if ESP_HOSTED_SDIO_HOST_INTERFACE default ESP_HOSTED_SPI_HD_GPIO_RESET_SLAVE if ESP_HOSTED_SPI_HD_HOST_INTERFACE default ESP_HOSTED_UART_GPIO_RESET_SLAVE if ESP_HOSTED_UART_HOST_INTERFACE - config ESP_HOSTED_RESET_GPIO_ACTIVE_LOW + config ESP_HOSTED_RESET_GPIO_ACTIVE_LOW bool default n if ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH || ESP_HOSTED_SPI_RESET_ACTIVE_HIGH || ESP_HOSTED_SPI_HD_RESET_ACTIVE_HIGH || ESP_HOSTED_UART_RESET_ACTIVE_HIGH default y if ESP_HOSTED_SDIO_RESET_ACTIVE_LOW || ESP_HOSTED_SPI_RESET_ACTIVE_LOW || ESP_HOSTED_SPI_HD_RESET_ACTIVE_LOW || ESP_HOSTED_UART_RESET_ACTIVE_LOW diff --git a/host/port/esp/freertos/src/spi_wrapper.c b/host/port/esp/freertos/src/spi_wrapper.c index 88b8edd3..56cee511 100644 --- a/host/port/esp/freertos/src/spi_wrapper.c +++ b/host/port/esp/freertos/src/spi_wrapper.c @@ -51,9 +51,9 @@ void * hosted_spi_init(void) esp_err_t ret; - ESP_LOGI(TAG, "Transport: SPI, Mode:%u Freq:%uMHz TxQ:%u RxQ:%u\n GPIOs: MOSI:%u MISO:%u CLK:%u CS:%u HS:%u DR:%u SlaveReset:%u", + ESP_LOGI(TAG, "Transport: SPI, Mode:%u Freq:%uMHz TxQ:%u RxQ:%u\n GPIOs: CLK:%u MOSI:%u MISO:%u CS:%u HS:%u DR:%u SlaveReset:%u", H_SPI_MODE, H_SPI_INIT_CLK_MHZ, H_SPI_TX_Q, H_SPI_RX_Q, - H_GPIO_MOSI_Pin, H_GPIO_MISO_Pin, H_GPIO_SCLK_Pin, + H_GPIO_SCLK_Pin, H_GPIO_MOSI_Pin, H_GPIO_MISO_Pin, H_GPIO_CS_Pin, H_GPIO_HANDSHAKE_Pin, H_GPIO_DATA_READY_Pin, H_GPIO_PIN_RESET_Pin); diff --git a/slave/main/spi_slave_api.c b/slave/main/spi_slave_api.c index d00eaed4..a89d71c8 100644 --- a/slave/main/spi_slave_api.c +++ b/slave/main/spi_slave_api.c @@ -835,9 +835,9 @@ static interface_handle_t * esp_spi_init(void) gpio_set_pull_mode(GPIO_SCLK, GPIO_PULLUP_ONLY); gpio_set_pull_mode(GPIO_CS, GPIO_PULLUP_ONLY); - ESP_LOGI(TAG, "SPI Ctrl:%u mode: %u, Freq:ConfigAtHost\nGPIOs: MOSI: %u, MISO: %u, CS: %u, CLK: %u HS: %u DR: %u\n", + ESP_LOGI(TAG, "SPI Ctrl:%u mode: %u, Freq:ConfigAtHost\nGPIOs: CLK:%u MOSI:%u MISO:%u CS:%u HS:%u DR:%u\n", ESP_SPI_CONTROLLER, slvcfg.mode, - GPIO_MOSI, GPIO_MISO, GPIO_CS, GPIO_SCLK, + GPIO_SCLK, GPIO_MOSI, GPIO_MISO, GPIO_CS, GPIO_HANDSHAKE, GPIO_DATA_READY); ESP_LOGI(TAG, "Hosted SPI queue size: Tx:%u Rx:%u", SPI_TX_QUEUE_SIZE, SPI_RX_QUEUE_SIZE); From 1553244c827f480a456b79d2ffbb436a0f1439f7 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 4 Jun 2025 14:31:38 +0800 Subject: [PATCH 24/44] doc: update sdio doc to make it cleaner separate shield box and optimization docs --- docs/esp32_p4_function_ev_board.md | 21 +++--- docs/performance_optimization.md | 106 +++++++++++++++++++++++++++++ docs/sdio.md | 93 ++++++++++++------------- docs/shield-box-test-setup.md | 75 ++++++++++++++++++++ docs/spi_full_duplex.md | 4 +- docs/spi_half_duplex.md | 4 +- docs/uart.md | 4 +- 7 files changed, 243 insertions(+), 64 deletions(-) create mode 100644 docs/performance_optimization.md create mode 100644 docs/shield-box-test-setup.md diff --git a/docs/esp32_p4_function_ev_board.md b/docs/esp32_p4_function_ev_board.md index 368519b4..d8029062 100644 --- a/docs/esp32_p4_function_ev_board.md +++ b/docs/esp32_p4_function_ev_board.md @@ -3,21 +3,20 @@

Table of Contents -- [Introduction](#1-introduction) -- [Set-Up ESP-IDF](#2-set-up-esp-idf) -- [Building Host for the P4](#3-building-host-for-the-p4) +- [1. Introduction](#1-introduction) +- [2. Set-Up ESP-IDF](#2-set-up-esp-idf) +- [3. Building Host for the P4](#3-building-host-for-the-p4) - [Adding Components](#31-adding-components) - [Configuring Defaults](#32-configuring-defaults) - [Building Firmware](#33-building-firmware) -- [Checking ESP-Hosted](#4-checking-esp-hosted) -- [Flashing ESP32-C6](#5-flashing-esp32-c6) +- [4. Checking ESP-Hosted](#4-checking-esp-hosted) +- [5. Flashing ESP32-C6](#5-flashing-esp32-c6) - [Using ESP-Prog](#51-using-esp-prog) - [OTA Updates](#52-ota-updates) -- [Troubleshooting](#6-troubleshooting) -- [Flashing the On-board ESP32-P4 through the ESP-Prog](#7-flashing-esp32-p4) -- [Testing ESP-Hosted with SPI-FD with other MCUs](#8-testing-esp-hosted-with-spi-fd-with-other-mcus) -- [References](#9-references) - +- [6. Troubleshooting](#6-troubleshooting) +- [7. Flashing the On-board ESP32-P4 through the ESP-Prog](#7-flashing-esp32-p4) +- [8. Testing ESP-Hosted with SPI-FD with other MCUs](#8-testing-esp-hosted-with-spi-fd-with-other-mcus) +- [9. References](#10-references)
## 1. Introduction @@ -382,6 +381,8 @@ Use GPIOs 36 or lower on the P4 DevKit to avoid LDO power issues with high numbe > [!NOTE] > Avoid using GPIO 35 and 36 as they affect the ESP32-P4 Bootloader Mode. See [ESP32-P4 Boot Mode Selection](https://docs.espressif.com/projects/esptool/en/latest/esp32p4/advanced-topics/boot-mode-selection.html#select-bootloader-mode) for more information. +Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). + ## 9. References - ESP32-P4-Function-EV-Board: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/ diff --git a/docs/performance_optimization.md b/docs/performance_optimization.md new file mode 100644 index 00000000..9851bdfd --- /dev/null +++ b/docs/performance_optimization.md @@ -0,0 +1,106 @@ +# ESP-Hosted Performance Optimization Guide + +Quick reference for optimizing ESP-Hosted performance across different transport interfaces. + +## Quick Start - High Performance Config + +For immediate performance gains, add these to your host's `sdkconfig.defaults.esp32XX` file: + +``` +# Wi-Fi Performance +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=64 +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=32 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=32 + +# TCP/IP Performance +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534 +CONFIG_LWIP_TCP_WND_DEFAULT=65534 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=64 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=64 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 +CONFIG_LWIP_TCP_SACK_OUT=y +``` + +> **Note**: Adjust values based on your MCU host's memory capacity and as per change as per build system + +## Transport Optimization + +### SDIO (Highest Performance) +- **Clock Speed**: Start at 20 MHz, optimize up to 50 MHz +- **Bus Width**: Use 4-bit mode +- **Hardware**: Use PCB with controlled impedance, external pull-ups (51kΩ) +- **Checksum**: Optional (SDIO hardware handles verification) + +``` +CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=40000 +CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH=4 +``` + +### SPI Full-Duplex +- **Clock Speed**: ESP32: ≤10 MHz, Others: ≤40 MHz +- **Hardware**: Use IO_MUX pins, short traces (≤10cm for jumpers) +- **Checksum**: Mandatory (SPI hardware lacks error detection) + +``` +CONFIG_ESP_HOSTED_SPI_CLK_FREQ=40 +``` + +### SPI Half-Duplex +- **Data Lines**: Use 4-line (Quad SPI) mode +- **Similar optimizations as SPI Full-Duplex** + +### UART (Lowest Performance) +- **Baud Rate**: Use 921600 (highest stable rate) +- **Best for**: Low-throughput applications, debugging + +## Memory Optimization + +- Reduce memory footprint for resource-constrained applications: + + ``` + # Reduce queue sizes + CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE=10 # Default: 20 + CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE=10 # Default: 20 + + # Enable memory pooling + CONFIG_ESP_HOSTED_USE_MEMPOOL=y + ``` + +- Disable the not-in-use features + - For example, disable bluetooth if not needed +- Use external RAM, for higher memory (PSRAM is supported) +- Optimise internal RAM using [ESP-IDF iram optimization tricks](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/performance/ram-usage.html) +## Hardware Guidelines + +### Critical Requirements +1. **Signal Integrity**: Use PCB designs for production, jumpers only for prototyping +2. **Power Supply**: Stable 3.3V, proper decoupling capacitors +3. **Trace Length**: Match lengths, especially clock vs data lines +4. **Pull-ups**: Required for SDIO (51kΩ) on CMD, D0-D3 lines + +### PCB Design Checklist +- [ ] Equal trace lengths for communication signals +- [ ] Ground plane for signal stability +- [ ] Controlled impedance traces (50Ω typical) +- [ ] Series termination resistors for high-speed signals +- [ ] Extra GPIOs reserved for future features (deep sleep, etc.) + + + +## Development Workflow +1. **Proof of Concept**: Start with jumper wires, low clock speeds +2. **Incremental Optimization**: Increase transport clock step by step +3. **Hardware Validation**: Move to PCB for final validation +4. **Performance Tuning**: Optimize buffers and configurations + + + + + +4. **Disable features**: Any unsued components from ESP-IDF or +ESP-Hosted-MCU features could be disabled for more memory +availability. diff --git a/docs/sdio.md b/docs/sdio.md index 3d838491..a1d41993 100644 --- a/docs/sdio.md +++ b/docs/sdio.md @@ -127,72 +127,61 @@ For optimal performance and reliability in production designs: Setting up the hardware involves connecting the master and co-processor devices via the SDIO pins and ensuring all extra GPIO signals are properly connected. Below is the table of connections for the SDIO setup between a host ESP chipset and another ESP chipset as co-processor: -### Host connections +### Host Connections -| Signal | ESP32 | ESP32-S3 | ESP32-P4-Function-EV-Board | -|-------------|-------|----------|----------| -| CLK | 14 | 19 | 18 | -| Reset Out | 5 | 42 | 54 | -| CMD | 15+[ext-pull-up](#34-pull-up-resistors) | 47+[ext-pull-up](#34-pull-up-resistors) | 19+[ext-pull-up](#34-pull-up-resistors) | -| DAT0 | 2+[ext-pull-up](#34-pull-up-resistors) | 13+[ext-pull-up](#34-pull-up-resistors) | 14+[ext-pull-up](#34-pull-up-resistors) | -| DAT1 | 4+[ext-pull-up](#34-pull-up-resistors) | 35+[ext-pull-up](#34-pull-up-resistors) | 15+[ext-pull-up](#34-pull-up-resistors) | -| DAT2 | 12+[ext-pull-up](#34-pull-up-resistors) | 20+[ext-pull-up](#34-pull-up-resistors) | 16+[ext-pull-up](#34-pull-up-resistors) | -| DAT3 | 13+[ext-pull-up](#34-pull-up-resistors) | 9+[ext-pull-up](#34-pull-up-resistors) | 17+[ext-pull-up](#34-pull-up-resistors) | +SDIO-capable host microcontrollers (MCUs) can connect their GPIO lines to the co-processor as detailed in the table below. -#### ESP32-P4-Function-EV-Board with ESP32-C5 Test Board +#### GPIO Flexibility -> [!NOTE] -> Support for the ESP32-C5 is currently in BETA (`--preview` in ESP-IDF master branch) - -ESP32-P4 Function EV Board GPIO setup to use with a ESP32-C5 Test Board: +- The ESP32 supports SDIO host on fixed GPIOs. +- The ESP32-S3 supports SDIO host on flexible GPIOs. +- For the ESP32-P4, Slot 0 supports fixed GPIOs, while Slot 1 supports flexible GPIOs. -| Signal | ESP32-P4 | -|-----------|----------| -| CLK | 33 | -| Reset Out | 53 | -| CMD | 4+[ext-pull-up](#34-pull-up-resistors) | -| DAT0 | 20+[ext-pull-up](#34-pull-up-resistors) | -| DAT1 | 23+[ext-pull-up](#34-pull-up-resistors) | -| DAT2 | 21+[ext-pull-up](#34-pull-up-resistors) | -| DAT3 | 22+[ext-pull-up](#34-pull-up-resistors) | +By default, Slot 1 is used on the ESP32-P4 to take advantage of its flexible pin mapping; however, Slot 0 is also supported. Parallel access to both Slot 0 and Slot 1 is supported for all hosts. -Current iPerf numbers, connected to a 5G Network and running iPerf (TCP and UDP tests). -| | Direction | Throughput (MBits/s) | -|-----|-----------|----------------------| -| UDP | PC to P4 | 60 | -| | P4 to PC | 51 | -| TCP | PC to P4 | 38 | -| | P4 to PC | 22 | +| Signal | ESP32 | ESP32-S3 | +|-----------|-------|----------| +| CLK | 14 | 19 | +| CMD | 15+[ext-pull-up](#34-pull-up-resistors) | 47+[ext-pull-up](#34-pull-up-resistors) | +| D0 | 2+[ext-pull-up](#34-pull-up-resistors) | 13+[ext-pull-up](#34-pull-up-resistors) | +| D1 | 4+[ext-pull-up](#34-pull-up-resistors) | 35+[ext-pull-up](#34-pull-up-resistors) | +| D2 | 12+[ext-pull-up](#34-pull-up-resistors) | 20+[ext-pull-up](#34-pull-up-resistors) | +| D3 | 13+[ext-pull-up](#34-pull-up-resistors) | 9+[ext-pull-up](#34-pull-up-resistors) | +| Reset Out | 5 | 42 | -- PC to P4 - - PC -> Router -> ESP Co-processor --> SDIO --> ESP32-P4 -- P4 to PC - - ESP32-P4 --> SDIO --> ESP Co-processor -> Router ->PC +### ESP32-P4-Function-EV-Board Host Pin Mapping -Testing using a shield box. For open air: similar setup, just with no shield box. +| Signal | ESP32-P4 with ESP32-C6 Co-processor | ESP32-P4 with ESP32-C5 Co-processor | +|-----------|-------------------------------------|-------------------------------------| +| CLK | 18 | 33 | +| CMD | 19+[ext-pull-up](#34-pull-up-resistors) | 4+[ext-pull-up](#34-pull-up-resistors) | +| D0 | 14+[ext-pull-up](#34-pull-up-resistors) | 20+[ext-pull-up](#34-pull-up-resistors) | +| D1 | 15+[ext-pull-up](#34-pull-up-resistors) | 23+[ext-pull-up](#34-pull-up-resistors) | +| D2 | 16+[ext-pull-up](#34-pull-up-resistors) | 21+[ext-pull-up](#34-pull-up-resistors) | +| D3 | 17+[ext-pull-up](#34-pull-up-resistors) | 22+[ext-pull-up](#34-pull-up-resistors) | +| Reset Out | 54 | 53 | -iPerf testing setup - Shield box way +> ### Co-processor connections -| Signal | ESP32 | ESP32-C6 | ESP32-C5 | -|-------------|-------|----------|----------| -| CLK | 14 | 19 | 9 | -| CMD | 15 | 18 | 10 | -| DAT0 | 2 | 20 | 8 | -| DAT1 | 4 | 21 | 7 | -| DAT2 | 12 | 22 | 14 | -| DAT3 | 13 | 23 | 13 | -| Reset In | EN | EN/RST | RST | +SDIO slave provider ESP chips are : ESP32, ESP32-C5, ESP32-C6.\ +All these chips have fixed GPIOs SDIO support. + +| Signal | ESP32 | ESP32-C6 | ESP32-C5 | +|----------|-------|----------|----------| +| CLK | 14 | 19 | 9 | +| CMD | 15 | 18 | 10 | +| D0 | 2 | 20 | 8 | +| D1 | 4 | 21 | 7 | +| D2 | 12 | 22 | 14 | +| D3 | 13 | 23 | 13 | +| Reset In | EN | EN/RST | RST | > [!NOTE] > -> A. Try to use IO_MUX pins from the datasheet for optimal performance on both sides. \ -> B. These GPIO assignments are based on default Kconfig configurations. You can modify these in the menuconfig for both host and co-processor if needed. \ -> C. Once ported, any other host with standard SDIO can be used. \ -> D. ESP32, ESP32-S3, and ESP32-P4 can be used as hosts; ESP32, ESP32-C6 and ESP32-C5 can be used as co-processors in SDIO mode. \ -> E. External pull-ups are mandatory +> - External pull-ups are mandatory ## 5 Set-Up ESP-IDF @@ -555,6 +544,8 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. + [!NOTE] Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). + 8. Troubleshooting: - Consider using a lower clock speed or checking your [hardware setup](docs/sdio.md#7-hardware-setup) if you experience communication problems. - ESP-Hosted-MCU troubleshooting guide: [docs/troubleshooting.md](docs/troubleshooting.md) diff --git a/docs/shield-box-test-setup.md b/docs/shield-box-test-setup.md new file mode 100644 index 00000000..c107a3a5 --- /dev/null +++ b/docs/shield-box-test-setup.md @@ -0,0 +1,75 @@ +# Shield Box Test Setup for ESP-Hosted + +Controlled RF environment for consistent throughput measurements and performance evaluation. + +## Overview + +**Shield Box Testing** uses RF-shielded enclosure to eliminate external interference and provide repeatable test conditions. + +**Key Benefits:** +- Controlled RF environment (no external Wi-Fi/cellular interference) +- Repeatable, consistent results +- Better measurement accuracy vs open air + + +## Equipment Required + +### Essential Components +- **RF Shield Box/Chamber**: Faraday cage enclosure +- **ESP32-P4 Function EV Board**: Host device +- **ESP32-C6/C5 Test Board**: Co-processor device +- **External PC**: For iPerf client/server +- **Router/Access Point**: Wi-Fi infrastructure +- **Ethernet Connection**: Wired backhaul to PC + +Please change the host and co-processor nodes as per current use-case under test. + +## Test Setup + +### Physical Configuration + +Shield box testing setup + +### Data Flow +- **PC to MCU Host**: + ``` + PC -> Router -> ESP Co-processor == SDIO/SPI/UART ==> ESP32-P4 + ``` +- **MCU Host to PC**: + ``` + PC <- Router <- ESP Co-processor <== SDIO/SPI/UART == ESP32-P4 + ``` + +**Traffic route:** +- PC-to-Router: Ethernet with static IP (eliminates wireless variables) +- Router-to-ESP: Wi-Fi connection (only wireless link in test chain) + +## Transport Configurations + +### SDIO (Highest Performance) +- **Clock**: 20-50 MHz (start low, optimize up) +- **Bus Width**: 4-bit mode +- **Hardware**: External pull-ups (51kΩ) on CMD, D0-D3 + +### SPI +- **Clock**: ESP32: ≤10 MHz, Others: ≤40 MHz +- **Mode**: Full-duplex (simple) or Quad SPI (highest throughput) + +### UART +- **Baud Rate**: 921600 (highest stable rate) +- **Use Case**: Low-throughput validation, debugging + + +## Shield Box vs Open Air + +| Aspect | Shield Box | Open Air | +|--------|------------|----------| +| **Repeatability** | High | Variable | +| **Interference** | Eliminated | Present | +| **Debugging** | Easier | Complex | +| **Reality** | Lower | Higher | + + +--- + +*For transport setup details: [SDIO](sdio.md) | [SPI Full-Duplex](spi_full_duplex.md) | [SPI Half-Duplex](spi_half_duplex.md) | [UART](uart.md)* \ No newline at end of file diff --git a/docs/spi_full_duplex.md b/docs/spi_full_duplex.md index 6e62b978..72398ed0 100644 --- a/docs/spi_full_duplex.md +++ b/docs/spi_full_duplex.md @@ -538,7 +538,7 @@ After flashing both the co-processor and host devices, follow these steps to con - Set Wi-Fi mode: `wifi_mode ` (where mode can be 'sta', 'ap', or 'apsta') 7. Advanced iperf testing: - Once connected, you can run iperf tests: + Once connected, you can run iperf tests to verify performance: | Test Case | Host Command | External STA Command | |-----------|--------------|----------------------| @@ -549,6 +549,8 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. + Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). + 8. Troubleshooting: - If you encounter issues, refer to section 6.3 for testing the SPI connection. - Consider using a lower clock speed or checking your [hardware connections](#5-hardware-setup) if you experience communication problems. diff --git a/docs/spi_half_duplex.md b/docs/spi_half_duplex.md index 64967538..0bed729a 100644 --- a/docs/spi_half_duplex.md +++ b/docs/spi_half_duplex.md @@ -784,7 +784,7 @@ After flashing both the co-processor and host devices, follow these steps to con - Set Wi-Fi mode: `wifi_mode ` (where mode can be 'sta', 'ap', or 'apsta') 7. Advanced iperf testing: - Once connected, you can run iperf tests: + Once connected, you can run iperf tests to verify performance: | Test Case | Host Command | External STA Command | | :-------: | :----------: | :------------------: | @@ -795,6 +795,8 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. + [!NOTE] Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). + 8. Troubleshooting: - If you encounter issues, refer to section 6.3 for testing the SPI connection. - Consider using a lower clock speed or checking your [hardware setup](#7-hardware-setup) if you experience communication problems. diff --git a/docs/uart.md b/docs/uart.md index 6e8ce079..144f4dad 100644 --- a/docs/uart.md +++ b/docs/uart.md @@ -424,7 +424,7 @@ After flashing both the co-processor and host devices, follow these steps to con - Set Wi-Fi mode: `wifi_mode ` (where mode can be 'sta', 'ap', or 'apsta') 7. Advanced iperf testing: - Once connected, you can run iperf tests: + Once connected, you can run iperf tests to verify performance: | Test Case | Host Command | External STA Command | |-----------|--------------|----------------------| @@ -435,6 +435,8 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. + Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). + 8. Troubleshooting: - If you encounter issues, refer to section 3.3 for checking the UART connection. - Consider using a lower baud rate or checking your [hardware setup](#4-hardware-setup) if you experience communication problems. From 45c23329da8bbe715a45b3577086ca96bc3bfc37 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Wed, 4 Jun 2025 15:22:13 +0800 Subject: [PATCH 25/44] fix: build on 5.5 to set the default sdio pins --- .gitlab-ci.yml | 53 ++++++++++++++++++++++----- Kconfig | 57 +++++++++++++++++++++--------- README.md | 4 +++ docs/esp32_p4_function_ev_board.md | 4 ++- docs/sdio.md | 18 +++++----- docs/spi_full_duplex.md | 17 +++++---- docs/spi_half_duplex.md | 17 +++++---- docs/uart.md | 17 +++++---- 8 files changed, 126 insertions(+), 61 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f890f383..ee58fcb4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,7 @@ before_script: - build artifacts: paths: - - "${IDF_PATH}/${IDF_EXAMPLE_PATH}/build*/build_log.txt" + - "artifacts_*/" when: always expire_in: 4 days script: @@ -21,13 +21,44 @@ before_script: # Replaces esp_hosted component in example's deps with the one from the current repository - export OVERRIDE_PATH=`pwd` - cd ${IDF_PATH}/${IDF_EXAMPLE_PATH} - - cat ${OVERRIDE_PATH}/.gitlab-ci-override-idf-component.yml >> main/idf_component.yml + # Create components directory and link esp_hosted component + - mkdir -p components + - ln -sf ${OVERRIDE_PATH} components/esp_hosted + - echo "Created components directory with esp_hosted link:" + - ls -la components/ + # Override component dependency as backup only if not already present + - | + if ! grep -q "esp_hosted" main/idf_component.yml 2>/dev/null; then + cat ${OVERRIDE_PATH}/.gitlab-ci-override-idf-component.yml >> main/idf_component.yml + echo "Added esp_hosted override to idf_component.yml" + fi + # Add slave target configuration if specified + - | + if [ ! -z "${IDF_SLAVE_TARGET}" ]; then + echo "CONFIG_SLAVE_IDF_TARGET_${IDF_SLAVE_TARGET^^}=y" >> sdkconfig.defaults + echo "Added slave target CONFIG_SLAVE_IDF_TARGET_${IDF_SLAVE_TARGET^^}=y to sdkconfig.defaults" + fi # Build with IDF pedantic flags and IDF build apps script - export PEDANTIC_FLAGS="-DIDF_CI_BUILD -Werror -Werror=deprecated-declarations -Werror=unused-variable -Werror=unused-but-set-variable -Werror=unused-function" - export EXTRA_CFLAGS="${PEDANTIC_FLAGS} -Wstrict-prototypes" - export EXTRA_CXXFLAGS="${PEDANTIC_FLAGS}" + # Remove the conflicting extconn config that disables hosted + - rm -f sdkconfig.ci.*extconn* - idf-build-apps find --config sdkconfig.ci* -vv --target ${IDF_TARGET} - idf-build-apps build --config sdkconfig.ci* -vv --target ${IDF_TARGET} +# - echo "----------- last sdkconfig.defaults,ci* used (${IDF_TARGET}-${IDF_SLAVE_TARGET}) --------------" +# - cat sdkconfig.defaults +# - cat sdkconfig.ci* +# - echo "----------- last (generated) sdkconfig used (${IDF_TARGET}-${IDF_SLAVE_TARGET}) --------------" +# - cat sdkconfig +# - echo "----------------------------------------------" + # Copy config files back to project directory for artifacts + - mkdir -p ${OVERRIDE_PATH}/artifacts_${IDF_TARGET}_${IDF_SLAVE_TARGET} + - cp sdkconfig* ${OVERRIDE_PATH}/artifacts_${IDF_TARGET}_${IDF_SLAVE_TARGET}/ 2>/dev/null || echo "No sdkconfig files found" + - cp main/idf_component.yml ${OVERRIDE_PATH}/artifacts_${IDF_TARGET}_${IDF_SLAVE_TARGET}/ 2>/dev/null || echo "No component file found" + # Clean up the component symlink + - unlink components/esp_hosted + - echo "Cleaned up esp_hosted component symlink" # Rename back, since post scripts expect the original name - cd ${OVERRIDE_PATH} && cd .. && mv esp_hosted esp_hosted_mcu @@ -37,24 +68,29 @@ build_idf_v5.3: image: espressif/idf:release-v5.3 parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] + - IDF_TARGET: ["esp32p4", "esp32h2"] + IDF_SLAVE_TARGET: ["esp32c6"] IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp + #IDF_EXAMPLE_PATH: [examples/wifi/iperf] build_idf_v5.4: extends: .build_template image: espressif/idf:release-v5.4 parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] + - IDF_TARGET: ["esp32p4", "esp32h2"] + IDF_SLAVE_TARGET: ["esp32c6"] IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp + #IDF_EXAMPLE_PATH: [examples/wifi/iperf] build_idf_v5.5: extends: .build_template image: espressif/idf:release-v5.5 parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] - IDF_EXAMPLE_PATH: [examples/protocols/mqtt/tcp, examples/wifi/iperf] + - IDF_TARGET: ["esp32p4", "esp32h2"] + IDF_SLAVE_TARGET: ["esp32c6", "esp32c5", "esp32", "esp32c2", "esp32c3", "esp32s3" ] + IDF_EXAMPLE_PATH: [examples/wifi/iperf] build_idf_master: extends: .build_template @@ -62,5 +98,6 @@ build_idf_master: allow_failure: true parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2", "esp32s3"] - IDF_EXAMPLE_PATH: [examples/protocols/mqtt/tcp, examples/wifi/iperf] + - IDF_TARGET: ["esp32p4", "esp32h2"] + IDF_SLAVE_TARGET: ["esp32c6", "esp32c5", "esp32", "esp32c2", "esp32c3", "esp32s3" ] + IDF_EXAMPLE_PATH: [examples/wifi/iperf] diff --git a/Kconfig b/Kconfig index 0ef74a31..0262c156 100644 --- a/Kconfig +++ b/Kconfig @@ -480,7 +480,6 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_CUSTOM_SDIO_PINS bool "Use custom SDIO GPIO pins" - default n if IDF_TARGET_ESP32 default n help Enable this to use custom GPIO pins for SDIO on ESP32-P4 and ESP32-S3. @@ -491,101 +490,103 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MIN int default 4 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 15 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MAX int default 4 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 19 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 15 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MIN int default 33 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 14 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MAX int default 33 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 18 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 14 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_D0_GPIO_RANGE_MIN int default 20 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 2 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_D0_GPIO_RANGE_MAX int default 20 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 14 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 2 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN int default 23 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 4 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX int default 23 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 15 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 4 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_D2_GPIO_RANGE_MIN int default 21 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 12 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_D2_GPIO_RANGE_MAX int default 21 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 16 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 12 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_D3_GPIO_RANGE_MIN int default 22 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 13 if IDF_TARGET_ESP32 default 0 config ESP_HOSTED_SDIO_D3_GPIO_RANGE_MAX int default 22 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C5 && IDF_TARGET_ESP32P4 - default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && SLAVE_IDF_TARGET_ESP32C6 && IDF_TARGET_ESP32P4 + default 17 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 13 if IDF_TARGET_ESP32 default 100 config ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MIN int - default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 default 53 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 42 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32S3 + default 5 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32 default 0 help GPIO pin for Resetting ESP SDIO slave device. Should be connected to RST/EN of ESP SDIO slave device. config ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MAX int - default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C6 default 53 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 54 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32P4 default 42 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32S3 + default 5 if !ESP_HOSTED_CUSTOM_SDIO_PINS && IDF_TARGET_ESP32 default 100 ### *START* GPIO SDIO pin configurations for Slot 0 and 1 @@ -602,6 +603,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "CMD GPIO number" default 47 if IDF_TARGET_ESP32S3 + default 4 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 19 if IDF_TARGET_ESP32P4 + default 15 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MIN ESP_HOSTED_SDIO_CMD_GPIO_RANGE_MAX help CMD GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -620,6 +624,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "CLK GPIO number" default 19 if IDF_TARGET_ESP32S3 + default 33 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 18 if IDF_TARGET_ESP32P4 + default 14 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MIN ESP_HOSTED_SDIO_CLK_GPIO_RANGE_MAX help CLK GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -638,6 +645,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D0 GPIO number" default 13 if IDF_TARGET_ESP32S3 + default 20 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 14 if IDF_TARGET_ESP32P4 + default 2 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_D0_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D0_GPIO_RANGE_MAX help D0 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -657,6 +667,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number" default 35 if IDF_TARGET_ESP32S3 + default 23 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 15 if IDF_TARGET_ESP32P4 + default 4 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX help D1 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -675,6 +688,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D2 GPIO number" default 20 if IDF_TARGET_ESP32S3 + default 21 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 16 if IDF_TARGET_ESP32P4 + default 12 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_D2_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D2_GPIO_RANGE_MAX help D2 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -693,6 +709,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D3 GPIO number" default 9 if IDF_TARGET_ESP32S3 + default 22 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 17 if IDF_TARGET_ESP32P4 + default 13 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_D3_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D3_GPIO_RANGE_MAX help D3 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -712,6 +731,9 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 depends on ESP_HOSTED_SDIO_SLOT_1 int "D1 GPIO number (Interrupt Line)" default 35 if IDF_TARGET_ESP32S3 + default 23 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 15 if IDF_TARGET_ESP32P4 + default 4 if IDF_TARGET_ESP32 range ESP_HOSTED_SDIO_D1_GPIO_RANGE_MIN ESP_HOSTED_SDIO_D1_GPIO_RANGE_MAX help D1 GPIO pin for SDIO. Range enforced dynamically based on slave target to ensure IOMUX compliance. @@ -721,7 +743,10 @@ ESP32XX_SDIO_CLK_FREQ_KHZ_RANGE_MAX := 50000 config ESP_HOSTED_SDIO_GPIO_RESET_SLAVE int "GPIO pin for Reseting slave ESP" range ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MIN ESP_HOSTED_SDIO_RESET_SLAVE_GPIO_MAX - default 5 + default 53 if IDF_TARGET_ESP32P4 && SLAVE_IDF_TARGET_ESP32C5 + default 54 if IDF_TARGET_ESP32P4 + default 42 if IDF_TARGET_ESP32S3 + default 5 if IDF_TARGET_ESP32 help GPIO pin for Resetting ESP SDIO slave device. Should be connected to RST/EN of ESP SDIO slave device. diff --git a/README.md b/README.md index 32167ec2..72695cdc 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,10 @@ Legends: - TBD : To be determined - iperf : iperf2 with test resukts in mbps +> [!NOTE] +> +> For the shield box readings maked with (S), full network set up explained in [Shield Box Test Setup](shield-box-test-setup.md) + **Host can be any ESP chipset or any non-ESP MCU.** ###### Hosted Transports table diff --git a/docs/esp32_p4_function_ev_board.md b/docs/esp32_p4_function_ev_board.md index d8029062..9061d1f6 100644 --- a/docs/esp32_p4_function_ev_board.md +++ b/docs/esp32_p4_function_ev_board.md @@ -381,7 +381,9 @@ Use GPIOs 36 or lower on the P4 DevKit to avoid LDO power issues with high numbe > [!NOTE] > Avoid using GPIO 35 and 36 as they affect the ESP32-P4 Bootloader Mode. See [ESP32-P4 Boot Mode Selection](https://docs.espressif.com/projects/esptool/en/latest/esp32p4/advanced-topics/boot-mode-selection.html#select-bootloader-mode) for more information. -Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). +> [!TIP] +> +> To measure the optimal performance, check out the [Shield Box Test Setup](shield-box-test-setup.md). ## 9. References diff --git a/docs/sdio.md b/docs/sdio.md index a1d41993..6d0f3a27 100644 --- a/docs/sdio.md +++ b/docs/sdio.md @@ -275,14 +275,11 @@ idf.py -p flash > > Put host in bootloader mode using following command and then retry flashing the co-processor > -> ```bash -> esptool.py -p **** --before default_reset --after no_reset run -> ``` - -Monitor the output (optional): -``` -idf.py -p monitor -``` +> `esptool.py -p **** --before default_reset --after no_reset run` +> +> Flash the co-processor and log the output: +> +> `idf.py -p flash monitor` ##### 6.4.2 Co-processor OTA Flashing (Subsequent Updates) @@ -544,7 +541,9 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. - [!NOTE] Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). +> [!TIP] +> +> To measure the optimal performance, check out the [Shield Box Test Setup](shield-box-test-setup.md). 8. Troubleshooting: - Consider using a lower clock speed or checking your [hardware setup](docs/sdio.md#7-hardware-setup) if you experience communication problems. @@ -562,3 +561,4 @@ After flashing both the co-processor and host devices, follow these steps to con - [ESP32 Hardware Design Guidelines](https://www.espressif.com/en/products/hardware/esp32/resources) - [SDIO Protocol Basics](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface) - [ESP SDIO Slave Communication](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/protocols/esp_sdio_slave_protocol.html) + diff --git a/docs/spi_full_duplex.md b/docs/spi_full_duplex.md index 72398ed0..dad9738d 100644 --- a/docs/spi_full_duplex.md +++ b/docs/spi_full_duplex.md @@ -310,14 +310,11 @@ idf.py -p flash > > Put host in bootloader mode using following command and then retry flashing the co-processor > -> ```bash -> esptool.py -p --before default_reset --after no_reset run -> ``` - -Monitor the output (optional): -``` -idf.py -p monitor -``` +> `esptool.py -p **** --before default_reset --after no_reset run` +> +> Flash the co-processor and log the output: +> +> `idf.py -p flash monitor` ##### 7.4.2 Co-processor OTA Flashing (Subsequent Updates) @@ -549,7 +546,9 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. - Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). +> [!TIP] +> +> To measure the optimal performance, check out the [Shield Box Test Setup](shield-box-test-setup.md). 8. Troubleshooting: - If you encounter issues, refer to section 6.3 for testing the SPI connection. diff --git a/docs/spi_half_duplex.md b/docs/spi_half_duplex.md index 0bed729a..ed35b539 100644 --- a/docs/spi_half_duplex.md +++ b/docs/spi_half_duplex.md @@ -547,14 +547,11 @@ idf.py -p flash > > Put host in bootloader mode using following command and then retry flashing the co-processor > -> ```bash -> esptool.py -p **** --before default_reset --after no_reset run -> ``` - -Monitor the output (optional): -``` -idf.py -p monitor -``` +> `esptool.py -p **** --before default_reset --after no_reset run` +> +> Flash the co-processor and log the output: +> +> `idf.py -p flash monitor` ##### 9.4.2 Co-processor OTA Flashing (Subsequent Updates) @@ -795,7 +792,9 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. - [!NOTE] Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). +> [!TIP] +> +> To measure the optimal performance, check out the [Shield Box Test Setup](shield-box-test-setup.md). 8. Troubleshooting: - If you encounter issues, refer to section 6.3 for testing the SPI connection. diff --git a/docs/uart.md b/docs/uart.md index 144f4dad..0caa6eaf 100644 --- a/docs/uart.md +++ b/docs/uart.md @@ -187,14 +187,11 @@ idf.py -p flash > > Put host in bootloader mode using following command and then retry flashing the co-processor > -> ```bash -> esptool.py -p **** --before default_reset --after no_reset run -> ``` - -Monitor the output (optional): -``` -idf.py -p monitor -``` +> `esptool.py -p **** --before default_reset --after no_reset run` +> +> Flash the co-processor and log the output: +> +> `idf.py -p flash monitor` ##### 6.4.2 Co-processor OTA Flashing (Subsequent Updates) @@ -435,7 +432,9 @@ After flashing both the co-processor and host devices, follow these steps to con Note: Replace `` with the IP address of the external STA, and `` with the IP address of the ESP-Hosted device. - Shield box throughput testing setup explained in [Shield Box Test Setup](shield-box-test-setup.md). +> [!TIP] +> +> To measure the optimal performance, check out the [Shield Box Test Setup](shield-box-test-setup.md). 8. Troubleshooting: - If you encounter issues, refer to section 3.3 for checking the UART connection. From b401eddfc851ef5487afab9c85c2e447a955e7fe Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Thu, 5 Jun 2025 15:59:10 +0800 Subject: [PATCH 26/44] fix: re-enable hearbeat event subscription Fixes #55 --- host/drivers/rpc/wrap/rpc_wrap.c | 55 ++++++++++---------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/host/drivers/rpc/wrap/rpc_wrap.c b/host/drivers/rpc/wrap/rpc_wrap.c index 7053dae6..ee71d6d6 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.c +++ b/host/drivers/rpc/wrap/rpc_wrap.c @@ -75,7 +75,6 @@ static ctrl_cmd_t * RPC_DEFAULT_REQ(void) #define YES 1 #define NO 0 -#define MIN_TIMESTAMP_STR_SIZE 30 #define HEARTBEAT_DURATION_SEC 20 @@ -96,21 +95,9 @@ int rpc_deinit(void) return rpc_slaveif_deinit(); } -static char * get_timestamp_str(char *str, uint16_t str_size) -{ - if (str && str_size>=MIN_TIMESTAMP_STR_SIZE) { - time_t t = time(NULL); - struct tm tm = *localtime(&t); - sprintf(str, "%d-%02d-%02d %02d:%02d:%02d > ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - return str; - } - return NULL; -} static int rpc_event_callback(ctrl_cmd_t * app_event) { - char ts[MIN_TIMESTAMP_STR_SIZE] = {'\0'}; - ESP_LOGV(TAG, "%u",app_event->msg_id); if (!app_event || (app_event->msg_type != RPC_TYPE__Event)) { if (app_event) @@ -127,19 +114,17 @@ static int rpc_event_callback(ctrl_cmd_t * app_event) switch(app_event->msg_id) { case RPC_ID__Event_ESPInit: { - ESP_LOGD(TAG, "Received Slave ESP Init"); + ESP_LOGI(TAG, "--- ESP Event: Slave ESP Init ---"); break; } case RPC_ID__Event_Heartbeat: { - ESP_LOGV(TAG, "%s ESP EVENT: Heartbeat event [%lu]", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE), + ESP_LOGI(TAG, "ESP Event: Heartbeat event [%lu]", (long unsigned int)app_event->u.e_heartbeat.hb_num); break; } case RPC_ID__Event_AP_StaConnected: { wifi_event_ap_staconnected_t *p_e = &app_event->u.e_wifi_ap_staconnected; if (strlen((char*)p_e->mac)) { - ESP_LOGV(TAG, "%s ESP EVENT: SoftAP mode: connected station", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE)); + ESP_LOGV(TAG, "ESP Event: SoftAP mode: connected station"); g_h.funcs->_h_event_wifi_post(WIFI_EVENT_AP_STACONNECTED, p_e, sizeof(wifi_event_ap_staconnected_t), HOSTED_BLOCK_MAX); } @@ -147,22 +132,19 @@ static int rpc_event_callback(ctrl_cmd_t * app_event) } case RPC_ID__Event_AP_StaDisconnected: { wifi_event_ap_stadisconnected_t *p_e = &app_event->u.e_wifi_ap_stadisconnected; if (strlen((char*)p_e->mac)) { - ESP_LOGV(TAG, "%s ESP EVENT: SoftAP mode: disconnected MAC", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE)); + ESP_LOGV(TAG, "ESP Event: SoftAP mode: disconnected MAC"); g_h.funcs->_h_event_wifi_post(WIFI_EVENT_AP_STADISCONNECTED, p_e, sizeof(wifi_event_ap_stadisconnected_t), HOSTED_BLOCK_MAX); } break; } case RPC_ID__Event_StaConnected: { - ESP_LOGV(TAG, "%s ESP EVENT: Station mode: Connected", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE)); + ESP_LOGV(TAG, "ESP Event: Station mode: Connected"); wifi_event_sta_connected_t *p_e = &app_event->u.e_wifi_sta_connected; g_h.funcs->_h_event_wifi_post(WIFI_EVENT_STA_CONNECTED, p_e, sizeof(wifi_event_sta_connected_t), HOSTED_BLOCK_MAX); break; } case RPC_ID__Event_StaDisconnected: { - ESP_LOGV(TAG, "%s ESP EVENT: Station mode: Disconnected", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE)); + ESP_LOGV(TAG, "ESP Event: Station mode: Disconnected"); wifi_event_sta_disconnected_t *p_e = &app_event->u.e_wifi_sta_disconnected; g_h.funcs->_h_event_wifi_post(WIFI_EVENT_STA_DISCONNECTED, p_e, sizeof(wifi_event_sta_disconnected_t), HOSTED_BLOCK_MAX); @@ -173,25 +155,26 @@ static int rpc_event_callback(ctrl_cmd_t * app_event) switch (wifi_event_id) { case WIFI_EVENT_STA_START: - ESP_LOGV(TAG, "%s ESP EVENT: WiFi Event[%s]", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE), "WIFI_EVENT_STA_START"); + ESP_LOGV(TAG, "ESP Event: wifi station started"); break; case WIFI_EVENT_STA_STOP: - ESP_LOGV(TAG, "%s ESP EVENT: WiFi Event[%s]", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE), "WIFI_EVENT_STA_STOP"); + ESP_LOGV(TAG, "ESP Event: wifi station stopped"); break; case WIFI_EVENT_AP_START: - ESP_LOGD(TAG,"ESP EVENT: softap started"); + ESP_LOGD(TAG,"ESP Event: softap started"); break; case WIFI_EVENT_AP_STOP: - ESP_LOGD(TAG,"ESP EVENT: softap stopped"); + ESP_LOGD(TAG,"ESP Event: softap stopped"); + break; + + case WIFI_EVENT_HOME_CHANNEL_CHANGE: + ESP_LOGD(TAG,"ESP Event: Home channel changed"); break; default: - ESP_LOGV(TAG, "%s ESP EVENT: WiFi Event[%x]", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE), wifi_event_id); + ESP_LOGW(TAG, "ESP Event: Event[%x] - unhandled", wifi_event_id); break; } /* inner switch case */ g_h.funcs->_h_event_wifi_post(wifi_event_id, 0, 0, HOSTED_BLOCK_MAX); @@ -199,15 +182,13 @@ static int rpc_event_callback(ctrl_cmd_t * app_event) break; } case RPC_ID__Event_StaScanDone: { wifi_event_sta_scan_done_t *p_e = &app_event->u.e_wifi_sta_scan_done; - ESP_LOGV(TAG, "%s ESP EVENT: StaScanDone", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE)); + ESP_LOGV(TAG, "ESP Event: StaScanDone"); ESP_LOGV(TAG, "scan: status: %lu number:%u scan_id:%u", p_e->status, p_e->number, p_e->scan_id); g_h.funcs->_h_event_wifi_post(WIFI_EVENT_SCAN_DONE, p_e, sizeof(wifi_event_sta_scan_done_t), HOSTED_BLOCK_MAX); break; } default: { - ESP_LOGW(TAG, "%s Invalid event[0x%x] to parse", - get_timestamp_str(ts, MIN_TIMESTAMP_STR_SIZE), app_event->msg_id); + ESP_LOGW(TAG, "Invalid event[0x%x] to parse", app_event->msg_id); break; } } @@ -304,9 +285,7 @@ int rpc_register_event_callbacks(void) event_callback_table_t events[] = { { RPC_ID__Event_ESPInit, rpc_event_callback }, -#if 0 { RPC_ID__Event_Heartbeat, rpc_event_callback }, -#endif { RPC_ID__Event_AP_StaConnected, rpc_event_callback }, { RPC_ID__Event_AP_StaDisconnected, rpc_event_callback }, { RPC_ID__Event_WifiEventNoArgs, rpc_event_callback }, From 08bd7554462540f486f0f36a84d70657ffd33018 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Mon, 9 Jun 2025 15:52:28 +0800 Subject: [PATCH 27/44] fix(esp32_bt) Fixed BT examples, check for BT version - for ESP32 as co-processor, check that BLE 5.0 was not enabled on host - updated `idf_component.yml` configurations - updated `sdkconfig.defaults` to disable BLE 5.0 in Bluedroid examples - added warning to BT documentation about BT version and ESP32 --- docs/bluetooth_design.md | 4 ++++ .../main/idf_component.yml | 6 +++++- .../sdkconfig.defaults | 3 +++ .../main/idf_component.yml | 6 +++++- .../sdkconfig.defaults | 13 ++++++++++--- .../host_bluedroid_host_only/main/idf_component.yml | 6 +++++- .../host_bluedroid_host_only/sdkconfig.defaults | 7 ++++++- .../main/idf_component.yml | 4 ++-- .../main/idf_component.yml | 6 +++++- host/api/include/esp_hosted_bt_config.h | 7 +++++++ host/drivers/bt/vhci_drv.c | 2 +- idf_component.yml | 2 +- 12 files changed, 54 insertions(+), 12 deletions(-) diff --git a/docs/bluetooth_design.md b/docs/bluetooth_design.md index 408f0b2a..9af184d3 100644 --- a/docs/bluetooth_design.md +++ b/docs/bluetooth_design.md @@ -39,6 +39,10 @@ hardware. > Check that the memory requirement for your preferred Bluetooth host > stack can be satisfied on the Host. +> [!WARNING] +> The ESP32 only supports Bluetooth v4.2. If you are using a ESP32 as +> the co-processor, the host Bluetooth stack must also be v4.2. + ESP-Hosted is Bluetooth stack agnostic. To showcase ESP-Hosted's Bluetooth support, both `esp-nimble` and `esp-bluedroid` are used here. Users can use their own preferred Bluetooth stack with some diff --git a/examples/host_bluedroid_ble_compatibility_test/main/idf_component.yml b/examples/host_bluedroid_ble_compatibility_test/main/idf_component.yml index 57b37833..50ffd415 100644 --- a/examples/host_bluedroid_ble_compatibility_test/main/idf_component.yml +++ b/examples/host_bluedroid_ble_compatibility_test/main/idf_component.yml @@ -1,5 +1,9 @@ dependencies: espressif/esp_wifi_remote: - version: "~0.5.1" + version: ">=0.10" + rules: + - if: "target in [esp32p4, esp32h2]" + espressif/esp_hosted: + version: "~2" rules: - if: "target in [esp32p4, esp32h2]" diff --git a/examples/host_bluedroid_ble_compatibility_test/sdkconfig.defaults b/examples/host_bluedroid_ble_compatibility_test/sdkconfig.defaults index 5b03a89c..30fef8e2 100644 --- a/examples/host_bluedroid_ble_compatibility_test/sdkconfig.defaults +++ b/examples/host_bluedroid_ble_compatibility_test/sdkconfig.defaults @@ -1,7 +1,10 @@ # # BT config +# - ESP32 co-processor only supports BLE 4.2 # CONFIG_BT_ENABLED=y +CONFIG_BT_CONTROLLER_DISABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. diff --git a/examples/host_bluedroid_bt_hid_mouse_device/main/idf_component.yml b/examples/host_bluedroid_bt_hid_mouse_device/main/idf_component.yml index 57b37833..50ffd415 100644 --- a/examples/host_bluedroid_bt_hid_mouse_device/main/idf_component.yml +++ b/examples/host_bluedroid_bt_hid_mouse_device/main/idf_component.yml @@ -1,5 +1,9 @@ dependencies: espressif/esp_wifi_remote: - version: "~0.5.1" + version: ">=0.10" + rules: + - if: "target in [esp32p4, esp32h2]" + espressif/esp_hosted: + version: "~2" rules: - if: "target in [esp32p4, esp32h2]" diff --git a/examples/host_bluedroid_bt_hid_mouse_device/sdkconfig.defaults b/examples/host_bluedroid_bt_hid_mouse_device/sdkconfig.defaults index f4e3f497..22e6ff19 100644 --- a/examples/host_bluedroid_bt_hid_mouse_device/sdkconfig.defaults +++ b/examples/host_bluedroid_bt_hid_mouse_device/sdkconfig.defaults @@ -1,18 +1,25 @@ # # BT config +# - ESP32 co-processor only supports BLE 4.2 # CONFIG_BT_ENABLED=y -CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n -CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y -CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BT_CONTROLLER_DISABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_HID_ENABLED=y CONFIG_BT_HID_DEVICE_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CTRL_MODE_BTDM=n # # Wi-Fi Remote +# - set ESP32 as default co-processor # CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y +CONFIG_SLAVE_IDF_TARGET_ESP32=y # # Enable ESP Hosted BT diff --git a/examples/host_bluedroid_host_only/main/idf_component.yml b/examples/host_bluedroid_host_only/main/idf_component.yml index 57b37833..50ffd415 100644 --- a/examples/host_bluedroid_host_only/main/idf_component.yml +++ b/examples/host_bluedroid_host_only/main/idf_component.yml @@ -1,5 +1,9 @@ dependencies: espressif/esp_wifi_remote: - version: "~0.5.1" + version: ">=0.10" + rules: + - if: "target in [esp32p4, esp32h2]" + espressif/esp_hosted: + version: "~2" rules: - if: "target in [esp32p4, esp32h2]" diff --git a/examples/host_bluedroid_host_only/sdkconfig.defaults b/examples/host_bluedroid_host_only/sdkconfig.defaults index 2cc253b9..02095c4c 100644 --- a/examples/host_bluedroid_host_only/sdkconfig.defaults +++ b/examples/host_bluedroid_host_only/sdkconfig.defaults @@ -1,15 +1,20 @@ # # BT config +# - ESP32 co-processor only supports BLE 4.2 # -CONFIG_BT_CONTROLLER_DISABLED=y CONFIG_BT_ENABLED=y +CONFIG_BT_CONTROLLER_DISABLED=y CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # # Wi-Fi Remote +# - set ESP32 as default co-processor # CONFIG_ESP_WIFI_REMOTE_LIBRARY_HOSTED=y +CONFIG_SLAVE_IDF_TARGET_ESP32=y # # Enable ESP Hosted BT diff --git a/examples/host_nimble_bleprph_host_only_uart_hci/main/idf_component.yml b/examples/host_nimble_bleprph_host_only_uart_hci/main/idf_component.yml index aea470e2..6a234b8b 100644 --- a/examples/host_nimble_bleprph_host_only_uart_hci/main/idf_component.yml +++ b/examples/host_nimble_bleprph_host_only_uart_hci/main/idf_component.yml @@ -2,10 +2,10 @@ dependencies: nimble_peripheral_utils: path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils espressif/esp_wifi_remote: - version: "*" + version: ">=0.10" rules: - if: "target in [esp32p4, esp32h2]" espressif/esp_hosted: - version: "~1" + version: "~2" rules: - if: "target in [esp32p4, esp32h2]" diff --git a/examples/host_nimble_bleprph_host_only_vhci/main/idf_component.yml b/examples/host_nimble_bleprph_host_only_vhci/main/idf_component.yml index c2ddf618..6a234b8b 100644 --- a/examples/host_nimble_bleprph_host_only_vhci/main/idf_component.yml +++ b/examples/host_nimble_bleprph_host_only_vhci/main/idf_component.yml @@ -2,6 +2,10 @@ dependencies: nimble_peripheral_utils: path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils espressif/esp_wifi_remote: - version: "~0.5.1" + version: ">=0.10" + rules: + - if: "target in [esp32p4, esp32h2]" + espressif/esp_hosted: + version: "~2" rules: - if: "target in [esp32p4, esp32h2]" diff --git a/host/api/include/esp_hosted_bt_config.h b/host/api/include/esp_hosted_bt_config.h index 4b3aa325..6e95d262 100644 --- a/host/api/include/esp_hosted_bt_config.h +++ b/host/api/include/esp_hosted_bt_config.h @@ -6,6 +6,13 @@ #ifndef __ESP_HOSTED_BT_CONFIG_H__ #define __ESP_HOSTED_BT_CONFIG_H__ +// check: if co-processor SOC is ESP32, only BT BLE 4.2 is supported +#if CONFIG_SLAVE_IDF_TARGET_ESP32 +#if CONFIG_BT_BLE_50_FEATURES_SUPPORTED || CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT +#error "ESP32 co-processor only supports BLE 4.2" +#endif +#endif + // Hosted BT defines for NimBLE #if CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE #define H_BT_HOST_ESP_NIMBLE 1 diff --git a/host/drivers/bt/vhci_drv.c b/host/drivers/bt/vhci_drv.c index 2a52cd9a..4a1fa850 100644 --- a/host/drivers/bt/vhci_drv.c +++ b/host/drivers/bt/vhci_drv.c @@ -216,7 +216,7 @@ int hci_rx_handler(interface_buffer_handle_t *buf_handle) s_callback.notify_host_recv(data, len_total_read); } - return ESP_FAIL; + return ESP_OK; } void hosted_hci_bluedroid_open(void) diff --git a/idf_component.yml b/idf_component.yml index 09761db5..e9b86827 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.4" +version: "2.0.5" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From b89e330352ea6b362c5c28b9320d589128fe729b Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 9 Jun 2025 15:47:57 +0800 Subject: [PATCH 28/44] refactor(port): use normal heap API to allocate aligned DMA buffer --- host/port/esp/freertos/include/os_wrapper.h | 13 ++----------- host/port/esp/freertos/src/os_wrapper.c | 14 ++------------ 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/host/port/esp/freertos/include/os_wrapper.h b/host/port/esp/freertos/include/os_wrapper.h index 19d5fe21..da061581 100644 --- a/host/port/esp/freertos/include/os_wrapper.h +++ b/host/port/esp/freertos/include/os_wrapper.h @@ -17,6 +17,7 @@ #include "hosted_os_abstraction.h" #include "esp_timer.h" #include "esp_event.h" +#include "esp_heap_caps.h" #include "esp_netif_types.h" #include "esp_wifi_types.h" #include "esp_wifi_default.h" @@ -102,17 +103,7 @@ enum { #define MALLOC(x) malloc(x) /* This is [malloc + aligned DMA] */ -#define MEM_ALLOC(x) ({ \ - esp_dma_mem_info_t dma_mem_info = { \ - .extra_heap_caps = 0, \ - .dma_alignment_bytes = 64, \ - }; \ - void *tmp_buf = NULL; \ - size_t actual_size = 0; \ - esp_err_t err = ESP_OK; \ - err = esp_dma_capable_malloc((x), &dma_mem_info, &tmp_buf, &actual_size);\ - if (err) tmp_buf = NULL; \ - tmp_buf;}) +#define MEM_ALLOC(x) heap_caps_aligned_alloc(64, (x), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT) #define FREE(x) free(x); diff --git a/host/port/esp/freertos/src/os_wrapper.c b/host/port/esp/freertos/src/os_wrapper.c index 8506d53c..19b016b9 100644 --- a/host/port/esp/freertos/src/os_wrapper.c +++ b/host/port/esp/freertos/src/os_wrapper.c @@ -19,6 +19,7 @@ #include "esp_log.h" #include "driver/gpio.h" #include "esp_event.h" +#include "esp_heap_caps.h" #include "freertos/portmacro.h" #include "esp_macros.h" #include "esp_hosted_config.h" @@ -129,18 +130,7 @@ void *hosted_realloc(void *mem, size_t newsize) void *hosted_malloc_align(size_t size, size_t align) { - esp_dma_mem_info_t dma_mem_info = { - .extra_heap_caps = 0, - .dma_alignment_bytes = align, - }; - void *tmp_buf = NULL; - size_t actual_size = 0; - esp_err_t err = ESP_OK; - - err = esp_dma_capable_malloc((size), &dma_mem_info, &tmp_buf, &actual_size); - if (err) tmp_buf = NULL; - - return tmp_buf; + return heap_caps_aligned_alloc(align, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); } void hosted_free_align(void* ptr) From 4479ea812be4b224f14fadca228994f2a7b6e040 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Sat, 7 Jun 2025 12:30:48 +0800 Subject: [PATCH 29/44] fix: Disable reserved fields by default, which break the idf portability --- Kconfig | 9 ++++++ host/drivers/rpc/core/rpc_req.c | 31 ++++++++++++++----- host/drivers/rpc/core/rpc_rsp.c | 21 +++++++++---- .../freertos/include/esp_hosted_wifi_config.h | 25 +++++++++++++-- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/Kconfig b/Kconfig index 0262c156..e9b4b5c9 100644 --- a/Kconfig +++ b/Kconfig @@ -1344,4 +1344,13 @@ ESP32XX_SPI_HD_CLK_FREQ_RANGE_MAX := 40 Once the Wi-Fi is no more stressed, data throttling would be stopped, once slave Wi-Fi load is lower than this threshold endmenu + + config ESP_HOSTED_DECODE_WIFI_RESERVED_FIELD + bool "Decode WiFi configuration reserved field names" + default n + help + Enable this option to use legacy 'reserved' and 'he_reserved' field names + in WiFi configuration structures OR newer 'reserved1' and 'reserved2' + field names. + endmenu diff --git a/host/drivers/rpc/core/rpc_req.c b/host/drivers/rpc/core/rpc_req.c index 22331a84..800ef0f6 100644 --- a/host/drivers/rpc/core/rpc_req.c +++ b/host/drivers/rpc/core/rpc_req.c @@ -249,10 +249,23 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) if (p_a_sta->transition_disable) H_SET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); -#else - WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved1, p_c_sta->bitmask); +#if H_WIFI_VHT_FIELDS_AVAILABLE + if (p_a_sta->vht_su_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); + + if (p_a_sta->vht_mu_beamformee_disabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); + + if (p_a_sta->vht_mcs8_enabled) + H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); +#endif + +#if H_DECODE_WIFI_RESERVED_FIELD + #if H_WIFI_NEW_RESERVED_FIELD_NAMES + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); + #else + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); + #endif #endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; @@ -284,9 +297,7 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) if (p_a_sta->he_trig_cqi_feedback_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); -#else +#if H_WIFI_VHT_FIELDS_AVAILABLE if (p_a_sta->vht_su_beamformee_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); @@ -295,8 +306,14 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) if (p_a_sta->vht_mcs8_enabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); +#endif +#if H_DECODE_WIFI_RESERVED_FIELD + #if H_WIFI_NEW_RESERVED_FIELD_NAMES WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); + #else + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); + #endif #endif RPC_REQ_COPY_BYTES(p_c_sta->sae_h2e_identifier, p_a_sta->sae_h2e_identifier, SAE_H2E_IDENTIFIER_LEN); diff --git a/host/drivers/rpc/core/rpc_rsp.c b/host/drivers/rpc/core/rpc_rsp.c index 54e6a293..809485e1 100644 --- a/host/drivers/rpc/core/rpc_rsp.c +++ b/host/drivers/rpc/core/rpc_rsp.c @@ -8,6 +8,7 @@ #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" #include "esp_idf_version.h" +#include "esp_hosted_config.h" DEFINE_LOG_TAG(rpc_rsp); @@ -267,10 +268,12 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_sta->owe_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); p_a_sta->transition_disable = H_GET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); -#else +#if H_DECODE_WIFI_RESERVED_FIELD + #if H_WIFI_NEW_RESERVED_FIELD_NAMES p_a_sta->reserved1 = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); + #else + p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); + #endif #endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; @@ -287,13 +290,19 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->bitmask); p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); -#else + +#if H_WIFI_VHT_FIELDS_AVAILABLE p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); +#endif + +#if H_DECODE_WIFI_RESERVED_FIELD + #if H_WIFI_NEW_RESERVED_FIELD_NAMES p_a_sta->reserved2 = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); + #else + p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); + #endif #endif break; diff --git a/host/port/esp/freertos/include/esp_hosted_wifi_config.h b/host/port/esp/freertos/include/esp_hosted_wifi_config.h index b1e75d9f..7a93ebd3 100644 --- a/host/port/esp/freertos/include/esp_hosted_wifi_config.h +++ b/host/port/esp/freertos/include/esp_hosted_wifi_config.h @@ -6,11 +6,30 @@ #ifndef __ESP_HOSTED_WIFI_CONFIG_H__ #define __ESP_HOSTED_WIFI_CONFIG_H__ +#include "esp_idf_version.h" +#include "esp_hosted_config.h" + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) -// dual band API support available -#define H_WIFI_DUALBAND_SUPPORT 1 + /* dual band API support available */ + #define H_WIFI_DUALBAND_SUPPORT 1 +#else + #define H_WIFI_DUALBAND_SUPPORT 0 +#endif + +/* ESP-IDF 5.5.0 breaking change: reserved/he_reserved renamed to reserved1/reserved2 */ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + #define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 + #define H_WIFI_VHT_FIELDS_AVAILABLE 1 #else -#define H_WIFI_DUALBAND_SUPPORT 0 + #define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 + #define H_WIFI_VHT_FIELDS_AVAILABLE 0 #endif +/* User-controllable reserved field decoding - works regardless of IDF version */ +#ifdef CONFIG_ESP_HOSTED_DECODE_WIFI_RESERVED_FIELD + #define H_DECODE_WIFI_RESERVED_FIELD 1 +#else + #define H_DECODE_WIFI_RESERVED_FIELD 0 #endif + +#endif /* __ESP_HOSTED_WIFI_CONFIG_H__ */ From 1ef1a6aa16d6c3e9db307f98e164a890f6201434 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Mon, 9 Jun 2025 19:23:32 +0800 Subject: [PATCH 30/44] fix: incorrect bitmasks used while decoding he params at slave --- Kconfig | 8 ++-- host/api/include/esp_hosted_api.h | 1 + idf_component.yml | 2 +- slave/main/slave_control.c | 80 +++++++++++++++++++++---------- 4 files changed, 62 insertions(+), 29 deletions(-) diff --git a/Kconfig b/Kconfig index e9b4b5c9..929e3567 100644 --- a/Kconfig +++ b/Kconfig @@ -1346,11 +1346,11 @@ ESP32XX_SPI_HD_CLK_FREQ_RANGE_MAX := 40 endmenu config ESP_HOSTED_DECODE_WIFI_RESERVED_FIELD - bool "Decode WiFi configuration reserved field names" + bool "Copy Wi-Fi configuration reserved field values" default n help - Enable this option to use legacy 'reserved' and 'he_reserved' field names - in WiFi configuration structures OR newer 'reserved1' and 'reserved2' - field names. + ESP-IDF Wi-Fi structures contain reserved bitmask values. + Enable this option if you want to copy these values between host and co-processor. + It is usually safe to ignore these reserved values. endmenu diff --git a/host/api/include/esp_hosted_api.h b/host/api/include/esp_hosted_api.h index 69b83795..94e1535e 100644 --- a/host/api/include/esp_hosted_api.h +++ b/host/api/include/esp_hosted_api.h @@ -18,6 +18,7 @@ extern "C" { #include "esp_wifi_remote.h" #include "esp_hosted_api_types.h" #include "esp_hosted_ota.h" +#include "esp_hosted_wifi_config.h" /** Exported variables **/ #define ESP_HOSTED_CHANNEL_CONFIG_DEFAULT() { \ diff --git a/idf_component.yml b/idf_component.yml index e9b86827..002a9b6e 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.5" +version: "2.0.6" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/slave_control.c b/slave/main/slave_control.c index 35323f64..3f7655e4 100644 --- a/slave/main/slave_control.c +++ b/slave/main/slave_control.c @@ -27,6 +27,21 @@ #include "esp_hosted_bitmasks.h" #include "esp_idf_version.h" +/* ESP-IDF 5.5.0: renamed reserved fields to reserved1/reserved2 + * and added VHT beamforming/MCS fields */ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) +#define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 +#define H_WIFI_VHT_FIELDS_AVAILABLE 1 +#else +#define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 +#define H_WIFI_VHT_FIELDS_AVAILABLE 0 +#endif + +/* Slave-side: Always support reserved field decoding for maximum compatibility + * The host may or may not have CONFIG_ESP_HOSTED_DECODE_WIFI_RESERVED_FIELD enabled + */ +#define H_DECODE_WIFI_RESERVED_FIELD 1 + #include "coprocessor_fw_version.h" #define MAC_STR_LEN 17 @@ -876,32 +891,41 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_sta->ft_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_ft_enabled, p_c_sta->bitmask); p_a_sta->owe_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_owe_enabled, p_c_sta->bitmask); p_a_sta->transition_disable = H_GET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); -#else +#if H_DECODE_WIFI_RESERVED_FIELD +#if H_WIFI_NEW_RESERVED_FIELD_NAMES p_a_sta->reserved1 = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#else + p_a_sta->reserved = WIFI_STA_CONFIG_1_GET_RESERVED_VAL(p_c_sta->bitmask); +#endif #endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; p_a_sta->failure_retry_cnt = p_c_sta->failure_retry_cnt; p_a_sta->he_dcm_set = H_GET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide + /* WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx is two bits wide */ p_a_sta->he_dcm_max_constellation_tx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS) & 0x03; - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide + /* WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx is two bits wide */ p_a_sta->he_dcm_max_constellation_rx = (p_c_sta->he_bitmask >> WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS) & 0x03; + p_a_sta->he_mcs9_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); p_a_sta->he_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_su_beamformee_disabled_BIT, p_c_sta->he_bitmask); - p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->bitmask); - p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); - p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); -#else + p_a_sta->he_trig_su_bmforming_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_su_bmforming_feedback_disabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->he_bitmask); + p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); + +#if H_WIFI_VHT_FIELDS_AVAILABLE p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); +#endif + +#if H_DECODE_WIFI_RESERVED_FIELD +#if H_WIFI_NEW_RESERVED_FIELD_NAMES p_a_sta->reserved2 = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#else + p_a_sta->he_reserved = WIFI_STA_CONFIG_2_GET_RESERVED_VAL(p_c_sta->he_bitmask); +#endif #endif /* Avoid using fast scan, which leads to faster SSID selection, @@ -997,25 +1021,29 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) if (p_a_sta->transition_disable) H_SET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); -#else +#if H_DECODE_WIFI_RESERVED_FIELD +#if H_WIFI_NEW_RESERVED_FIELD_NAMES WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved1, p_c_sta->bitmask); +#else + WIFI_STA_CONFIG_1_SET_RESERVED_VAL(p_a_sta->reserved, p_c_sta->bitmask); +#endif #endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; p_c_sta->failure_retry_cnt = p_a_sta->failure_retry_cnt; + /* HE field handling */ if (p_a_sta->he_dcm_set) H_SET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_tx is two bits wide - if (p_a_sta->he_dcm_max_constellation_tx) - p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_tx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS); - - // WIFI_HE_STA_CONFIG_he_dcm_max_constellation_rx is two bits wide - if (p_a_sta->he_dcm_max_constellation_rx) - p_c_sta->he_bitmask |= ((p_a_sta->he_dcm_max_constellation_rx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS); + /* WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx is two bits wide */ + if (p_a_sta->he_dcm_max_constellation_tx & 0x03) { + p_c_sta->he_bitmask |= (p_a_sta->he_dcm_max_constellation_tx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_tx_BITS; + } + /* WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx is two bits wide */ + if (p_a_sta->he_dcm_max_constellation_rx & 0x03) { + p_c_sta->he_bitmask |= (p_a_sta->he_dcm_max_constellation_rx & 0x03) << WIFI_STA_CONFIG_2_he_dcm_max_constellation_rx_BITS; + } if (p_a_sta->he_mcs9_enabled) H_SET_BIT(WIFI_STA_CONFIG_2_he_mcs9_enabled_BIT, p_c_sta->he_bitmask); @@ -1032,9 +1060,7 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) if (p_a_sta->he_trig_cqi_feedback_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) - WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); -#else +#if H_WIFI_VHT_FIELDS_AVAILABLE if (p_a_sta->vht_su_beamformee_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); @@ -1043,8 +1069,14 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) if (p_a_sta->vht_mcs8_enabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); +#endif +#if H_DECODE_WIFI_RESERVED_FIELD +#if H_WIFI_NEW_RESERVED_FIELD_NAMES WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); +#else + WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->he_reserved, p_c_sta->he_bitmask); +#endif #endif break; From 83a9fc31094ce44313c5809ae3906954cee8126b Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 29 May 2025 15:57:21 +0800 Subject: [PATCH 31/44] feature(sdio_rx_queue_size): Allow SDIO Tx/Rx Queue Size to be adjusted - corrected: `Rx` is now `Tx` on co-processor, adjusted code, added - added correct `Rx` as Kconfig option - use Kconfig to adjust Tx and Rx queue size on co-processor - added SDIO documentation on effect of changing Tx and Rx queue size - on Host Rx Raw throughput - on Host heap memory used to receive Tx data from co-processor --- docs/performance_optimization.md | 24 ++++----- docs/sdio.md | 86 ++++++++++++++++++++++++++++++-- slave/main/Kconfig.projbuild | 6 +++ slave/main/sdio_slave_api.c | 34 +++++++------ 4 files changed, 116 insertions(+), 34 deletions(-) diff --git a/docs/performance_optimization.md b/docs/performance_optimization.md index 9851bdfd..b7cf70c6 100644 --- a/docs/performance_optimization.md +++ b/docs/performance_optimization.md @@ -16,7 +16,7 @@ CONFIG_ESP_WIFI_TX_BA_WIN=32 CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y CONFIG_ESP_WIFI_RX_BA_WIN=32 -# TCP/IP Performance +# TCP/IP Performance CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534 CONFIG_LWIP_TCP_WND_DEFAULT=65534 CONFIG_LWIP_TCP_RECVMBOX_SIZE=64 @@ -40,8 +40,11 @@ CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=40000 CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH=4 ``` +> [!NOTE] +> See [Performance and Memory Usage](sdio.md#9-performance-and-memory-usage) on the trade-off between SDIO Performance and Memory Use + ### SPI Full-Duplex -- **Clock Speed**: ESP32: ≤10 MHz, Others: ≤40 MHz +- **Clock Speed**: ESP32: ≤10 MHz, Others: ≤40 MHz - **Hardware**: Use IO_MUX pins, short traces (≤10cm for jumpers) - **Checksum**: Mandatory (SPI hardware lacks error detection) @@ -49,7 +52,7 @@ CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH=4 CONFIG_ESP_HOSTED_SPI_CLK_FREQ=40 ``` -### SPI Half-Duplex +### SPI Half-Duplex - **Data Lines**: Use 4-line (Quad SPI) mode - **Similar optimizations as SPI Full-Duplex** @@ -65,7 +68,7 @@ CONFIG_ESP_HOSTED_SPI_CLK_FREQ=40 # Reduce queue sizes CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE=10 # Default: 20 CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE=10 # Default: 20 - + # Enable memory pooling CONFIG_ESP_HOSTED_USE_MEMPOOL=y ``` @@ -83,24 +86,17 @@ CONFIG_ESP_HOSTED_SPI_CLK_FREQ=40 4. **Pull-ups**: Required for SDIO (51kΩ) on CMD, D0-D3 lines ### PCB Design Checklist -- [ ] Equal trace lengths for communication signals +- [ ] Equal trace lengths for communication signals - [ ] Ground plane for signal stability - [ ] Controlled impedance traces (50Ω typical) - [ ] Series termination resistors for high-speed signals - [ ] Extra GPIOs reserved for future features (deep sleep, etc.) - - ## Development Workflow 1. **Proof of Concept**: Start with jumper wires, low clock speeds 2. **Incremental Optimization**: Increase transport clock step by step 3. **Hardware Validation**: Move to PCB for final validation 4. **Performance Tuning**: Optimize buffers and configurations - - - - - -4. **Disable features**: Any unsued components from ESP-IDF or -ESP-Hosted-MCU features could be disabled for more memory +5. **Disable features**: Any unsued components from ESP-IDF or +ESP-Hosted-MCU features could be disabled for more memory availability. diff --git a/docs/sdio.md b/docs/sdio.md index 6d0f3a27..219d02d7 100644 --- a/docs/sdio.md +++ b/docs/sdio.md @@ -24,7 +24,9 @@ If you wish to skip the theory, you can refer the [Quick Start Guide](#1-quick-s 8. [Testing and Troubleshooting](#8-testing-and-troubleshooting) -9. [References](#9-references) +9. [Performance and Memory Usage](#9-performance-and-memory-usage) || [9.1 Stream and Packet Mode](#91-stream-and-packet-mode) || [9.2 Double Buffering on the Host](#92-double-buffering-on-the-host) || [9.3 Reducing Memory Usage](#93-reducing-memory-usage) || [9.4 Switching to Packet Mode](#94-switching-to-packet-mode) + +10. [References](#10-references) @@ -37,6 +39,7 @@ This section provides a brief overview of how to get started with ESP-Hosted usi - [6. Flashing the Co-processor](#6-flashing-the-co-processor) - [7. Flashing the Host](#7-flashing-the-host) - [8. Testing and Troubleshooting](#8-testing-and-troubleshooting) +- [9. Performance and Memory Usage](#9-performance-and-memory-usage) These sections will guide you through the process of flashing both the co-processor and host devices, setting up the hardware connections, and verifying successful communication. @@ -555,10 +558,83 @@ After flashing both the co-processor and host devices, follow these steps to con - Use a logic analyzer or oscilloscope to verify the SDIO signals. - Ensure that the power supply to both devices is stable and within the required voltage levels. -## 9 References +## 9 Performance and Memory Usage + +Quick summary: + +- for maximum network performance, at the cost of more memory usage on host and co-processor, use SDIO Streaming Mode (default mode of operation) +- for lower memory usage, at the cost of lower network performance, use [SDIO Packet Mode](#94-switching-to-packet-mode) + +### 9.1 Stream and Packet Mode + +The co-processor SDIO can operate in two modes: Streaming Mode and Packet Mode. + +| **Streaming Mode** | **Packet Mode** | +| --- | --- | +| Co-processor combines multiple queued Tx packets together into one large packet | Co-processor queues individual Tx packets | +| Host fetches the large packet as one SDIO transfer | Host fetches each packet one at a time | +| Host breaks the large packet back into individual packets to send to the Rx queue | Host sends each packet to the Rx queue | +| More efficient (less SDIO overhead), but requires more memory at Host to hold the large packet | Less efficient (higher SDIO overhead for each packet), but minimises memory required at Host | + +### 9.2 Double Buffering on the Host + +The Host implements a double-buffering scheme to receive data. One thread fetches data (using hardware DMA) from the co-processor and stores it in one Rx buffer, while another thread breaks up previously received data into packets for processing. + +### 9.3 Reducing Memory Usage in Streaming Mode + +#### 9.3.1 Host Receive + +> [!NOTE] +> **Host Receive**: Router --Network Data--> Co-processor --SDIO--> Host + +In SDIO streaming mode, the host receives SDIO data from the co-processor in one large SDIO transfer. For this reason, **Streaming mode consumes more heap memory** compared to Packet mode, and has a higher throughput (less SDIO overhead). + +For Host systems with high heap memory usage, you can reduce the amount of heap memory used by ESP-Hosted for buffers, at the cost of reduced throughput, by adjusting the number of Tx buffers used by the co-processor. + +**On the co-processor**: run `idf.py menuconfig` ---> `Example Configuration` ---> `SDIO Configuration` and adjust `SDIO Tx queue size`. The default queue size is `20`. + +The table below shows the effect of changing `SDIO Tx queue size` on throughput and memory usage on the Host. The throughput numbers are obtained by using the RawTP option in ESP-Hosted to send / receive raw SDIO data. + +| SDIO Tx queue size | Host Rx Raw Throughput (Mbits/s) | Memory Used by Buffers (Tested) | Memory Used by Buffers (Theoretical) | +| ---: | ---: | ---: | ---: | +| 5 | 54 | 12,288 | 15,360 | +| 10 | 70 | 26,624 | 30,720 | +| 15 | 76 | 41,984 | 46,080 | +| 20 | 80 | 56,320 | 61,440 | +| 25 | 82 | 65,536 | 76,800 | +| 30 | 84 | 65,536 | 92,160 | + +> [!NOTE] +> The SDIO packet size is 1536 bytes. The co-processor can send at most `(Tx queue size) * 1536` bytes. Since the Host does double buffering, the theoretical Buffer Size needed is `2 * (Tx queue size) * 1536`. + +From the table above, throughput is more or less stagnant on and above Rx queue size of `25`. For a good trade off between memory consumption vs performance, the Rx queue sizes are currently defaulted to `20`. + +#### 9.3.2 Host Transmit + +> [!NOTE] +> **Host Transmit**: Host --SDIO--> Co-Processor --Network Data--> Router + +To reduce memory usage on the co-processor, you can reduce the number of buffers the co-processor uses to receive data from the Host. + +**On the co-processor**: run `idf.py menuconfig` ---> `Example Configuration` ---> `SDIO Configuration` and adjust `SDIO Rx queue size`. The default queue size is `20`. + +Reducing the number of Rx buffers on the co-processor can affect the Tx throughput from the Host if the number of Rx buffers is set to a small value. + +### 9.4 Switching to Packet Mode + +For mimimal memory usage with a lower throughput, you can switch to Packet Mode. To do this: + +- on the co-processor: run `idf.py menuconfig` ---> `Example Configuration` ---> `SDIO Configuration` and untoggle `Enable SDIO Streaming Mode` +- on the host: run `idf.py menuconfig` ---> `Component config` ---> `ESP-Hosted config` ---> `Hosted SDIO COnfiguration` ---> `SDIO Receive Optimization` and select either `No optimization` or `Always Rx Max Packet size`. `Always Rx Max Packet size` will give a slightly higher throughput. + +In Packet Mode, the host uses `2 * 1536` or `3,072` bytes of memory for Rx buffers. + +- with `No optimization`, Rx Raw Throughput is 33.0 Mbits/s +- with `Always Rx Max Packet size`, Rx Raw Throughput is 33.2 Mbits/s + +## 10 References - [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/) - [ESP32 Hardware Design Guidelines](https://www.espressif.com/en/products/hardware/esp32/resources) -- [SDIO Protocol Basics](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface) -- [ESP SDIO Slave Communication](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/protocols/esp_sdio_slave_protocol.html) - +- [ESP SDIO Slave Communication](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_sdio_slave_protocol.html) +- [ESP SDIO Card Slave Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdio_slave.html) diff --git a/slave/main/Kconfig.projbuild b/slave/main/Kconfig.projbuild index f1e96a6b..4025af24 100644 --- a/slave/main/Kconfig.projbuild +++ b/slave/main/Kconfig.projbuild @@ -354,6 +354,12 @@ menu "Example Configuration" bool "Send at negative edge, sample at negative edge" endchoice + config ESP_SDIO_TX_Q_SIZE + int "SDIO TX queue size" + default 20 + help + Very small TX queue will lower ESP --> SDIO -- Host data rate + config ESP_SDIO_RX_Q_SIZE int "SDIO RX queue size" default 20 diff --git a/slave/main/sdio_slave_api.c b/slave/main/sdio_slave_api.c index 3207d915..40ccbe0a 100644 --- a/slave/main/sdio_slave_api.c +++ b/slave/main/sdio_slave_api.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// 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. @@ -32,10 +32,10 @@ #include "esp_hosted_transport_init.h" //#define SIMPLIFIED_SDIO_SLAVE 1 -#define SDIO_SLAVE_QUEUE_SIZE 20 -#define BUFFER_SIZE MAX_TRANSPORT_BUF_SIZE -#define BUFFER_NUM 20 -static uint8_t sdio_slave_rx_buffer[BUFFER_NUM][BUFFER_SIZE]; +#define SDIO_SLAVE_QUEUE_SIZE CONFIG_ESP_SDIO_TX_Q_SIZE +#define NUM_RX_BUFFERS CONFIG_ESP_SDIO_RX_Q_SIZE +#define BUFFER_SIZE MAX_TRANSPORT_BUF_SIZE +static uint8_t sdio_slave_rx_buffer[NUM_RX_BUFFERS][BUFFER_SIZE]; #define SDIO_MEMPOOL_NUM_BLOCKS 40 static struct hosted_mempool * buf_mp_tx_g; @@ -44,7 +44,7 @@ interface_context_t context; interface_handle_t if_handle_g; static const char TAG[] = "SDIO_SLAVE"; -#define SDIO_RX_QUEUE_SIZE CONFIG_ESP_SDIO_RX_Q_SIZE +#define SDIO_TX_QUEUE_SIZE CONFIG_ESP_SDIO_TX_Q_SIZE #if !SIMPLIFIED_SDIO_SLAVE static SemaphoreHandle_t sdio_rx_sem; @@ -135,7 +135,7 @@ static void start_rx_data_throttling_if_needed(void) pkt_stats.slave_wifi_rx_msg_loaded = queue_load; #endif - load_percent = (queue_load*100/SDIO_RX_QUEUE_SIZE); + load_percent = (queue_load*100/NUM_RX_BUFFERS); if (load_percent > slv_cfg_g.throttle_high_threshold) { slv_state_g.current_throttling = 1; ESP_LOGV(TAG, "start data throttling at host"); @@ -156,7 +156,7 @@ static void stop_rx_data_throttling_if_needed(void) pkt_stats.slave_wifi_rx_msg_loaded = queue_load; #endif - load_percent = (queue_load*100/SDIO_RX_QUEUE_SIZE); + load_percent = (queue_load*100/NUM_RX_BUFFERS); if (load_percent < slv_cfg_g.throttle_low_threshold) { slv_state_g.current_throttling = 0; ESP_LOGV(TAG, "stop data throttling at host"); @@ -244,9 +244,13 @@ void generate_startup_event(uint8_t cap, uint32_t ext_cap) *pos = LENGTH_1_BYTE; pos++;len++; *pos = raw_tp_cap; pos++;len++; + *pos = ESP_PRIV_TX_Q_SIZE; pos++;len++; + *pos = LENGTH_1_BYTE; pos++;len++; + *pos = SDIO_TX_QUEUE_SIZE; pos++;len++; + *pos = ESP_PRIV_RX_Q_SIZE; pos++;len++; *pos = LENGTH_1_BYTE; pos++;len++; - *pos = SDIO_RX_QUEUE_SIZE; pos++;len++; + *pos = NUM_RX_BUFFERS; pos++;len++; /* TLVs end */ event->event_len = len; @@ -316,7 +320,7 @@ static interface_handle_t * sdio_init(void) #else #error Invalid SDIO bus speed selection #endif - .timing = SDIO_SLAVE_TIMING, + .timing = SDIO_SLAVE_TIMING, }; #if CONFIG_ESP_SDIO_STREAMING_MODE @@ -325,20 +329,20 @@ static interface_handle_t * sdio_init(void) ESP_LOGI(TAG, "%s: sending mode: SDIO_SLAVE_SEND_PACKET", __func__); #endif #if defined(CONFIG_IDF_TARGET_ESP32C6) - ESP_LOGI(TAG, "%s: ESP32-C6 SDIO RxQ[%d] timing[%u]\n", __func__, SDIO_RX_QUEUE_SIZE, config.timing); + ESP_LOGI(TAG, "%s: ESP32-C6 SDIO TxQ[%d] timing[%u]\n", __func__, SDIO_TX_QUEUE_SIZE, config.timing); #else - ESP_LOGI(TAG, "%s: ESP32 SDIO RxQ[%d] timing[%u]\n", __func__, SDIO_RX_QUEUE_SIZE, config.timing); + ESP_LOGI(TAG, "%s: ESP32 SDIO TxQ[%d] timing[%u]\n", __func__, SDIO_TX_QUEUE_SIZE, config.timing); #endif #if !SIMPLIFIED_SDIO_SLAVE sdio_send_queue_sem = xSemaphoreCreateCounting(SDIO_SLAVE_QUEUE_SIZE, SDIO_SLAVE_QUEUE_SIZE); assert(sdio_send_queue_sem); - sdio_rx_sem = xSemaphoreCreateCounting(SDIO_RX_QUEUE_SIZE*3, 0); + sdio_rx_sem = xSemaphoreCreateCounting(NUM_RX_BUFFERS * MAX_PRIORITY_QUEUES, 0); assert(sdio_rx_sem != NULL); for (prio_q_idx=0; prio_q_idx Date: Wed, 11 Jun 2025 10:18:24 +0800 Subject: [PATCH 32/44] fix(ota): handle HTTP_EVENT_ON_HEADERS_COMPLETE event --- .gitlab-ci.yml | 3 ++- host/port/esp/freertos/src/esp_hosted_ota.c | 4 ++++ idf_component.yml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ee58fcb4..fe1d81f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,4 +100,5 @@ build_idf_master: matrix: - IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6", "esp32c5", "esp32", "esp32c2", "esp32c3", "esp32s3" ] - IDF_EXAMPLE_PATH: [examples/wifi/iperf] + IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp + # IDF_EXAMPLE_PATH: [examples/wifi/iperf] diff --git a/host/port/esp/freertos/src/esp_hosted_ota.c b/host/port/esp/freertos/src/esp_hosted_ota.c index a0aae214..332862f2 100644 --- a/host/port/esp/freertos/src/esp_hosted_ota.c +++ b/host/port/esp/freertos/src/esp_hosted_ota.c @@ -57,6 +57,10 @@ static esp_err_t http_client_event_handler(esp_http_client_event_t *evt) case HTTP_EVENT_REDIRECT: ESP_LOGW(TAG, "HTTP_EVENT_REDIRECT"); break; + // Other trivial events like HTTP_EVENT_ON_HEADERS_COMPLETE can be handled when needed + default: + ESP_LOGD(TAG, "Unhandled event id: %d", evt->event_id); + break; } return ESP_OK; diff --git a/idf_component.yml b/idf_component.yml index 002a9b6e..4e751500 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.6" +version: "2.0.7" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From e5eea34d086a4fc81e1341f9040eec7b6fe13db0 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Wed, 11 Jun 2025 18:39:08 +0800 Subject: [PATCH 33/44] fix(ci_for_master): CI with mqtt/tcp and iperf on master branch - CI build with mqtt/tcp and iperf on master branch --- .gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fe1d81f8..bcd622ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,5 +100,4 @@ build_idf_master: matrix: - IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6", "esp32c5", "esp32", "esp32c2", "esp32c3", "esp32s3" ] - IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp - # IDF_EXAMPLE_PATH: [examples/wifi/iperf] + IDF_EXAMPLE_PATH: [examples/protocols/mqtt/tcp, examples/wifi/iperf] From c0e349b7ab822ecf98aeff511c479d35a9edfdda Mon Sep 17 00:00:00 2001 From: Christian Krenslehner Date: Wed, 11 Jun 2025 14:26:06 +0200 Subject: [PATCH 34/44] Fix typos --- docs/bluetooth_design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bluetooth_design.md b/docs/bluetooth_design.md index 9af184d3..cc52e89c 100644 --- a/docs/bluetooth_design.md +++ b/docs/bluetooth_design.md @@ -49,7 +49,7 @@ here. Users can use their own preferred Bluetooth stack with some porting effort. `esp-nimble` is a fork of Apache NimBLE and available from -ESP-IDF. The NimBLE Bluetooth slack proves Bluetooth Low Energy (BLE) +ESP-IDF. The NimBLE Bluetooth stack provides Bluetooth Low Energy (BLE) only functionality. `esp-bluedroid` is a fork of the Bluedroid based stack and available From 08c6580a4970f152f1a05ba893503d2be8f04939 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Thu, 12 Jun 2025 17:57:58 +0530 Subject: [PATCH 35/44] fix: Build issue due to inclusion of esp_hosted_wifi_config.h in public headers --- CMakeLists.txt | 59 +++++++++---------- .../api/include/esp_hosted_wifi_remote_glue.h | 44 ++++++++++++++ .../esp_hosted_api_priv.h} | 36 +++-------- host/api/src/esp_hosted_api.c | 25 ++------ host/api/src/esp_wifi_weak.c | 2 +- host/drivers/rpc/wrap/rpc_wrap.c | 1 - host/drivers/rpc/wrap/rpc_wrap.h | 4 +- host/esp_hosted.h | 29 ++++++++- host/esp_hosted_host_init.c | 2 +- idf_component.yml | 2 +- 10 files changed, 119 insertions(+), 85 deletions(-) create mode 100644 host/api/include/esp_hosted_wifi_remote_glue.h rename host/api/{include/esp_hosted_api.h => priv/esp_hosted_api_priv.h} (80%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41e4423f..3f79b2fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,83 +3,82 @@ if(CONFIG_ESP_HOSTED_ENABLED) set(FG_root_dir ".") set(host_dir "${FG_root_dir}/host") - set(COMPONENT_SRCS "${host_dir}/esp_hosted_host_init.c" "${host_dir}/api/src/esp_wifi_weak.c" "${host_dir}/api/src/esp_hosted_api.c" "${host_dir}/drivers/transport/transport_drv.c" "${host_dir}/drivers/serial/serial_ll_if.c" "${host_dir}/utils/common.c" "${host_dir}/utils/util.c" "${host_dir}/utils/stats.c" "${host_dir}/drivers/serial/serial_drv.c") + set(srcs "${host_dir}/esp_hosted_host_init.c" "${host_dir}/api/src/esp_wifi_weak.c" "${host_dir}/api/src/esp_hosted_api.c" "${host_dir}/drivers/transport/transport_drv.c" "${host_dir}/drivers/serial/serial_ll_if.c" "${host_dir}/utils/common.c" "${host_dir}/utils/util.c" "${host_dir}/utils/stats.c" "${host_dir}/drivers/serial/serial_drv.c") # only these directories are public. Others are private - set(COMPONENT_ADD_PUBLIC_INCLUDEDIRS "${host_dir}" "${host_dir}/api/include") + set(pub_include "${host_dir}" "${host_dir}/api/include") - set(COMPONENT_ADD_INCLUDEDIRS "${host_dir}/drivers/transport" "${host_dir}/drivers/transport/spi" "${host_dir}/drivers/transport/sdio" "${host_dir}/drivers/serial" "${host_dir}/utils") + set(priv_include "${host_dir}/drivers/transport" "${host_dir}/drivers/transport/spi" "${host_dir}/drivers/transport/sdio" "${host_dir}/drivers/serial" "${host_dir}/utils" "${host_dir}/api/priv") # rpc files - wrap -> slaveif -> core set(rpc_dir "${host_dir}/drivers/rpc") set(rpc_core_dir "${rpc_dir}/core") set(rpc_slaveif_dir "${rpc_dir}/slaveif") set(rpc_wrap_dir "${rpc_dir}/wrap") - list(APPEND COMPONENT_SRCS "${rpc_core_dir}/rpc_core.c" "${rpc_core_dir}/rpc_req.c" "${rpc_core_dir}/rpc_rsp.c" "${rpc_core_dir}/rpc_evt.c" + list(APPEND srcs "${rpc_core_dir}/rpc_core.c" "${rpc_core_dir}/rpc_req.c" "${rpc_core_dir}/rpc_rsp.c" "${rpc_core_dir}/rpc_evt.c" "${rpc_slaveif_dir}/rpc_slave_if.c" "${rpc_wrap_dir}/rpc_wrap.c") - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${rpc_core_dir}" "${rpc_slaveif_dir}" "${rpc_wrap_dir}") + list(APPEND priv_include "${rpc_core_dir}" "${rpc_slaveif_dir}" "${rpc_wrap_dir}") # virtual serial set(virt_serial_dir "${host_dir}/drivers/virtual_serial_if") - list(APPEND COMPONENT_SRCS "${virt_serial_dir}/serial_if.c") - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${virt_serial_dir}") + list(APPEND srcs "${virt_serial_dir}/serial_if.c") + list(APPEND priv_include "${virt_serial_dir}") # mempool - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/mempool/mempool.c") - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/drivers/mempool" ) + list(APPEND srcs "${host_dir}/drivers/mempool/mempool.c") + list(APPEND priv_include "${host_dir}/drivers/mempool" ) # slave and host common files set(common_dir "${FG_root_dir}/common") - list(APPEND COMPONENT_SRCS "${common_dir}/protobuf-c/protobuf-c/protobuf-c.c" "${common_dir}/proto/esp_hosted_rpc.pb-c.c" ) - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${common_dir}" "${common_dir}/log" "${common_dir}/rpc" "${common_dir}/transport" "${common_dir}/protobuf-c" "${common_dir}/proto" ) + list(APPEND srcs "${common_dir}/protobuf-c/protobuf-c/protobuf-c.c" "${common_dir}/proto/esp_hosted_rpc.pb-c.c" ) + list(APPEND priv_include "${common_dir}" "${common_dir}/log" "${common_dir}/rpc" "${common_dir}/transport" "${common_dir}/protobuf-c" "${common_dir}/proto" ) # host ESP32 specific files - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/esp_hosted_ota.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/esp_hosted_ota.c") # bt (NimBLE) ### TODO config for HCI over UART - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/drivers/bt") + list(APPEND priv_include "${host_dir}/drivers/bt") if(CONFIG_ESP_HOSTED_NIMBLE_HCI_VHCI OR CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI) - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/bt/vhci_drv.c") + list(APPEND srcs "${host_dir}/drivers/bt/vhci_drv.c") else() - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/bt/hci_stub_drv.c") + list(APPEND srcs "${host_dir}/drivers/bt/hci_stub_drv.c") endif() # transport files if(CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/transport/sdio/sdio_drv.c") + list(APPEND srcs "${host_dir}/drivers/transport/sdio/sdio_drv.c") elseif(CONFIG_ESP_HOSTED_SPI_HD_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/transport/spi_hd/spi_hd_drv.c") + list(APPEND srcs "${host_dir}/drivers/transport/spi_hd/spi_hd_drv.c") elseif(CONFIG_ESP_HOSTED_SPI_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/transport/spi/spi_drv.c") + list(APPEND srcs "${host_dir}/drivers/transport/spi/spi_drv.c") elseif(CONFIG_ESP_HOSTED_UART_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/drivers/transport/uart/uart_drv.c") + list(APPEND srcs "${host_dir}/drivers/transport/uart/uart_drv.c") endif() # config files - list(APPEND COMPONENT_ADD_INCLUDEDIRS "${host_dir}/port/esp/freertos/include") - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/esp_hosted_config.c" "${host_dir}/port/esp/freertos/src/esp_hosted_transport_config.c") + list(APPEND priv_include "${host_dir}/port/esp/freertos/include") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/esp_hosted_config.c" "${host_dir}/port/esp/freertos/src/esp_hosted_transport_config.c") # transport port files - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/os_wrapper.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/os_wrapper.c") if(CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/sdio_wrapper.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/sdio_wrapper.c") elseif(CONFIG_ESP_HOSTED_SPI_HD_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/spi_hd_wrapper.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/spi_hd_wrapper.c") elseif(CONFIG_ESP_HOSTED_SPI_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/spi_wrapper.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/spi_wrapper.c") elseif(CONFIG_ESP_HOSTED_UART_HOST_INTERFACE) - list(APPEND COMPONENT_SRCS "${host_dir}/port/esp/freertos/src/uart_wrapper.c") + list(APPEND srcs "${host_dir}/port/esp/freertos/src/uart_wrapper.c") endif() endif() -idf_component_register(SRCS ${COMPONENT_SRCS} +idf_component_register(SRCS ${srcs} PRIV_REQUIRES soc esp_event esp_netif esp_timer driver esp_wifi bt esp_http_client - EXCLUDE_SRCS ${EXCLUDE_COMPONENT_SRCS} - INCLUDE_DIRS ${COMPONENT_ADD_PUBLIC_INCLUDEDIRS} - PRIV_INCLUDE_DIRS ${COMPONENT_ADD_INCLUDEDIRS}) + INCLUDE_DIRS ${pub_include} + PRIV_INCLUDE_DIRS ${priv_include}) idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE) diff --git a/host/api/include/esp_hosted_wifi_remote_glue.h b/host/api/include/esp_hosted_wifi_remote_glue.h new file mode 100644 index 00000000..6e070556 --- /dev/null +++ b/host/api/include/esp_hosted_wifi_remote_glue.h @@ -0,0 +1,44 @@ +/* +* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +#ifndef __ESP_HOSTED_WIFI_REMOTE_GLUE_H__ +#define __ESP_HOSTED_WIFI_REMOTE_GLUE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_hosted_interface.h" +#include "esp_wifi_remote.h" +#include "esp_wifi.h" + +struct esp_remote_channel_config { + esp_hosted_if_type_t if_type; + bool secure; +}; + +typedef struct esp_remote_channel_config * esp_remote_channel_config_t; + +/* Transport/Channel related data structures and macros */ +#define ESP_HOSTED_CHANNEL_CONFIG_DEFAULT() { \ + .secure = true, \ +} + +/* Function pointer types for channel callbacks */ +typedef esp_err_t (*esp_remote_channel_rx_fn_t)(void *h, void *buffer, + void *buff_to_free, size_t len); +typedef esp_err_t (*esp_remote_channel_tx_fn_t)(void *h, void *buffer, size_t len); + +/* Transport/Channel Management API Functions - use managed component typedef */ +esp_remote_channel_t esp_hosted_add_channel(esp_remote_channel_config_t config, + esp_remote_channel_tx_fn_t *tx, const esp_remote_channel_rx_fn_t rx); +esp_err_t esp_hosted_remove_channel(esp_remote_channel_t channel); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_HOSTED_WIFI_REMOTE_GLUE_H__ */ diff --git a/host/api/include/esp_hosted_api.h b/host/api/priv/esp_hosted_api_priv.h similarity index 80% rename from host/api/include/esp_hosted_api.h rename to host/api/priv/esp_hosted_api_priv.h index 94e1535e..94a817bc 100644 --- a/host/api/include/esp_hosted_api.h +++ b/host/api/priv/esp_hosted_api_priv.h @@ -4,15 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -/** prevent recursive inclusion **/ -#ifndef __ESP_HOSTED_API_H__ -#define __ESP_HOSTED_API_H__ +/* prevent recursive inclusion */ +#ifndef __ESP_HOSTED_API_PRIV_H__ +#define __ESP_HOSTED_API_PRIV_H__ #ifdef __cplusplus extern "C" { #endif -/** Includes **/ +/* Includes */ #include "stdbool.h" #include "esp_wifi.h" #include "esp_wifi_remote.h" @@ -20,29 +20,7 @@ extern "C" { #include "esp_hosted_ota.h" #include "esp_hosted_wifi_config.h" -/** Exported variables **/ -#define ESP_HOSTED_CHANNEL_CONFIG_DEFAULT() { \ - .secure = true, \ -} - -struct esp_remote_channel_config { - int if_type; // values should be of esp_hosted_if_type_t - bool secure; -}; - -/** Inline functions **/ - -/** Exported Functions **/ -esp_err_t esp_hosted_init(void); -esp_err_t esp_hosted_deinit(void); -esp_err_t esp_hosted_reinit(void); - -esp_err_t esp_hosted_setup(void); -esp_err_t esp_hosted_slave_reset(void); -esp_remote_channel_t esp_hosted_add_channel(esp_remote_channel_config_t config, - esp_remote_channel_tx_fn_t *tx, const esp_remote_channel_rx_fn_t rx); -esp_err_t esp_hosted_remove_channel(esp_remote_channel_t channel); - +/* Remote WiFi API Functions - Port/Implementation Specific */ esp_err_t esp_wifi_remote_init(const wifi_init_config_t *arg); esp_err_t esp_wifi_remote_deinit(void); esp_err_t esp_wifi_remote_set_mode(wifi_mode_t mode); @@ -86,9 +64,9 @@ esp_err_t esp_wifi_remote_set_max_tx_power(int8_t power); esp_err_t esp_wifi_remote_get_max_tx_power(int8_t *power); esp_err_t esp_wifi_remote_sta_get_negotiated_phymode(wifi_phy_mode_t *phymode); esp_err_t esp_wifi_remote_sta_get_aid(uint16_t *aid); -esp_err_t esp_hosted_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info); #if H_WIFI_DUALBAND_SUPPORT +/* Dual-band WiFi API (Depends upon co-processor used) */ esp_err_t esp_wifi_remote_set_band(wifi_band_t band); esp_err_t esp_wifi_remote_get_band(wifi_band_t *band); esp_err_t esp_wifi_remote_set_band_mode(wifi_band_mode_t band_mode); @@ -103,4 +81,4 @@ esp_err_t esp_wifi_remote_get_bandwidths(wifi_interface_t ifx, wifi_bandwidths_t } #endif -#endif +#endif /* __ESP_HOSTED_API_PRIV_H__ */ \ No newline at end of file diff --git a/host/api/src/esp_hosted_api.c b/host/api/src/esp_hosted_api.c index fb877f87..01aa034b 100644 --- a/host/api/src/esp_hosted_api.c +++ b/host/api/src/esp_hosted_api.c @@ -10,9 +10,9 @@ extern "C" { /** Includes **/ #include "esp_hosted_transport_config.h" -#include "esp_wifi_remote.h" #include "esp_hosted_wifi_config.h" -#include "esp_hosted_api.h" +#include "esp_hosted_api_priv.h" +#include "esp_hosted_wifi_remote_glue.h" #include "esp_check.h" #include "transport_drv.h" #include "rpc_wrap.h" @@ -30,10 +30,6 @@ struct esp_remote_channel { //static semaphore_handle_t transport_up_sem; -typedef esp_err_t (*esp_remote_channel_rx_fn_t)(void *h, void *buffer, - void *buff_to_free, size_t len); -typedef esp_err_t (*esp_remote_channel_tx_fn_t)(void *h, void *buffer, size_t len); - /** Inline functions **/ /** Exported Functions **/ @@ -96,7 +92,7 @@ static void set_host_modules_log_level(void) esp_log_level_set("rpc_rsp", ESP_LOG_WARN); esp_log_level_set("rpc_evt", ESP_LOG_WARN); } -esp_err_t esp_hosted_init(void) +int esp_hosted_init(void) { if (esp_hosted_init_done) return ESP_OK; @@ -120,7 +116,7 @@ esp_err_t esp_hosted_init(void) return ESP_OK; } -esp_err_t esp_hosted_deinit(void) +int esp_hosted_deinit(void) { ESP_LOGI(TAG, "ESP-Hosted deinit\n"); rpc_unregister_event_callbacks(); @@ -141,16 +137,7 @@ static inline esp_err_t esp_hosted_reconfigure(void) return ESP_OK; } -esp_err_t esp_hosted_reinit(void) -{ - ESP_LOGI(TAG, "ESP-Hosted re-init\n"); - ESP_ERROR_CHECK(esp_hosted_deinit()); - ESP_ERROR_CHECK(esp_hosted_init()); - ESP_ERROR_CHECK(esp_hosted_reconfigure()); - return ESP_OK; -} - -esp_err_t esp_hosted_slave_reset(void) +int esp_hosted_connect_to_slave(void) { ESP_LOGI(TAG, "ESP-Hosted Try to communicate with ESP-Hosted slave\n"); return esp_hosted_reconfigure(); @@ -399,8 +386,8 @@ esp_err_t esp_wifi_remote_sta_get_aid(uint16_t *aid) { return rpc_wifi_sta_get_aid(aid); } - #if H_WIFI_DUALBAND_SUPPORT +/* Dual-band WiFi API - always available at high level, but returns ESP_ERR_NOT_SUPPORTED when co-processor do not support */ esp_err_t esp_wifi_remote_set_band(wifi_band_t band) { return rpc_wifi_set_band(band); diff --git a/host/api/src/esp_wifi_weak.c b/host/api/src/esp_wifi_weak.c index 33459d72..ba9374d4 100644 --- a/host/api/src/esp_wifi_weak.c +++ b/host/api/src/esp_wifi_weak.c @@ -19,7 +19,7 @@ * Used when WiFi-Remote does not provide required esp_wifi calls */ -#include "esp_hosted_api.h" +#include "esp_hosted_api_priv.h" #include "esp_hosted_wifi_config.h" #define WEAK __attribute__((weak)) diff --git a/host/drivers/rpc/wrap/rpc_wrap.c b/host/drivers/rpc/wrap/rpc_wrap.c index ee71d6d6..d0c9aca8 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.c +++ b/host/drivers/rpc/wrap/rpc_wrap.c @@ -29,7 +29,6 @@ #include "esp_hosted_rpc.h" #include "esp_log.h" #include "esp_hosted_wifi_config.h" -#include "esp_hosted_api.h" #include "esp_hosted_transport.h" DEFINE_LOG_TAG(rpc_wrap); diff --git a/host/drivers/rpc/wrap/rpc_wrap.h b/host/drivers/rpc/wrap/rpc_wrap.h index 048e572c..04c64f3d 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.h +++ b/host/drivers/rpc/wrap/rpc_wrap.h @@ -25,8 +25,8 @@ extern "C" { #include "common.h" #include "esp_wifi.h" #include "esp_hosted_wifi_config.h" -#include "esp_hosted_api.h" #include "esp_hosted_api_types.h" +#include "esp_hosted_ota.h" /** Exported variables **/ @@ -81,7 +81,7 @@ esp_err_t rpc_wifi_set_max_tx_power(int8_t power); esp_err_t rpc_wifi_get_max_tx_power(int8_t *power); esp_err_t rpc_wifi_sta_get_negotiated_phymode(wifi_phy_mode_t *phymode); esp_err_t rpc_wifi_sta_get_aid(uint16_t *aid); -esp_err_t rpc_ota(const char* image_url); + esp_err_t rpc_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info); esp_err_t rpc_ota_begin(void); diff --git a/host/esp_hosted.h b/host/esp_hosted.h index cfff1d3b..a7bbe437 100644 --- a/host/esp_hosted.h +++ b/host/esp_hosted.h @@ -7,11 +7,38 @@ #ifndef __ESP_HOSTED_H__ #define __ESP_HOSTED_H__ -#include "esp_hosted_api.h" +#ifdef __cplusplus +extern "C" { +#endif + #include "esp_hosted_config.h" #include "esp_hosted_bt_config.h" #include "esp_hosted_transport_config.h" +#include "esp_hosted_api_types.h" typedef struct esp_hosted_transport_config esp_hosted_config_t; +/* --------- Hosted Minimal APIs --------- */ +int esp_hosted_init(void); +int esp_hosted_deinit(void); + +int esp_hosted_connect_to_slave(void); +int esp_hosted_get_coprocessor_fwversion(esp_hosted_coprocessor_fwver_t *ver_info); + +/* --------- Exhaustive API list --------- */ +/* + * 1. All Wi-Fi supported APIs + * File: host/api/src/esp_wifi_weak.c + * + * 2. Communication Bus APIs (Set and get transport config) + * File : host/api/include/esp_hosted_transport_config.h + * + * 3. Co-Processor OTA API + * File : host/api/include/esp_hosted_ota.h + */ + +#ifdef __cplusplus +} +#endif + #endif /* __ESP_HOSTED_H__ */ diff --git a/host/esp_hosted_host_init.c b/host/esp_hosted_host_init.c index 30479e06..aa508f25 100644 --- a/host/esp_hosted_host_init.c +++ b/host/esp_hosted_host_init.c @@ -7,7 +7,7 @@ #include "os_header.h" #include "os_wrapper.h" #include "esp_log.h" -#include "esp_hosted_api.h" +#include "esp_hosted.h" #include "esp_private/startup_internal.h" DEFINE_LOG_TAG(host_init); diff --git a/idf_component.yml b/idf_component.yml index 4e751500..e3acb7f8 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.7" +version: "2.0.8" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 2a5e9fd6cd1096b3f21c20d5986bb47aa066a2b3 Mon Sep 17 00:00:00 2001 From: Yogesh Mantri Date: Fri, 13 Jun 2025 17:34:31 +0530 Subject: [PATCH 36/44] fix(sync): as C5 takes little longer, waait for 1500ms instead of 1000ms --- host/drivers/transport/transport_drv.c | 2 +- idf_component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/host/drivers/transport/transport_drv.c b/host/drivers/transport/transport_drv.c index 3039d2ee..f3022149 100644 --- a/host/drivers/transport/transport_drv.c +++ b/host/drivers/transport/transport_drv.c @@ -79,7 +79,7 @@ static void reset_slave(void) g_h.funcs->_h_write_gpio(reset_pin.port, reset_pin.pin, H_RESET_VAL_ACTIVE); /* stop spi transactions short time to avoid slave sync issues */ - g_h.funcs->_h_sleep(1); + g_h.funcs->_h_msleep(1500); } static void transport_driver_event_handler(uint8_t event) diff --git a/idf_component.yml b/idf_component.yml index e3acb7f8..1ced3202 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.8" +version: "2.0.9" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 2fe99d2f0e266d3912fc55bdbe04e43401eca17c Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 17 Jun 2025 12:02:34 +0800 Subject: [PATCH 37/44] fix(ota): Fixed build break when doing ota - include `esp_hosted_ota.h` in `esp_hosted.h`: otherwise, app cannot call ota API - removed sdmmc include from `esp_hosted_config.h`: breaks compilation and not needed - tested by including `esp_hosted.h` and calling ota API from app --- host/api/include/esp_hosted_config.h | 4 ---- host/esp_hosted.h | 1 + idf_component.yml | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/host/api/include/esp_hosted_config.h b/host/api/include/esp_hosted_config.h index 4c974824..f4e796d8 100644 --- a/host/api/include/esp_hosted_config.h +++ b/host/api/include/esp_hosted_config.h @@ -16,10 +16,6 @@ #define H_TRANSPORT_SPI 3 #define H_TRANSPORT_UART 4 -#ifdef CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE -#include "driver/sdmmc_host.h" -#endif - #ifdef CONFIG_ESP_HOSTED_UART_HOST_INTERFACE #include "hal/uart_types.h" #endif diff --git a/host/esp_hosted.h b/host/esp_hosted.h index a7bbe437..31c77680 100644 --- a/host/esp_hosted.h +++ b/host/esp_hosted.h @@ -15,6 +15,7 @@ extern "C" { #include "esp_hosted_bt_config.h" #include "esp_hosted_transport_config.h" #include "esp_hosted_api_types.h" +#include "esp_hosted_ota.h" typedef struct esp_hosted_transport_config esp_hosted_config_t; diff --git a/idf_component.yml b/idf_component.yml index 1ced3202..66957d29 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.9" +version: "2.0.10" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From 607153424f30246d7e99c1458ff177dac77a0bcb Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Tue, 17 Jun 2025 15:08:58 +0800 Subject: [PATCH 38/44] fix(scan_timeout): increase timeout if doing blocking scan - increase default timeout if use requests to block while doing scan, which may take a longer time --- host/drivers/rpc/slaveif/rpc_slave_if.h | 2 +- host/drivers/rpc/wrap/rpc_wrap.c | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/host/drivers/rpc/slaveif/rpc_slave_if.h b/host/drivers/rpc/slaveif/rpc_slave_if.h index b5621b80..685531a9 100644 --- a/host/drivers/rpc/slaveif/rpc_slave_if.h +++ b/host/drivers/rpc/slaveif/rpc_slave_if.h @@ -46,7 +46,7 @@ extern "C" { * */ //#define WAIT_TIME_B2B_RPC_REQ 5 #define DEFAULT_RPC_RSP_TIMEOUT 5 - +#define DEFAULT_RPC_RSP_SCAN_TIMEOUT 30 #define SUCCESS_STR "success" #define FAILURE_STR "failure" diff --git a/host/drivers/rpc/wrap/rpc_wrap.c b/host/drivers/rpc/wrap/rpc_wrap.c index d0c9aca8..47aa1cdb 100644 --- a/host/drivers/rpc/wrap/rpc_wrap.c +++ b/host/drivers/rpc/wrap/rpc_wrap.c @@ -53,7 +53,7 @@ static ctrl_cmd_t * RPC_DEFAULT_REQ(void) assert(new_req); new_req->msg_type = RPC_TYPE__Req; new_req->rpc_rsp_cb = NULL; - new_req->rsp_timeout_sec = DEFAULT_RPC_RSP_TIMEOUT; /* 5 sec */ + new_req->rsp_timeout_sec = DEFAULT_RPC_RSP_TIMEOUT; /* new_req->wait_prev_cmd_completion = WAIT_TIME_B2B_RPC_REQ; */ return new_req; } @@ -955,7 +955,10 @@ int rpc_wifi_scan_start(const wifi_scan_config_t *config, bool block) } req->u.wifi_scan_config.block = block; - + if (req->u.wifi_scan_config.block) { + // blocking while doing scan may take a long time: increase timeout value + req->rsp_timeout_sec = DEFAULT_RPC_RSP_SCAN_TIMEOUT; + } resp = rpc_slaveif_wifi_scan_start(req); return rpc_rsp_callback(resp); From 551711fb11f7c056b891ebeb0117d17aeb5a44d3 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Fri, 30 May 2025 16:15:43 +0800 Subject: [PATCH 39/44] feature(update_wifi_structs): updated structs for sta and softap - added channel bitmaps (2.4GHz and 5GHz) to RPC scan structure - updated structure definitions for station and softap to match with latest ESP-IDF Wi-Fi headers - added defines to control compilation of parts that are structure specific to ESP-IDF 5.4.0 or 5.5.0 --- common/proto/esp_hosted_rpc.pb-c.c | 333 +++++++++++++++++- common/proto/esp_hosted_rpc.pb-c.h | 133 ++++++- common/proto/esp_hosted_rpc.proto | 26 +- host/drivers/rpc/core/rpc_req.c | 33 +- host/drivers/rpc/core/rpc_rsp.c | 38 +- .../freertos/include/esp_hosted_wifi_config.h | 10 +- idf_component.yml | 2 +- slave/main/slave_control.c | 90 +++-- 8 files changed, 599 insertions(+), 66 deletions(-) diff --git a/common/proto/esp_hosted_rpc.pb-c.c b/common/proto/esp_hosted_rpc.pb-c.c index eb1a1e39..6516892e 100644 --- a/common/proto/esp_hosted_rpc.pb-c.c +++ b/common/proto/esp_hosted_rpc.pb-c.c @@ -187,6 +187,51 @@ void wifi_scan_time__free_unpacked assert(message->base.descriptor == &wifi_scan_time__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void wifi_scan_channel_bitmap__init + (WifiScanChannelBitmap *message) +{ + static const WifiScanChannelBitmap init_value = WIFI_SCAN_CHANNEL_BITMAP__INIT; + *message = init_value; +} +size_t wifi_scan_channel_bitmap__get_packed_size + (const WifiScanChannelBitmap *message) +{ + assert(message->base.descriptor == &wifi_scan_channel_bitmap__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t wifi_scan_channel_bitmap__pack + (const WifiScanChannelBitmap *message, + uint8_t *out) +{ + assert(message->base.descriptor == &wifi_scan_channel_bitmap__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t wifi_scan_channel_bitmap__pack_to_buffer + (const WifiScanChannelBitmap *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &wifi_scan_channel_bitmap__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +WifiScanChannelBitmap * + wifi_scan_channel_bitmap__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (WifiScanChannelBitmap *) + protobuf_c_message_unpack (&wifi_scan_channel_bitmap__descriptor, + allocator, len, data); +} +void wifi_scan_channel_bitmap__free_unpacked + (WifiScanChannelBitmap *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &wifi_scan_channel_bitmap__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void wifi_scan_config__init (WifiScanConfig *message) { @@ -412,6 +457,51 @@ void wifi_pmf_config__free_unpacked assert(message->base.descriptor == &wifi_pmf_config__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } +void wifi_bss_max_idle_config__init + (WifiBssMaxIdleConfig *message) +{ + static const WifiBssMaxIdleConfig init_value = WIFI_BSS_MAX_IDLE_CONFIG__INIT; + *message = init_value; +} +size_t wifi_bss_max_idle_config__get_packed_size + (const WifiBssMaxIdleConfig *message) +{ + assert(message->base.descriptor == &wifi_bss_max_idle_config__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t wifi_bss_max_idle_config__pack + (const WifiBssMaxIdleConfig *message, + uint8_t *out) +{ + assert(message->base.descriptor == &wifi_bss_max_idle_config__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t wifi_bss_max_idle_config__pack_to_buffer + (const WifiBssMaxIdleConfig *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &wifi_bss_max_idle_config__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +WifiBssMaxIdleConfig * + wifi_bss_max_idle_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (WifiBssMaxIdleConfig *) + protobuf_c_message_unpack (&wifi_bss_max_idle_config__descriptor, + allocator, len, data); +} +void wifi_bss_max_idle_config__free_unpacked + (WifiBssMaxIdleConfig *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &wifi_bss_max_idle_config__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} void wifi_ap_config__init (WifiApConfig *message) { @@ -7774,7 +7864,58 @@ const ProtobufCMessageDescriptor wifi_scan_time__descriptor = (ProtobufCMessageInit) wifi_scan_time__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor wifi_scan_config__field_descriptors[7] = +static const ProtobufCFieldDescriptor wifi_scan_channel_bitmap__field_descriptors[2] = +{ + { + "ghz_2_channels", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiScanChannelBitmap, ghz_2_channels), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "ghz_5_channels", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiScanChannelBitmap, ghz_5_channels), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned wifi_scan_channel_bitmap__field_indices_by_name[] = { + 0, /* field[0] = ghz_2_channels */ + 1, /* field[1] = ghz_5_channels */ +}; +static const ProtobufCIntRange wifi_scan_channel_bitmap__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor wifi_scan_channel_bitmap__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "wifi_scan_channel_bitmap", + "WifiScanChannelBitmap", + "WifiScanChannelBitmap", + "", + sizeof(WifiScanChannelBitmap), + 2, + wifi_scan_channel_bitmap__field_descriptors, + wifi_scan_channel_bitmap__field_indices_by_name, + 1, wifi_scan_channel_bitmap__number_ranges, + (ProtobufCMessageInit) wifi_scan_channel_bitmap__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor wifi_scan_config__field_descriptors[8] = { { "ssid", @@ -7860,10 +8001,23 @@ static const ProtobufCFieldDescriptor wifi_scan_config__field_descriptors[7] = 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "channel_bitmap", + 8, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(WifiScanConfig, channel_bitmap), + &wifi_scan_channel_bitmap__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned wifi_scan_config__field_indices_by_name[] = { 1, /* field[1] = bssid */ 2, /* field[2] = channel */ + 7, /* field[7] = channel_bitmap */ 6, /* field[6] = home_chan_dwell_time */ 5, /* field[5] = scan_time */ 4, /* field[4] = scan_type */ @@ -7873,7 +8027,7 @@ static const unsigned wifi_scan_config__field_indices_by_name[] = { static const ProtobufCIntRange wifi_scan_config__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 7 } + { 0, 8 } }; const ProtobufCMessageDescriptor wifi_scan_config__descriptor = { @@ -7883,7 +8037,7 @@ const ProtobufCMessageDescriptor wifi_scan_config__descriptor = "WifiScanConfig", "", sizeof(WifiScanConfig), - 7, + 8, wifi_scan_config__field_descriptors, wifi_scan_config__field_indices_by_name, 1, wifi_scan_config__number_ranges, @@ -8161,7 +8315,7 @@ const ProtobufCMessageDescriptor wifi_ap_record__descriptor = (ProtobufCMessageInit) wifi_ap_record__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor wifi_scan_threshold__field_descriptors[2] = +static const ProtobufCFieldDescriptor wifi_scan_threshold__field_descriptors[3] = { { "rssi", @@ -8187,15 +8341,28 @@ static const ProtobufCFieldDescriptor wifi_scan_threshold__field_descriptors[2] 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "rssi_5g_adjustment", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiScanThreshold, rssi_5g_adjustment), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned wifi_scan_threshold__field_indices_by_name[] = { 1, /* field[1] = authmode */ 0, /* field[0] = rssi */ + 2, /* field[2] = rssi_5g_adjustment */ }; static const ProtobufCIntRange wifi_scan_threshold__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 2 } + { 0, 3 } }; const ProtobufCMessageDescriptor wifi_scan_threshold__descriptor = { @@ -8205,7 +8372,7 @@ const ProtobufCMessageDescriptor wifi_scan_threshold__descriptor = "WifiScanThreshold", "", sizeof(WifiScanThreshold), - 2, + 3, wifi_scan_threshold__field_descriptors, wifi_scan_threshold__field_indices_by_name, 1, wifi_scan_threshold__number_ranges, @@ -8263,7 +8430,58 @@ const ProtobufCMessageDescriptor wifi_pmf_config__descriptor = (ProtobufCMessageInit) wifi_pmf_config__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor wifi_ap_config__field_descriptors[12] = +static const ProtobufCFieldDescriptor wifi_bss_max_idle_config__field_descriptors[2] = +{ + { + "period", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiBssMaxIdleConfig, period), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "protected_keep_alive", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BOOL, + 0, /* quantifier_offset */ + offsetof(WifiBssMaxIdleConfig, protected_keep_alive), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned wifi_bss_max_idle_config__field_indices_by_name[] = { + 0, /* field[0] = period */ + 1, /* field[1] = protected_keep_alive */ +}; +static const ProtobufCIntRange wifi_bss_max_idle_config__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor wifi_bss_max_idle_config__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "wifi_bss_max_idle_config", + "WifiBssMaxIdleConfig", + "WifiBssMaxIdleConfig", + "", + sizeof(WifiBssMaxIdleConfig), + 2, + wifi_bss_max_idle_config__field_descriptors, + wifi_bss_max_idle_config__field_indices_by_name, + 1, wifi_bss_max_idle_config__number_ranges, + (ProtobufCMessageInit) wifi_bss_max_idle_config__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor wifi_ap_config__field_descriptors[18] = { { "ssid", @@ -8409,25 +8627,103 @@ static const ProtobufCFieldDescriptor wifi_ap_config__field_descriptors[12] = 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "csa_count", + 13, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, csa_count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "dtim_period", + 14, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, dtim_period), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "transition_disable", + 15, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, transition_disable), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "sae_ext", + 16, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, sae_ext), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "bss_max_idle_cfg", + 17, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, bss_max_idle_cfg), + &wifi_bss_max_idle_config__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "gtk_rekey_interval", + 18, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiApConfig, gtk_rekey_interval), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned wifi_ap_config__field_indices_by_name[] = { 4, /* field[4] = authmode */ 7, /* field[7] = beacon_interval */ + 16, /* field[16] = bss_max_idle_cfg */ 3, /* field[3] = channel */ + 12, /* field[12] = csa_count */ + 13, /* field[13] = dtim_period */ 9, /* field[9] = ftm_responder */ + 17, /* field[17] = gtk_rekey_interval */ 6, /* field[6] = max_connection */ 8, /* field[8] = pairwise_cipher */ 1, /* field[1] = password */ 10, /* field[10] = pmf_cfg */ + 15, /* field[15] = sae_ext */ 11, /* field[11] = sae_pwe_h2e */ 0, /* field[0] = ssid */ 5, /* field[5] = ssid_hidden */ 2, /* field[2] = ssid_len */ + 14, /* field[14] = transition_disable */ }; static const ProtobufCIntRange wifi_ap_config__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 12 } + { 0, 18 } }; const ProtobufCMessageDescriptor wifi_ap_config__descriptor = { @@ -8437,14 +8733,14 @@ const ProtobufCMessageDescriptor wifi_ap_config__descriptor = "WifiApConfig", "", sizeof(WifiApConfig), - 12, + 18, wifi_ap_config__field_descriptors, wifi_ap_config__field_indices_by_name, 1, wifi_ap_config__number_ranges, (ProtobufCMessageInit) wifi_ap_config__init, NULL,NULL,NULL /* reserved[123] */ }; -static const ProtobufCFieldDescriptor wifi_sta_config__field_descriptors[15] = +static const ProtobufCFieldDescriptor wifi_sta_config__field_descriptors[16] = { { "ssid", @@ -8626,6 +8922,18 @@ static const ProtobufCFieldDescriptor wifi_sta_config__field_descriptors[15] = 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "sae_pk_mode", + 16, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(WifiStaConfig, sae_pk_mode), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned wifi_sta_config__field_indices_by_name[] = { 10, /* field[10] = bitmask */ @@ -8638,6 +8946,7 @@ static const unsigned wifi_sta_config__field_indices_by_name[] = { 1, /* field[1] = password */ 9, /* field[9] = pmf_cfg */ 14, /* field[14] = sae_h2e_identifier */ + 15, /* field[15] = sae_pk_mode */ 11, /* field[11] = sae_pwe_h2e */ 2, /* field[2] = scan_method */ 7, /* field[7] = sort_method */ @@ -8647,7 +8956,7 @@ static const unsigned wifi_sta_config__field_indices_by_name[] = { static const ProtobufCIntRange wifi_sta_config__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 15 } + { 0, 16 } }; const ProtobufCMessageDescriptor wifi_sta_config__descriptor = { @@ -8657,7 +8966,7 @@ const ProtobufCMessageDescriptor wifi_sta_config__descriptor = "WifiStaConfig", "", sizeof(WifiStaConfig), - 15, + 16, wifi_sta_config__field_descriptors, wifi_sta_config__field_indices_by_name, 1, wifi_sta_config__number_ranges, diff --git a/common/proto/esp_hosted_rpc.pb-c.h b/common/proto/esp_hosted_rpc.pb-c.h index 03a11fc5..2bfc6a4f 100644 --- a/common/proto/esp_hosted_rpc.pb-c.h +++ b/common/proto/esp_hosted_rpc.pb-c.h @@ -19,11 +19,13 @@ typedef struct WifiInitConfig WifiInitConfig; typedef struct WifiCountry WifiCountry; typedef struct WifiActiveScanTime WifiActiveScanTime; typedef struct WifiScanTime WifiScanTime; +typedef struct WifiScanChannelBitmap WifiScanChannelBitmap; typedef struct WifiScanConfig WifiScanConfig; typedef struct WifiHeApInfo WifiHeApInfo; typedef struct WifiApRecord WifiApRecord; typedef struct WifiScanThreshold WifiScanThreshold; typedef struct WifiPmfConfig WifiPmfConfig; +typedef struct WifiBssMaxIdleConfig WifiBssMaxIdleConfig; typedef struct WifiApConfig WifiApConfig; typedef struct WifiStaConfig WifiStaConfig; typedef struct WifiConfig WifiConfig; @@ -873,6 +875,23 @@ struct WifiScanTime , NULL, 0 } +struct WifiScanChannelBitmap +{ + ProtobufCMessage base; + /* + **< Represents 2.4 GHz channels, that bits can be set as wifi_2g_channel_bit_t shown. + */ + uint32_t ghz_2_channels; + /* + **< Represents 5 GHz channels, that bits can be set as wifi_5g_channel_bit_t shown. + */ + uint32_t ghz_5_channels; +}; +#define WIFI_SCAN_CHANNEL_BITMAP__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&wifi_scan_channel_bitmap__descriptor) \ + , 0, 0 } + + struct WifiScanConfig { ProtobufCMessage base; @@ -904,10 +923,16 @@ struct WifiScanConfig **< time spent at home channel between scanning consecutive channels. */ uint32_t home_chan_dwell_time; + /* + **< Channel bitmap for setting specific channels to be scanned. + *Please note that the 'channel' parameter above needs to be set to 0 to allow scanning by bitmap. + *Also, note that only allowed channels configured by wifi_country_t can be scanned. + */ + WifiScanChannelBitmap *channel_bitmap; }; #define WIFI_SCAN_CONFIG__INIT \ { PROTOBUF_C_MESSAGE_INIT (&wifi_scan_config__descriptor) \ - , {0,NULL}, {0,NULL}, 0, 0, 0, NULL, 0 } + , {0,NULL}, {0,NULL}, 0, 0, 0, NULL, 0, NULL } struct WifiHeApInfo @@ -1027,10 +1052,14 @@ struct WifiScanThreshold *Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */ int32_t authmode; + /* + **< The RSSI value of the 5G AP is within the rssi_5g_adjustment range compared to the 2G AP, the 5G AP will be given priority for connection. + */ + uint32_t rssi_5g_adjustment; }; #define WIFI_SCAN_THRESHOLD__INIT \ { PROTOBUF_C_MESSAGE_INIT (&wifi_scan_threshold__descriptor) \ - , 0, 0 } + , 0, 0, 0 } struct WifiPmfConfig @@ -1050,6 +1079,23 @@ struct WifiPmfConfig , 0, 0 } +struct WifiBssMaxIdleConfig +{ + ProtobufCMessage base; + /* + **< Sets BSS Max idle period (1 Unit = 1000TUs OR 1.024 Seconds). If there are no frames for this period from a STA, SoftAP will disassociate due to inactivity. Setting it to 0 disables the feature + */ + uint32_t period; + /* + **< Requires clients to use protected keep alive frames for BSS Max Idle period + */ + protobuf_c_boolean protected_keep_alive; +}; +#define WIFI_BSS_MAX_IDLE_CONFIG__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&wifi_bss_max_idle_config__descriptor) \ + , 0, 0 } + + struct WifiApConfig { ProtobufCMessage base; @@ -1103,10 +1149,34 @@ struct WifiApConfig **< Configuration for SAE PWE derivation method */ int32_t sae_pwe_h2e; + /* + **< Channel Switch Announcement Count. Notify the station that the channel will switch after the csa_count beacon intervals. Default value: 3 + */ + uint32_t csa_count; + /* + **< Dtim period of soft-AP. Range: 1 ~ 10. Default value: 1 + */ + uint32_t dtim_period; + /* + **< Whether to enable transition disable feature + */ + uint32_t transition_disable; + /* + **< Enable SAE EXT feature. SOC_GCMP_SUPPORT is required for this feature. + */ + uint32_t sae_ext; + /* + **< Configuration for bss max idle, effective if CONFIG_WIFI_BSS_MAX_IDLE_SUPPORT is enabled + */ + WifiBssMaxIdleConfig *bss_max_idle_cfg; + /* + **< GTK rekeying interval in seconds. If set to 0, GTK rekeying is disabled. Range: 60 ~ 65535 including 0. + */ + uint32_t gtk_rekey_interval; }; #define WIFI_AP_CONFIG__INIT \ { PROTOBUF_C_MESSAGE_INIT (&wifi_ap_config__descriptor) \ - , {0,NULL}, {0,NULL}, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0 } + , {0,NULL}, {0,NULL}, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0, NULL, 0 } struct WifiStaConfig @@ -1185,17 +1255,24 @@ struct WifiStaConfig *uint32_t he_trig_su_bmforming_feedback_disabled:1; **< Whether to disable support the transmission of SU feedback in an HE TB sounding sequence. * *uint32_t he_trig_mu_bmforming_partial_feedback_disabled:1; **< Whether to disable support the transmission of partial-bandwidth MU feedback in an HE TB sounding sequence. * * uint32_t he_trig_cqi_feedback_disabled:1; **< Whether to disable support the transmission of CQI feedback in an HE TB sounding sequence. * - * uint32_t he_reserved:22; **< Reserved for future feature set * + * uint32_t vht_su_beamformee_disabled: 1; **< Whether to disable support for operation as an VHT SU beamformee. * + * uint32_t vht_mu_beamformee_disabled: 1; **< Whether to disable support for operation as an VHT MU beamformee. * + * uint32_t vht_mcs8_enabled: 1; **< Whether to support VHT-MCS8. The default value is 0. * + * uint32_t he_reserved:19; **< Reserved for future feature set * */ uint32_t he_bitmask; /* **< Password identifier for H2E. this needs to be null terminated string. SAE_H2E_IDENTIFIER_LEN chars */ ProtobufCBinaryData sae_h2e_identifier; + /* + **< Configuration for SAE-PK (Public Key) Authentication method + */ + uint32_t sae_pk_mode; }; #define WIFI_STA_CONFIG__INIT \ { PROTOBUF_C_MESSAGE_INIT (&wifi_sta_config__descriptor) \ - , {0,NULL}, {0,NULL}, 0, 0, {0,NULL}, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, {0,NULL} } + , {0,NULL}, {0,NULL}, 0, 0, {0,NULL}, 0, 0, 0, NULL, NULL, 0, 0, 0, 0, {0,NULL}, 0 } typedef enum { @@ -3548,6 +3625,25 @@ WifiScanTime * void wifi_scan_time__free_unpacked (WifiScanTime *message, ProtobufCAllocator *allocator); +/* WifiScanChannelBitmap methods */ +void wifi_scan_channel_bitmap__init + (WifiScanChannelBitmap *message); +size_t wifi_scan_channel_bitmap__get_packed_size + (const WifiScanChannelBitmap *message); +size_t wifi_scan_channel_bitmap__pack + (const WifiScanChannelBitmap *message, + uint8_t *out); +size_t wifi_scan_channel_bitmap__pack_to_buffer + (const WifiScanChannelBitmap *message, + ProtobufCBuffer *buffer); +WifiScanChannelBitmap * + wifi_scan_channel_bitmap__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void wifi_scan_channel_bitmap__free_unpacked + (WifiScanChannelBitmap *message, + ProtobufCAllocator *allocator); /* WifiScanConfig methods */ void wifi_scan_config__init (WifiScanConfig *message); @@ -3643,6 +3739,25 @@ WifiPmfConfig * void wifi_pmf_config__free_unpacked (WifiPmfConfig *message, ProtobufCAllocator *allocator); +/* WifiBssMaxIdleConfig methods */ +void wifi_bss_max_idle_config__init + (WifiBssMaxIdleConfig *message); +size_t wifi_bss_max_idle_config__get_packed_size + (const WifiBssMaxIdleConfig *message); +size_t wifi_bss_max_idle_config__pack + (const WifiBssMaxIdleConfig *message, + uint8_t *out); +size_t wifi_bss_max_idle_config__pack_to_buffer + (const WifiBssMaxIdleConfig *message, + ProtobufCBuffer *buffer); +WifiBssMaxIdleConfig * + wifi_bss_max_idle_config__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void wifi_bss_max_idle_config__free_unpacked + (WifiBssMaxIdleConfig *message, + ProtobufCAllocator *allocator); /* WifiApConfig methods */ void wifi_ap_config__init (WifiApConfig *message); @@ -6564,6 +6679,9 @@ typedef void (*WifiActiveScanTime_Closure) typedef void (*WifiScanTime_Closure) (const WifiScanTime *message, void *closure_data); +typedef void (*WifiScanChannelBitmap_Closure) + (const WifiScanChannelBitmap *message, + void *closure_data); typedef void (*WifiScanConfig_Closure) (const WifiScanConfig *message, void *closure_data); @@ -6579,6 +6697,9 @@ typedef void (*WifiScanThreshold_Closure) typedef void (*WifiPmfConfig_Closure) (const WifiPmfConfig *message, void *closure_data); +typedef void (*WifiBssMaxIdleConfig_Closure) + (const WifiBssMaxIdleConfig *message, + void *closure_data); typedef void (*WifiApConfig_Closure) (const WifiApConfig *message, void *closure_data); @@ -7054,11 +7175,13 @@ extern const ProtobufCMessageDescriptor wifi_init_config__descriptor; extern const ProtobufCMessageDescriptor wifi_country__descriptor; extern const ProtobufCMessageDescriptor wifi_active_scan_time__descriptor; extern const ProtobufCMessageDescriptor wifi_scan_time__descriptor; +extern const ProtobufCMessageDescriptor wifi_scan_channel_bitmap__descriptor; extern const ProtobufCMessageDescriptor wifi_scan_config__descriptor; extern const ProtobufCMessageDescriptor wifi_he_ap_info__descriptor; extern const ProtobufCMessageDescriptor wifi_ap_record__descriptor; extern const ProtobufCMessageDescriptor wifi_scan_threshold__descriptor; extern const ProtobufCMessageDescriptor wifi_pmf_config__descriptor; +extern const ProtobufCMessageDescriptor wifi_bss_max_idle_config__descriptor; extern const ProtobufCMessageDescriptor wifi_ap_config__descriptor; extern const ProtobufCMessageDescriptor wifi_sta_config__descriptor; extern const ProtobufCMessageDescriptor wifi_config__descriptor; diff --git a/common/proto/esp_hosted_rpc.proto b/common/proto/esp_hosted_rpc.proto index db37ac49..7500fb71 100644 --- a/common/proto/esp_hosted_rpc.proto +++ b/common/proto/esp_hosted_rpc.proto @@ -605,6 +605,11 @@ message wifi_scan_time { cause station to disconnect from AP and are not recommended. */ } +message wifi_scan_channel_bitmap { + uint32 ghz_2_channels = 1; /**< Represents 2.4 GHz channels, that bits can be set as wifi_2g_channel_bit_t shown. */ + uint32 ghz_5_channels = 2; /**< Represents 5 GHz channels, that bits can be set as wifi_5g_channel_bit_t shown. */ +} + message wifi_scan_config { bytes ssid = 1; /**< SSID of AP 33char*/ bytes bssid = 2; /**< MAC address of AP 6char */ @@ -613,6 +618,9 @@ message wifi_scan_config { int32 scan_type = 5; /**< scan type, active or passive */ wifi_scan_time scan_time = 6; /**< scan time per channel */ uint32 home_chan_dwell_time = 7; /**< time spent at home channel between scanning consecutive channels.*/ + wifi_scan_channel_bitmap channel_bitmap = 8; /**< Channel bitmap for setting specific channels to be scanned. + Please note that the 'channel' parameter above needs to be set to 0 to allow scanning by bitmap. + Also, note that only allowed channels configured by wifi_country_t can be scanned. */ } message wifi_he_ap_info { @@ -661,6 +669,7 @@ message wifi_scan_threshold { Note: Incase this value is not set and password is set as per WPA2 standards(password len >= 8), it will be defaulted to WPA2 and device won't connect to deprecated WEP/WPA networks. Please set authmode threshold as WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK to connect to WEP/WPA networks */ + uint32 rssi_5g_adjustment = 3; /**< The RSSI value of the 5G AP is within the rssi_5g_adjustment range compared to the 2G AP, the 5G AP will be given priority for connection. */ } message wifi_pmf_config { @@ -668,6 +677,11 @@ message wifi_pmf_config { bool required = 2; /**< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */ } +message wifi_bss_max_idle_config { + uint32 period = 1; /**< Sets BSS Max idle period (1 Unit = 1000TUs OR 1.024 Seconds). If there are no frames for this period from a STA, SoftAP will disassociate due to inactivity. Setting it to 0 disables the feature */ + bool protected_keep_alive = 2; /**< Requires clients to use protected keep alive frames for BSS Max Idle period */ +} + message wifi_ap_config { bytes ssid = 1; /**< SSID of ESP32 soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. 32 char*/ bytes password = 2; /**< Password of ESP32 soft-AP. 64 char*/ @@ -683,6 +697,12 @@ message wifi_ap_config { bool ftm_responder = 10; /**< Enable FTM Responder mode */ wifi_pmf_config pmf_cfg = 11; /**< Configuration for Protected Management Frame */ int32 sae_pwe_h2e = 12; /**< Configuration for SAE PWE derivation method */ + uint32 csa_count = 13; /**< Channel Switch Announcement Count. Notify the station that the channel will switch after the csa_count beacon intervals. Default value: 3 */ + uint32 dtim_period = 14; /**< Dtim period of soft-AP. Range: 1 ~ 10. Default value: 1 */ + uint32 transition_disable = 15; /**< Whether to enable transition disable feature */ + uint32 sae_ext = 16; /**< Enable SAE EXT feature. SOC_GCMP_SUPPORT is required for this feature. */ + wifi_bss_max_idle_config bss_max_idle_cfg = 17; /**< Configuration for bss max idle, effective if CONFIG_WIFI_BSS_MAX_IDLE_SUPPORT is enabled */ + uint32 gtk_rekey_interval = 18; /**< GTK rekeying interval in seconds. If set to 0, GTK rekeying is disabled. Range: 60 ~ 65535 including 0. */ } message wifi_sta_config { @@ -720,9 +740,13 @@ Units: AP beacon intervals. Defaults to 3 if set to 0. */ //uint32_t he_trig_su_bmforming_feedback_disabled:1; /**< Whether to disable support the transmission of SU feedback in an HE TB sounding sequence. */ //uint32_t he_trig_mu_bmforming_partial_feedback_disabled:1; /**< Whether to disable support the transmission of partial-bandwidth MU feedback in an HE TB sounding sequence. */ // uint32_t he_trig_cqi_feedback_disabled:1; /**< Whether to disable support the transmission of CQI feedback in an HE TB sounding sequence. */ - // uint32_t he_reserved:22; /**< Reserved for future feature set */ + // uint32_t vht_su_beamformee_disabled: 1; /**< Whether to disable support for operation as an VHT SU beamformee. */ + // uint32_t vht_mu_beamformee_disabled: 1; /**< Whether to disable support for operation as an VHT MU beamformee. */ + // uint32_t vht_mcs8_enabled: 1; /**< Whether to support VHT-MCS8. The default value is 0. */ + // uint32_t he_reserved:19; /**< Reserved for future feature set */ uint32 he_bitmask = 14; bytes sae_h2e_identifier = 15; /**< Password identifier for H2E. this needs to be null terminated string. SAE_H2E_IDENTIFIER_LEN chars */ + uint32 sae_pk_mode = 16; /**< Configuration for SAE-PK (Public Key) Authentication method */ } message wifi_config { diff --git a/host/drivers/rpc/core/rpc_req.c b/host/drivers/rpc/core/rpc_req.c index 800ef0f6..477e3716 100644 --- a/host/drivers/rpc/core/rpc_req.c +++ b/host/drivers/rpc/core/rpc_req.c @@ -8,7 +8,6 @@ #include "esp_hosted_wifi_config.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" -#include "esp_idf_version.h" DEFINE_LOG_TAG(rpc_req); @@ -227,6 +226,9 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) RPC_ALLOC_ELEMENT(WifiScanThreshold, p_c_sta->threshold, wifi_scan_threshold__init); p_c_sta->threshold->rssi = p_a_sta->threshold.rssi; p_c_sta->threshold->authmode = p_a_sta->threshold.authmode; +#if H_PRESENT_IN_ESP_IDF_5_4_0 + p_c_sta->threshold->rssi_5g_adjustment = p_a_sta->threshold.rssi_5g_adjustment; +#endif RPC_ALLOC_ELEMENT(WifiPmfConfig, p_c_sta->pmf_cfg, wifi_pmf_config__init); p_c_sta->pmf_cfg->capable = p_a_sta->pmf_cfg.capable; p_c_sta->pmf_cfg->required = p_a_sta->pmf_cfg.required; @@ -249,17 +251,6 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) if (p_a_sta->transition_disable) H_SET_BIT(WIFI_STA_CONFIG_1_transition_disable, p_c_sta->bitmask); -#if H_WIFI_VHT_FIELDS_AVAILABLE - if (p_a_sta->vht_su_beamformee_disabled) - H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); - - if (p_a_sta->vht_mu_beamformee_disabled) - H_SET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); - - if (p_a_sta->vht_mcs8_enabled) - H_SET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); -#endif - #if H_DECODE_WIFI_RESERVED_FIELD #if H_WIFI_NEW_RESERVED_FIELD_NAMES WIFI_STA_CONFIG_2_SET_RESERVED_VAL(p_a_sta->reserved2, p_c_sta->he_bitmask); @@ -269,6 +260,7 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) #endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; + p_c_sta->sae_pk_mode = p_a_sta->sae_pk_mode; p_c_sta->failure_retry_cnt = p_a_sta->failure_retry_cnt; if (p_a_sta->he_dcm_set) @@ -297,7 +289,7 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) if (p_a_sta->he_trig_cqi_feedback_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); -#if H_WIFI_VHT_FIELDS_AVAILABLE +#if H_PRESENT_IN_ESP_IDF_5_5_0 if (p_a_sta->vht_su_beamformee_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); @@ -333,11 +325,22 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) p_c_ap->ssid_hidden = p_a_ap->ssid_hidden; p_c_ap->max_connection = p_a_ap->max_connection; p_c_ap->beacon_interval = p_a_ap->beacon_interval; + p_c_ap->csa_count = p_a_ap->csa_count; + p_c_ap->dtim_period = p_a_ap->dtim_period; p_c_ap->pairwise_cipher = p_a_ap->pairwise_cipher; p_c_ap->ftm_responder = p_a_ap->ftm_responder; RPC_ALLOC_ELEMENT(WifiPmfConfig, p_c_ap->pmf_cfg, wifi_pmf_config__init); p_c_ap->pmf_cfg->capable = p_a_ap->pmf_cfg.capable; p_c_ap->pmf_cfg->required = p_a_ap->pmf_cfg.required; + p_c_ap->sae_pwe_h2e = p_a_ap->sae_pwe_h2e; + p_c_ap->transition_disable = p_a_ap->transition_disable; +#if H_PRESENT_IN_ESP_IDF_5_5_0 + p_c_ap->sae_ext = p_a_ap->sae_ext; + RPC_ALLOC_ELEMENT(WifiBssMaxIdleConfig, p_c_ap->bss_max_idle_cfg, wifi_bss_max_idle_config__init); + p_c_ap->bss_max_idle_cfg->period = p_a_ap->bss_max_idle_cfg.period; + p_c_ap->bss_max_idle_cfg->protected_keep_alive = p_a_ap->bss_max_idle_cfg.protected_keep_alive; + p_c_ap->gtk_rekey_interval = p_a_ap->gtk_rekey_interval; +#endif break; } default: { ESP_LOGE(TAG, "unexpected wifi iface [%u]\n", p_a->iface); @@ -380,6 +383,10 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) p_c->home_chan_dwell_time = p_a->home_chan_dwell_time; + RPC_ALLOC_ELEMENT(WifiScanChannelBitmap, p_c->channel_bitmap, wifi_scan_channel_bitmap__init); + p_c->channel_bitmap->ghz_2_channels = p_a->channel_bitmap.ghz_2_channels; + p_c->channel_bitmap->ghz_5_channels = p_a->channel_bitmap.ghz_5_channels; + req_payload->config_set = 1; } ESP_LOGI(TAG, "Scan start Req\n"); diff --git a/host/drivers/rpc/core/rpc_rsp.c b/host/drivers/rpc/core/rpc_rsp.c index 809485e1..ea704ec0 100644 --- a/host/drivers/rpc/core/rpc_rsp.c +++ b/host/drivers/rpc/core/rpc_rsp.c @@ -7,7 +7,6 @@ #include "esp_hosted_wifi_config.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" -#include "esp_idf_version.h" #include "esp_hosted_config.h" DEFINE_LOG_TAG(rpc_rsp); @@ -254,13 +253,19 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_sta->channel = p_c_sta->channel; p_a_sta->listen_interval = p_c_sta->listen_interval; p_a_sta->sort_method = p_c_sta->sort_method; - p_a_sta->threshold.rssi = p_c_sta->threshold->rssi; - p_a_sta->threshold.authmode = p_c_sta->threshold->authmode; + if (p_c_sta->threshold) { + p_a_sta->threshold.rssi = p_c_sta->threshold->rssi; + p_a_sta->threshold.authmode = p_c_sta->threshold->authmode; +#if H_PRESENT_IN_ESP_IDF_5_4_0 + p_a_sta->threshold.rssi_5g_adjustment = p_c_sta->threshold->rssi_5g_adjustment; +#endif + } //p_a_sta->ssid_hidden = p_c_sta->ssid_hidden; //p_a_sta->max_connections = p_c_sta->max_connections; - p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; - p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; - + if (p_c_sta->pmf_cfg) { + p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; + p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; + } p_a_sta->rm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); p_a_sta->btm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); p_a_sta->mbo_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); @@ -277,6 +282,7 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) #endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; + p_a_sta->sae_pk_mode = p_c_sta->sae_pk_mode; p_a_sta->failure_retry_cnt = p_c_sta->failure_retry_cnt; p_a_sta->he_dcm_set = H_GET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); @@ -291,7 +297,7 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->bitmask); p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->bitmask); -#if H_WIFI_VHT_FIELDS_AVAILABLE +#if H_PRESENT_IN_ESP_IDF_5_5_0 p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); @@ -319,10 +325,24 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_ap->ssid_hidden = p_c_ap->ssid_hidden; p_a_ap->max_connection = p_c_ap->max_connection; p_a_ap->beacon_interval = p_c_ap->beacon_interval; + p_a_ap->csa_count = p_c_ap->csa_count; + p_a_ap->dtim_period = p_c_ap->dtim_period; p_a_ap->pairwise_cipher = p_c_ap->pairwise_cipher; p_a_ap->ftm_responder = p_c_ap->ftm_responder; - p_a_ap->pmf_cfg.capable = p_c_ap->pmf_cfg->capable; - p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; + if (p_c_ap->pmf_cfg) { + p_a_ap->pmf_cfg.capable = p_c_ap->pmf_cfg->capable; + p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; + } + p_a_ap->sae_pwe_h2e = p_c_ap->sae_pwe_h2e; + p_a_ap->transition_disable = p_c_ap->transition_disable; +#if H_PRESENT_IN_ESP_IDF_5_5_0 + p_a_ap->sae_ext = p_c_ap->sae_ext; + if (p_c_ap->bss_max_idle_cfg) { + p_a_ap->bss_max_idle_cfg.period = p_c_ap->bss_max_idle_cfg->period; + p_a_ap->bss_max_idle_cfg.protected_keep_alive = p_c_ap->bss_max_idle_cfg->protected_keep_alive; + } + p_a_ap->gtk_rekey_interval = p_c_ap->gtk_rekey_interval; +#endif break; } default: diff --git a/host/port/esp/freertos/include/esp_hosted_wifi_config.h b/host/port/esp/freertos/include/esp_hosted_wifi_config.h index 7a93ebd3..514bdf0a 100644 --- a/host/port/esp/freertos/include/esp_hosted_wifi_config.h +++ b/host/port/esp/freertos/include/esp_hosted_wifi_config.h @@ -19,10 +19,16 @@ /* ESP-IDF 5.5.0 breaking change: reserved/he_reserved renamed to reserved1/reserved2 */ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) #define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 - #define H_WIFI_VHT_FIELDS_AVAILABLE 1 + #define H_PRESENT_IN_ESP_IDF_5_5_0 1 #else #define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 - #define H_WIFI_VHT_FIELDS_AVAILABLE 0 + #define H_PRESENT_IN_ESP_IDF_5_5_0 0 +#endif + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) + #define H_PRESENT_IN_ESP_IDF_5_4_0 1 +#else + #define H_PRESENT_IN_ESP_IDF_5_4_0 0 #endif /* User-controllable reserved field decoding - works regardless of IDF version */ diff --git a/idf_component.yml b/idf_component.yml index 66957d29..dbbf52ba 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.10" +version: "2.0.11" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/slave_control.c b/slave/main/slave_control.c index 3f7655e4..33dfd2a8 100644 --- a/slave/main/slave_control.c +++ b/slave/main/slave_control.c @@ -27,14 +27,19 @@ #include "esp_hosted_bitmasks.h" #include "esp_idf_version.h" -/* ESP-IDF 5.5.0: renamed reserved fields to reserved1/reserved2 - * and added VHT beamforming/MCS fields */ +/* ESP-IDF 5.5.0: renamed reserved fields to reserved1/reserved2 */ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) #define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 -#define H_WIFI_VHT_FIELDS_AVAILABLE 1 +#define H_PRESENT_IN_ESP_IDF_5_5_0 1 #else #define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 -#define H_WIFI_VHT_FIELDS_AVAILABLE 0 +#define H_PRESENT_IN_ESP_IDF_5_5_0 0 +#endif + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#define H_PRESENT_IN_ESP_IDF_5_4_0 1 +#else +#define H_PRESENT_IN_ESP_IDF_5_4_0 0 #endif /* Slave-side: Always support reserved field decoding for maximum compatibility @@ -878,13 +883,19 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_sta->channel = p_c_sta->channel; p_a_sta->listen_interval = p_c_sta->listen_interval; p_a_sta->sort_method = p_c_sta->sort_method; - p_a_sta->threshold.rssi = p_c_sta->threshold->rssi; - p_a_sta->threshold.authmode = p_c_sta->threshold->authmode; + if (p_c_sta->threshold) { + p_a_sta->threshold.rssi = p_c_sta->threshold->rssi; + p_a_sta->threshold.authmode = p_c_sta->threshold->authmode; +#if H_PRESENT_IN_ESP_IDF_5_4_0 + p_a_sta->threshold.rssi_5g_adjustment = p_c_sta->threshold->rssi_5g_adjustment; +#endif + } //p_a_sta->ssid_hidden = p_c_sta->ssid_hidden; //p_a_sta->max_connections = p_c_sta->max_connections; - p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; - p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; - + if (p_c_sta->pmf_cfg) { + p_a_sta->pmf_cfg.capable = p_c_sta->pmf_cfg->capable; + p_a_sta->pmf_cfg.required = p_c_sta->pmf_cfg->required; + } p_a_sta->rm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_rm_enabled, p_c_sta->bitmask); p_a_sta->btm_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_btm_enabled, p_c_sta->bitmask); p_a_sta->mbo_enabled = H_GET_BIT(WIFI_STA_CONFIG_1_mbo_enabled, p_c_sta->bitmask); @@ -900,6 +911,7 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) #endif p_a_sta->sae_pwe_h2e = p_c_sta->sae_pwe_h2e; + p_a_sta->sae_pk_mode = p_c_sta->sae_pk_mode; p_a_sta->failure_retry_cnt = p_c_sta->failure_retry_cnt; p_a_sta->he_dcm_set = H_GET_BIT(WIFI_STA_CONFIG_2_he_dcm_set_BIT, p_c_sta->he_bitmask); @@ -914,7 +926,7 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_sta->he_trig_mu_bmforming_partial_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_mu_bmforming_partial_feedback_disabled_BIT, p_c_sta->he_bitmask); p_a_sta->he_trig_cqi_feedback_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); -#if H_WIFI_VHT_FIELDS_AVAILABLE +#if H_PRESENT_IN_ESP_IDF_5_5_0 p_a_sta->vht_su_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mu_beamformee_disabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mu_beamformee_disabled, p_c_sta->he_bitmask); p_a_sta->vht_mcs8_enabled = H_GET_BIT(WIFI_STA_CONFIG_2_vht_mcs8_enabled, p_c_sta->he_bitmask); @@ -948,11 +960,24 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_ap->ssid_hidden = p_c_ap->ssid_hidden; p_a_ap->max_connection = p_c_ap->max_connection; p_a_ap->beacon_interval = p_c_ap->beacon_interval; + p_a_ap->csa_count = p_c_ap->csa_count; + p_a_ap->dtim_period = p_c_ap->dtim_period; p_a_ap->pairwise_cipher = p_c_ap->pairwise_cipher; p_a_ap->ftm_responder = p_c_ap->ftm_responder; - p_a_ap->pmf_cfg.capable = p_c_ap->pmf_cfg->capable; - p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; + if (p_c_ap->pmf_cfg) { + p_a_ap->pmf_cfg.capable = p_c_ap->pmf_cfg->capable; + p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; + } p_a_ap->sae_pwe_h2e = p_c_ap->sae_pwe_h2e; + p_a_ap->transition_disable = p_c_ap->transition_disable; +#if H_PRESENT_IN_ESP_IDF_5_5_0 + p_a_ap->sae_ext = p_c_ap->sae_ext; + if (p_c_ap->bss_max_idle_cfg) { + p_a_ap->bss_max_idle_cfg.period = p_c_ap->bss_max_idle_cfg->period; + p_a_ap->bss_max_idle_cfg.protected_keep_alive = p_c_ap->bss_max_idle_cfg->protected_keep_alive; + } + p_a_ap->gtk_rekey_interval = p_c_ap->gtk_rekey_interval; +#endif } RPC_RET_FAIL_IF(esp_wifi_set_config(req_payload->iface, &cfg)); @@ -999,6 +1024,9 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) RPC_ALLOC_ELEMENT(WifiScanThreshold, p_c_sta->threshold, wifi_scan_threshold__init); p_c_sta->threshold->rssi = p_a_sta->threshold.rssi; p_c_sta->threshold->authmode = p_a_sta->threshold.authmode; +#if H_PRESENT_IN_ESP_IDF_5_4_0 + p_c_sta->threshold->rssi_5g_adjustment = p_a_sta->threshold.rssi_5g_adjustment; +#endif RPC_ALLOC_ELEMENT(WifiPmfConfig, p_c_sta->pmf_cfg, wifi_pmf_config__init); p_c_sta->pmf_cfg->capable = p_a_sta->pmf_cfg.capable; p_c_sta->pmf_cfg->required = p_a_sta->pmf_cfg.required; @@ -1030,6 +1058,7 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) #endif p_c_sta->sae_pwe_h2e = p_a_sta->sae_pwe_h2e; + p_c_sta->sae_pk_mode = p_a_sta->sae_pk_mode; p_c_sta->failure_retry_cnt = p_a_sta->failure_retry_cnt; /* HE field handling */ @@ -1060,7 +1089,7 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) if (p_a_sta->he_trig_cqi_feedback_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_he_trig_cqi_feedback_disabled_BIT, p_c_sta->he_bitmask); -#if H_WIFI_VHT_FIELDS_AVAILABLE +#if H_PRESENT_IN_ESP_IDF_5_5_0 if (p_a_sta->vht_su_beamformee_disabled) H_SET_BIT(WIFI_STA_CONFIG_2_vht_su_beamformee_disabled, p_c_sta->he_bitmask); @@ -1089,19 +1118,29 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) WifiApConfig * p_c_ap = resp_payload->cfg->ap; RPC_RESP_COPY_STR(p_c_ap->password, p_a_ap->password, PASSWORD_LENGTH); p_c_ap->ssid_len = p_a_ap->ssid_len; + if (p_c_ap->ssid_len) + RPC_RESP_COPY_STR(p_c_ap->ssid, p_a_ap->ssid, SSID_LENGTH); p_c_ap->channel = p_a_ap->channel; p_c_ap->authmode = p_a_ap->authmode; p_c_ap->ssid_hidden = p_a_ap->ssid_hidden; p_c_ap->max_connection = p_a_ap->max_connection; p_c_ap->beacon_interval = p_a_ap->beacon_interval; + p_c_ap->csa_count = p_a_ap->csa_count; + p_c_ap->dtim_period = p_a_ap->dtim_period; p_c_ap->pairwise_cipher = p_a_ap->pairwise_cipher; p_c_ap->ftm_responder = p_a_ap->ftm_responder; RPC_ALLOC_ELEMENT(WifiPmfConfig, p_c_ap->pmf_cfg, wifi_pmf_config__init); p_c_ap->pmf_cfg->capable = p_a_ap->pmf_cfg.capable; p_c_ap->pmf_cfg->required = p_a_ap->pmf_cfg.required; - if (p_c_ap->ssid_len) - RPC_RESP_COPY_STR(p_c_ap->ssid, p_a_ap->ssid, SSID_LENGTH); p_c_ap->sae_pwe_h2e = p_a_ap->sae_pwe_h2e; + p_c_ap->transition_disable = p_a_ap->transition_disable; +#if H_PRESENT_IN_ESP_IDF_5_5_0 + p_c_ap->sae_ext = p_a_ap->sae_ext; + RPC_ALLOC_ELEMENT(WifiBssMaxIdleConfig, p_c_ap->bss_max_idle_cfg, wifi_bss_max_idle_config__init); + p_c_ap->bss_max_idle_cfg->period = p_a_ap->bss_max_idle_cfg.period; + p_c_ap->bss_max_idle_cfg->protected_keep_alive = p_a_ap->bss_max_idle_cfg.protected_keep_alive; + p_c_ap->gtk_rekey_interval = p_a_ap->gtk_rekey_interval; +#endif break; } default: @@ -1149,6 +1188,11 @@ static esp_err_t req_wifi_scan_start(Rpc *req, Rpc *resp, void *priv_data) p_a_st->active.max = p_c_st->active->max ; p_a->home_chan_dwell_time = p_c->home_chan_dwell_time; + + if (p_c->channel_bitmap) { + p_a->channel_bitmap.ghz_2_channels = p_c->channel_bitmap->ghz_2_channels; + p_a->channel_bitmap.ghz_5_channels = p_c->channel_bitmap->ghz_5_channels; + } } RPC_RET_FAIL_IF(esp_wifi_scan_start(p_a, req_payload->block)); @@ -1715,7 +1759,7 @@ static esp_err_t req_wifi_sta_get_negotiated_phymode(Rpc *req, Rpc *resp, void * static esp_err_t req_wifi_set_protocols(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiSetProtocols, resp_wifi_set_protocols, RpcReqWifiSetProtocols, req_wifi_set_protocols, rpc__resp__wifi_set_protocols__init); @@ -1742,7 +1786,7 @@ static esp_err_t req_wifi_set_protocols(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_get_protocols(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiGetProtocols, resp_wifi_get_protocols, RpcReqWifiGetProtocols, req_wifi_get_protocols, rpc__resp__wifi_get_protocols__init); @@ -1770,7 +1814,7 @@ static esp_err_t req_wifi_get_protocols(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_set_bandwidths(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiSetBandwidths, resp_wifi_set_bandwidths, RpcReqWifiSetBandwidths, req_wifi_set_bandwidths, rpc__resp__wifi_set_bandwidths__init); @@ -1798,7 +1842,7 @@ static esp_err_t req_wifi_set_bandwidths(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_get_bandwidths(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiGetBandwidths, resp_wifi_get_bandwidths, RpcReqWifiGetBandwidths, req_wifi_get_bandwidths, rpc__resp__wifi_get_bandwidths__init); @@ -1826,7 +1870,7 @@ static esp_err_t req_wifi_get_bandwidths(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_set_band(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiSetBand, resp_wifi_set_band, RpcReqWifiSetBand, req_wifi_set_band, rpc__resp__wifi_set_band__init); @@ -1846,7 +1890,7 @@ static esp_err_t req_wifi_set_band(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_get_band(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE_SIMPLE(RpcRespWifiGetBand, resp_wifi_get_band, RpcReqWifiGetBand, req_wifi_get_band, rpc__resp__wifi_get_band__init); @@ -1866,7 +1910,7 @@ static esp_err_t req_wifi_get_band(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_set_band_mode(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE(RpcRespWifiSetBandMode, resp_wifi_set_bandmode, RpcReqWifiSetBandMode, req_wifi_set_bandmode, rpc__resp__wifi_set_band_mode__init); @@ -1886,7 +1930,7 @@ static esp_err_t req_wifi_set_band_mode(Rpc *req, Rpc *resp, void *priv_data) static esp_err_t req_wifi_get_band_mode(Rpc *req, Rpc *resp, void *priv_data) { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#if H_PRESENT_IN_ESP_IDF_5_4_0 RPC_TEMPLATE_SIMPLE(RpcRespWifiGetBandMode, resp_wifi_get_bandmode, RpcReqWifiGetBandMode, req_wifi_get_bandmode, rpc__resp__wifi_get_band_mode__init); From 55ea395911875d411db7f3ecfa7d63672e6bda39 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Fri, 20 Jun 2025 18:05:37 +0800 Subject: [PATCH 40/44] fix(CI_test_more_versions): Test against more ESP IDF versions - latest - release-v5.5 - release-v5.4, v5.4, v5.4.1 - release-v5.3, v5.3.3, v5.3.2, v5.3.1 - fixed build break caused by missing `transition_disable` in some IDF versions - Closes: https://github.com/espressif/esp-hosted-mcu/issues/62 --- .gitlab-ci.yml | 15 ++++--- host/drivers/rpc/core/rpc_req.c | 2 + host/drivers/rpc/core/rpc_rsp.c | 2 + .../freertos/include/esp_hosted_wifi_config.h | 14 +++++++ idf_component.yml | 2 +- slave/main/slave_control.c | 21 +++------- slave/main/slave_wifi_config.h | 40 +++++++++++++++++++ 7 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 slave/main/slave_wifi_config.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bcd622ea..2ea7bb49 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -65,30 +65,33 @@ before_script: build_idf_v5.3: extends: .build_template - image: espressif/idf:release-v5.3 + image: espressif/idf:${IDF_VER} parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2"] + - IDF_VER: ["v5.3.1", "v5.3.2", "v5.3.3", "release-v5.3"] + IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6"] IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp #IDF_EXAMPLE_PATH: [examples/wifi/iperf] build_idf_v5.4: extends: .build_template - image: espressif/idf:release-v5.4 + image: espressif/idf:${IDF_VER} parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2"] + - IDF_VER: ["v5.4", "v5.4.1", "release-v5.4"] + IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6"] IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp #IDF_EXAMPLE_PATH: [examples/wifi/iperf] build_idf_v5.5: extends: .build_template - image: espressif/idf:release-v5.5 + image: espressif/idf:${IDF_VER} parallel: matrix: - - IDF_TARGET: ["esp32p4", "esp32h2"] + - IDF_VER: ["release-v5.5"] + IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6", "esp32c5", "esp32", "esp32c2", "esp32c3", "esp32s3" ] IDF_EXAMPLE_PATH: [examples/wifi/iperf] diff --git a/host/drivers/rpc/core/rpc_req.c b/host/drivers/rpc/core/rpc_req.c index 477e3716..d4feec99 100644 --- a/host/drivers/rpc/core/rpc_req.c +++ b/host/drivers/rpc/core/rpc_req.c @@ -333,7 +333,9 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) p_c_ap->pmf_cfg->capable = p_a_ap->pmf_cfg.capable; p_c_ap->pmf_cfg->required = p_a_ap->pmf_cfg.required; p_c_ap->sae_pwe_h2e = p_a_ap->sae_pwe_h2e; +#if H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE p_c_ap->transition_disable = p_a_ap->transition_disable; +#endif #if H_PRESENT_IN_ESP_IDF_5_5_0 p_c_ap->sae_ext = p_a_ap->sae_ext; RPC_ALLOC_ELEMENT(WifiBssMaxIdleConfig, p_c_ap->bss_max_idle_cfg, wifi_bss_max_idle_config__init); diff --git a/host/drivers/rpc/core/rpc_rsp.c b/host/drivers/rpc/core/rpc_rsp.c index ea704ec0..b96f565d 100644 --- a/host/drivers/rpc/core/rpc_rsp.c +++ b/host/drivers/rpc/core/rpc_rsp.c @@ -334,7 +334,9 @@ int rpc_parse_rsp(Rpc *rpc_msg, ctrl_cmd_t *app_resp) p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; } p_a_ap->sae_pwe_h2e = p_c_ap->sae_pwe_h2e; +#if H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE p_a_ap->transition_disable = p_c_ap->transition_disable; +#endif #if H_PRESENT_IN_ESP_IDF_5_5_0 p_a_ap->sae_ext = p_c_ap->sae_ext; if (p_c_ap->bss_max_idle_cfg) { diff --git a/host/port/esp/freertos/include/esp_hosted_wifi_config.h b/host/port/esp/freertos/include/esp_hosted_wifi_config.h index 514bdf0a..ad6a6916 100644 --- a/host/port/esp/freertos/include/esp_hosted_wifi_config.h +++ b/host/port/esp/freertos/include/esp_hosted_wifi_config.h @@ -9,6 +9,10 @@ #include "esp_idf_version.h" #include "esp_hosted_config.h" +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 1) +#error ESP-IDF version used is not supported +#endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) /* dual band API support available */ #define H_WIFI_DUALBAND_SUPPORT 1 @@ -38,4 +42,14 @@ #define H_DECODE_WIFI_RESERVED_FIELD 0 #endif +/* wifi_ap_config_t::transition_disable only found in + * IDF v5.3.3 and above, or + * IDF v5.4.1 and above + */ +#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 3)) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 1)) +#define H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE 0 +#else +#define H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE 1 +#endif + #endif /* __ESP_HOSTED_WIFI_CONFIG_H__ */ diff --git a/idf_component.yml b/idf_component.yml index dbbf52ba..069b4522 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.11" +version: "2.0.12" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/slave_control.c b/slave/main/slave_control.c index 33dfd2a8..8019832c 100644 --- a/slave/main/slave_control.c +++ b/slave/main/slave_control.c @@ -25,22 +25,7 @@ #include "esp_hosted_rpc.h" #include "esp_hosted_transport.h" #include "esp_hosted_bitmasks.h" -#include "esp_idf_version.h" - -/* ESP-IDF 5.5.0: renamed reserved fields to reserved1/reserved2 */ -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) -#define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 -#define H_PRESENT_IN_ESP_IDF_5_5_0 1 -#else -#define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 -#define H_PRESENT_IN_ESP_IDF_5_5_0 0 -#endif - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) -#define H_PRESENT_IN_ESP_IDF_5_4_0 1 -#else -#define H_PRESENT_IN_ESP_IDF_5_4_0 0 -#endif +#include "slave_wifi_config.h" /* Slave-side: Always support reserved field decoding for maximum compatibility * The host may or may not have CONFIG_ESP_HOSTED_DECODE_WIFI_RESERVED_FIELD enabled @@ -969,7 +954,9 @@ static esp_err_t req_wifi_set_config(Rpc *req, Rpc *resp, void *priv_data) p_a_ap->pmf_cfg.required = p_c_ap->pmf_cfg->required; } p_a_ap->sae_pwe_h2e = p_c_ap->sae_pwe_h2e; +#if H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE p_a_ap->transition_disable = p_c_ap->transition_disable; +#endif #if H_PRESENT_IN_ESP_IDF_5_5_0 p_a_ap->sae_ext = p_c_ap->sae_ext; if (p_c_ap->bss_max_idle_cfg) { @@ -1133,7 +1120,9 @@ static esp_err_t req_wifi_get_config(Rpc *req, Rpc *resp, void *priv_data) p_c_ap->pmf_cfg->capable = p_a_ap->pmf_cfg.capable; p_c_ap->pmf_cfg->required = p_a_ap->pmf_cfg.required; p_c_ap->sae_pwe_h2e = p_a_ap->sae_pwe_h2e; +#if H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE p_c_ap->transition_disable = p_a_ap->transition_disable; +#endif #if H_PRESENT_IN_ESP_IDF_5_5_0 p_c_ap->sae_ext = p_a_ap->sae_ext; RPC_ALLOC_ELEMENT(WifiBssMaxIdleConfig, p_c_ap->bss_max_idle_cfg, wifi_bss_max_idle_config__init); diff --git a/slave/main/slave_wifi_config.h b/slave/main/slave_wifi_config.h new file mode 100644 index 00000000..7283c098 --- /dev/null +++ b/slave/main/slave_wifi_config.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SLAVE_WIFI_CONFIG_H__ +#define __SLAVE_WIFI_CONFIG_H__ + +#include "esp_idf_version.h" + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 1) +#error ESP-IDF version used is not supported +#endif + +/* ESP-IDF 5.5.0: renamed reserved fields to reserved1/reserved2 */ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) +#define H_WIFI_NEW_RESERVED_FIELD_NAMES 1 +#define H_PRESENT_IN_ESP_IDF_5_5_0 1 +#else +#define H_WIFI_NEW_RESERVED_FIELD_NAMES 0 +#define H_PRESENT_IN_ESP_IDF_5_5_0 0 +#endif + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +#define H_PRESENT_IN_ESP_IDF_5_4_0 1 +#else +#define H_PRESENT_IN_ESP_IDF_5_4_0 0 +#endif + +/* wifi_ap_config_t::transition_disable only found in + * IDF v5.3.3 and above, or + * IDF v5.4.1 and above + */ +#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 3)) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 1)) +#define H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE 0 +#else +#define H_GOT_AP_CONFIG_PARAM_TRANSITION_DISABLE 1 +#endif + +#endif From 0abadf1a61ee5ee61710b4f098da6f60994717ad Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Mon, 23 Jun 2025 17:02:38 +0800 Subject: [PATCH 41/44] fix(create_const_char): use const char for h_thread_create - updated `_h_thread_create_` to use `const char` to remove -Wdiscarded-qualifiers compiler warning --- host/hosted_os_abstraction.h | 2 +- host/port/esp/freertos/src/os_wrapper.c | 2 +- idf_component.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/host/hosted_os_abstraction.h b/host/hosted_os_abstraction.h index 8d2b17a9..dad5e8cb 100644 --- a/host/hosted_os_abstraction.h +++ b/host/hosted_os_abstraction.h @@ -19,7 +19,7 @@ typedef struct { /* 8 */ void (*_h_free_align)(void* ptr); /* Thread */ -/* 11 */ void* (*_h_thread_create)(char *tname, uint32_t tprio, uint32_t tstack_size, void (*start_routine)(void const *), void *sr_arg); +/* 11 */ void* (*_h_thread_create)(const char *tname, uint32_t tprio, uint32_t tstack_size, void (*start_routine)(void const *), void *sr_arg); /* 12 */ int (*_h_thread_cancel)(void *thread_handle); /* Sleeps */ diff --git a/host/port/esp/freertos/src/os_wrapper.c b/host/port/esp/freertos/src/os_wrapper.c index 19b016b9..d574a5a0 100644 --- a/host/port/esp/freertos/src/os_wrapper.c +++ b/host/port/esp/freertos/src/os_wrapper.c @@ -146,7 +146,7 @@ void hosted_init_hook(void) /* -------- Threads ---------- */ -void *hosted_thread_create(char *tname, uint32_t tprio, uint32_t tstack_size, void (*start_routine)(void const *), void *sr_arg) +void *hosted_thread_create(const char *tname, uint32_t tprio, uint32_t tstack_size, void (*start_routine)(void const *), void *sr_arg) { int task_created = RET_OK; diff --git a/idf_component.yml b/idf_component.yml index 069b4522..1d982675 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.12" +version: "2.0.13" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: From ea84a914cbf00a6bb9b5764bc6108994640de4ba Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Mon, 30 Jun 2025 11:58:41 +0800 Subject: [PATCH 42/44] fix(ci_5_4_2): Added v5.4.2 to CI - add ESP-IDF v5.4.2 to CI for coverage - https://github.com/espressif/esp-idf/releases/tag/v5.4.2 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2ea7bb49..b6c1f45f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,7 +79,7 @@ build_idf_v5.4: image: espressif/idf:${IDF_VER} parallel: matrix: - - IDF_VER: ["v5.4", "v5.4.1", "release-v5.4"] + - IDF_VER: ["v5.4", "v5.4.1", "v5.4.2", "release-v5.4"] IDF_TARGET: ["esp32p4", "esp32h2"] IDF_SLAVE_TARGET: ["esp32c6"] IDF_EXAMPLE_PATH: examples/protocols/mqtt/tcp From 2264cfe5498ad48a286f344283d2c55daf7049f7 Mon Sep 17 00:00:00 2001 From: Soh Kam Yung Date: Thu, 10 Jul 2025 18:02:47 +0800 Subject: [PATCH 43/44] bugfix(wifi_init): Updated to add missing wifi init configs - updated protobuf and RPC for missing wifi init configs - checked incoming wifi init config from host - disable cache_tx_buffer in config if slave does not have SPIRAM enabled - bug fix in stats.c for CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS --- common/proto/esp_hosted_rpc.pb-c.c | 58 +++++++++++++- common/proto/esp_hosted_rpc.pb-c.h | 18 ++++- common/proto/esp_hosted_rpc.proto | 4 + host/drivers/rpc/core/rpc_req.c | 5 ++ idf_component.yml | 2 +- slave/main/slave_control.c | 120 +++++++++++++++++++++++------ slave/main/stats.c | 2 +- 7 files changed, 181 insertions(+), 28 deletions(-) diff --git a/common/proto/esp_hosted_rpc.pb-c.c b/common/proto/esp_hosted_rpc.pb-c.c index 6516892e..84b5b4ec 100644 --- a/common/proto/esp_hosted_rpc.pb-c.c +++ b/common/proto/esp_hosted_rpc.pb-c.c @@ -7387,7 +7387,7 @@ void rpc__free_unpacked assert(message->base.descriptor == &rpc__descriptor); protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); } -static const ProtobufCFieldDescriptor wifi_init_config__field_descriptors[20] = +static const ProtobufCFieldDescriptor wifi_init_config__field_descriptors[24] = { { "static_rx_buf_num", @@ -7629,6 +7629,54 @@ static const ProtobufCFieldDescriptor wifi_init_config__field_descriptors[20] = 0, /* flags */ 0,NULL,NULL /* reserved1,reserved2, etc */ }, + { + "rx_mgmt_buf_type", + 21, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WifiInitConfig, rx_mgmt_buf_type), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "rx_mgmt_buf_num", + 22, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WifiInitConfig, rx_mgmt_buf_num), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "tx_hetb_queue_num", + 23, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WifiInitConfig, tx_hetb_queue_num), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "dump_hesigb_enable", + 24, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_INT32, + 0, /* quantifier_offset */ + offsetof(WifiInitConfig, dump_hesigb_enable), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, }; static const unsigned wifi_init_config__field_indices_by_name[] = { 7, /* field[7] = ampdu_rx_enable */ @@ -7637,6 +7685,7 @@ static const unsigned wifi_init_config__field_indices_by_name[] = { 14, /* field[14] = beacon_max_len */ 5, /* field[5] = cache_tx_buf_num */ 6, /* field[6] = csi_enable */ + 23, /* field[23] = dump_hesigb_enable */ 1, /* field[1] = dynamic_rx_buf_num */ 4, /* field[4] = dynamic_tx_buf_num */ 18, /* field[18] = espnow_max_encrypt_num */ @@ -7646,16 +7695,19 @@ static const unsigned wifi_init_config__field_indices_by_name[] = { 11, /* field[11] = nano_enable */ 10, /* field[10] = nvs_enable */ 12, /* field[12] = rx_ba_win */ + 21, /* field[21] = rx_mgmt_buf_num */ + 20, /* field[20] = rx_mgmt_buf_type */ 17, /* field[17] = sta_disconnected_pm */ 0, /* field[0] = static_rx_buf_num */ 3, /* field[3] = static_tx_buf_num */ 2, /* field[2] = tx_buf_type */ + 22, /* field[22] = tx_hetb_queue_num */ 13, /* field[13] = wifi_task_core_id */ }; static const ProtobufCIntRange wifi_init_config__number_ranges[1 + 1] = { { 1, 0 }, - { 0, 20 } + { 0, 24 } }; const ProtobufCMessageDescriptor wifi_init_config__descriptor = { @@ -7665,7 +7717,7 @@ const ProtobufCMessageDescriptor wifi_init_config__descriptor = "WifiInitConfig", "", sizeof(WifiInitConfig), - 20, + 24, wifi_init_config__field_descriptors, wifi_init_config__field_indices_by_name, 1, wifi_init_config__number_ranges, diff --git a/common/proto/esp_hosted_rpc.pb-c.h b/common/proto/esp_hosted_rpc.pb-c.h index 2bfc6a4f..ca2b379c 100644 --- a/common/proto/esp_hosted_rpc.pb-c.h +++ b/common/proto/esp_hosted_rpc.pb-c.h @@ -803,10 +803,26 @@ struct WifiInitConfig **< WiFi init magic number, it should be the last field */ int32_t magic; + /* + **< WiFi RX MGMT buffer type + */ + int32_t rx_mgmt_buf_type; + /* + **< WiFi RX MGMT buffer number + */ + int32_t rx_mgmt_buf_num; + /* + **< WiFi TX HE TB QUEUE number for STA HE TB PPDU transmission + */ + int32_t tx_hetb_queue_num; + /* + **< enable dump sigb field + */ + int32_t dump_hesigb_enable; }; #define WIFI_INIT_CONFIG__INIT \ { PROTOBUF_C_MESSAGE_INIT (&wifi_init_config__descriptor) \ - , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } + , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } struct WifiCountry diff --git a/common/proto/esp_hosted_rpc.proto b/common/proto/esp_hosted_rpc.proto index 7500fb71..0993b495 100644 --- a/common/proto/esp_hosted_rpc.proto +++ b/common/proto/esp_hosted_rpc.proto @@ -581,6 +581,10 @@ message wifi_init_config { bool sta_disconnected_pm = 18; /**< WiFi Power Management for station at disconnected status */ int32 espnow_max_encrypt_num = 19; /**< Maximum encrypt number of peers supported by espnow */ int32 magic = 20; /**< WiFi init magic number, it should be the last field */ + int32 rx_mgmt_buf_type = 21; /**< WiFi RX MGMT buffer type */ + int32 rx_mgmt_buf_num = 22; /**< WiFi RX MGMT buffer number */ + int32 tx_hetb_queue_num = 23; /**< WiFi TX HE TB QUEUE number for STA HE TB PPDU transmission */ + int32 dump_hesigb_enable = 24; /**< enable dump sigb field */ } message wifi_country { diff --git a/host/drivers/rpc/core/rpc_req.c b/host/drivers/rpc/core/rpc_req.c index d4feec99..cfe37cb3 100644 --- a/host/drivers/rpc/core/rpc_req.c +++ b/host/drivers/rpc/core/rpc_req.c @@ -168,6 +168,8 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) req_payload->cfg->tx_buf_type = p_a->tx_buf_type ; req_payload->cfg->static_tx_buf_num = p_a->static_tx_buf_num ; req_payload->cfg->dynamic_tx_buf_num = p_a->dynamic_tx_buf_num ; + req_payload->cfg->rx_mgmt_buf_type = p_a->rx_mgmt_buf_type ; + req_payload->cfg->rx_mgmt_buf_num = p_a->rx_mgmt_buf_num ; req_payload->cfg->cache_tx_buf_num = p_a->cache_tx_buf_num ; req_payload->cfg->csi_enable = p_a->csi_enable ; req_payload->cfg->ampdu_rx_enable = p_a->ampdu_rx_enable ; @@ -178,9 +180,12 @@ int compose_rpc_req(Rpc *req, ctrl_cmd_t *app_req, int32_t *failure_status) req_payload->cfg->rx_ba_win = p_a->rx_ba_win ; req_payload->cfg->wifi_task_core_id = p_a->wifi_task_core_id ; req_payload->cfg->beacon_max_len = p_a->beacon_max_len ; + req_payload->cfg->feature_caps = p_a->feature_caps ; req_payload->cfg->mgmt_sbuf_num = p_a->mgmt_sbuf_num ; req_payload->cfg->sta_disconnected_pm = p_a->sta_disconnected_pm ; req_payload->cfg->espnow_max_encrypt_num = p_a->espnow_max_encrypt_num ; + req_payload->cfg->tx_hetb_queue_num = p_a->tx_hetb_queue_num ; + req_payload->cfg->dump_hesigb_enable = p_a->dump_hesigb_enable ; req_payload->cfg->magic = p_a->magic ; /* uint64 - TODO: portable? */ diff --git a/idf_component.yml b/idf_component.yml index 1d982675..110e48c7 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "2.0.13" +version: "2.0.14" description: ESP-Hosted-MCU provide drivers to act any ESP chipset as Wi-Fi or Bluetooth co-processor. url: https://github.com/espressif/esp-hosted-mcu examples: diff --git a/slave/main/slave_control.c b/slave/main/slave_control.c index 8019832c..cece2533 100644 --- a/slave/main/slave_control.c +++ b/slave/main/slave_control.c @@ -739,6 +739,103 @@ static void event_handler(void* arg, esp_event_base_t event_base, } } +// macros to format output +#define PRINT_HEADER() ESP_LOGI(TAG, " Wifi Init Param | Default | Host | Actual"); +#define PRINT_FOOTER() ESP_LOGI(TAG, " End Wifi Init Param |"); +#define PRINT_USE_HOST_VALUE(param_str, default, host, final) \ + ESP_LOGD(TAG, "% 20s | % 7d | % 7d | % 7d", param_str, default, host, final); +#define PRINT_USE_DEFAULT_VALUE(param_str, default, host, final) \ + ESP_LOGW(TAG, "% 20s | % 7d | % 7d | % 7d", param_str, default, host, final); +#define PRINT_HEX64_USE_HOST_VALUE(param_str, default, host, final) \ + ESP_LOGD(TAG, "% 20s | 0x% 5"PRIx32" | 0x% 5"PRIx64" | 0x% 5"PRIx64, param_str, default, host, final); +#define PRINT_HEX64_USE_DEFAULT_VALUE(param_str, default, host, final) \ + ESP_LOGW(TAG, "% 20s | % 7"PRIx32" | % 7"PRIx64" | % 7"PRIx64, param_str, default, host, final); + +// macros to copy host or default value +#define USE_HOST_VALUE(PARAM_STR, DEFAULT, PARAM) \ + do { \ + dst_config->PARAM = src_config->PARAM; \ + PRINT_USE_HOST_VALUE(PARAM_STR, \ + DEFAULT, \ + src_config->PARAM, \ + dst_config->PARAM); \ + } while(0); + +#define USE_DEFAULT_VALUE(PARAM_STR, DEFAULT, PARAM) \ + do { \ + dst_config->PARAM = DEFAULT; \ + PRINT_USE_DEFAULT_VALUE(PARAM_STR, \ + DEFAULT, \ + src_config->PARAM, \ + dst_config->PARAM); \ + } while(0); + +/** Returns the merged wifi init config + * Compares the src config from the host with our Wi-Fi defaults + * and adjust dst_config as necessary. + * + * Also displays the changed configs. + */ +static wifi_init_config_t * get_merged_init_config(wifi_init_config_t *dst_config, WifiInitConfig *src_config) +{ + /* always use value from host, except for + * - cache_tx_buf_num + * - feature_caps + */ + PRINT_HEADER(); + USE_HOST_VALUE("static_rx_buf", CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM, static_rx_buf_num); + USE_HOST_VALUE("dynamic_rx_buf", CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM, dynamic_rx_buf_num); + USE_HOST_VALUE("tx_buf_type", CONFIG_ESP_WIFI_TX_BUFFER_TYPE, tx_buf_type); + USE_HOST_VALUE("static_tx_buf", WIFI_STATIC_TX_BUFFER_NUM, static_tx_buf_num); + USE_HOST_VALUE("dynamic_tx_buf", WIFI_DYNAMIC_TX_BUFFER_NUM, dynamic_tx_buf_num); + USE_HOST_VALUE("rx_mgmt_buf_type", CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF, rx_mgmt_buf_type); + USE_HOST_VALUE("rx_mgmt_buf", WIFI_RX_MGMT_BUF_NUM_DEF, rx_mgmt_buf_num); + + if (WIFI_ENABLE_CACHE_TX_BUFFER) { + // use setting from host + USE_HOST_VALUE("cache_tx_buf", WIFI_CACHE_TX_BUFFER_NUM, cache_tx_buf_num); + dst_config->feature_caps = src_config->feature_caps; + PRINT_HEX64_USE_HOST_VALUE("feature_caps", WIFI_FEATURE_CAPS, + src_config->feature_caps, + dst_config->feature_caps); + } else { + if (WIFI_FEATURE_CAPS != src_config->feature_caps) { + // don't use host setting, which may have enabled CACHE_TX_BUFFER + USE_DEFAULT_VALUE("cache_tx_buf", WIFI_CACHE_TX_BUFFER_NUM, cache_tx_buf_num); + dst_config->feature_caps = WIFI_FEATURE_CAPS; + PRINT_HEX64_USE_DEFAULT_VALUE("feature_caps", WIFI_FEATURE_CAPS, + src_config->feature_caps, + dst_config->feature_caps); + } else { + USE_HOST_VALUE("cache_tx_buf", WIFI_CACHE_TX_BUFFER_NUM, cache_tx_buf_num); + dst_config->feature_caps = src_config->feature_caps; + PRINT_HEX64_USE_HOST_VALUE("feature_caps", WIFI_FEATURE_CAPS, + src_config->feature_caps, + dst_config->feature_caps); + } + } + + USE_HOST_VALUE("csi_enable", WIFI_CSI_ENABLED, csi_enable); + USE_HOST_VALUE("ampdu_rx_enable", WIFI_AMPDU_RX_ENABLED, ampdu_rx_enable); + USE_HOST_VALUE("ampdu_tx_enable", WIFI_AMPDU_TX_ENABLED, ampdu_tx_enable); + USE_HOST_VALUE("amsdu_tx_enable", WIFI_AMSDU_TX_ENABLED, amsdu_tx_enable); + USE_HOST_VALUE("nvs_enable", WIFI_NVS_ENABLED, nvs_enable); + USE_HOST_VALUE("nano_enable", WIFI_NANO_FORMAT_ENABLED, nano_enable); + USE_HOST_VALUE("rx_ba_win", WIFI_DEFAULT_RX_BA_WIN, rx_ba_win); + USE_HOST_VALUE("wifi_task_core", WIFI_TASK_CORE_ID, wifi_task_core_id); + USE_HOST_VALUE("beacon_max_len", WIFI_SOFTAP_BEACON_MAX_LEN, beacon_max_len); + USE_HOST_VALUE("mgmt_sbuf_num", WIFI_MGMT_SBUF_NUM, mgmt_sbuf_num); + USE_HOST_VALUE("sta_disconnected_pm", WIFI_STA_DISCONNECTED_PM_ENABLED, sta_disconnected_pm); + USE_HOST_VALUE("espnow_max_encrypt",CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM, espnow_max_encrypt_num); + USE_HOST_VALUE("tx_hetb_queue", WIFI_TX_HETB_QUEUE_NUM, tx_hetb_queue_num); + USE_HOST_VALUE("dump_hesigb_enable", WIFI_DUMP_HESIGB_ENABLED, dump_hesigb_enable); + PRINT_FOOTER(); + + dst_config->magic = src_config->magic; + + return dst_config; +} + static esp_err_t req_wifi_init(Rpc *req, Rpc *resp, void *priv_data) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); @@ -748,28 +845,7 @@ static esp_err_t req_wifi_init(Rpc *req, Rpc *resp, void *priv_data) rpc__resp__wifi_init__init); RPC_RET_FAIL_IF(!req_payload->cfg); - cfg.static_rx_buf_num = req_payload->cfg->static_rx_buf_num ; - cfg.dynamic_rx_buf_num = req_payload->cfg->dynamic_rx_buf_num ; - cfg.tx_buf_type = req_payload->cfg->tx_buf_type ; - cfg.static_tx_buf_num = req_payload->cfg->static_tx_buf_num ; - cfg.dynamic_tx_buf_num = req_payload->cfg->dynamic_tx_buf_num ; - cfg.cache_tx_buf_num = req_payload->cfg->cache_tx_buf_num ; - cfg.csi_enable = req_payload->cfg->csi_enable ; - cfg.ampdu_rx_enable = req_payload->cfg->ampdu_rx_enable ; - cfg.ampdu_tx_enable = req_payload->cfg->ampdu_tx_enable ; - cfg.amsdu_tx_enable = req_payload->cfg->amsdu_tx_enable ; - cfg.nvs_enable = req_payload->cfg->nvs_enable ; - cfg.nano_enable = req_payload->cfg->nano_enable ; - cfg.rx_ba_win = req_payload->cfg->rx_ba_win ; - cfg.wifi_task_core_id = req_payload->cfg->wifi_task_core_id ; - cfg.beacon_max_len = req_payload->cfg->beacon_max_len ; - cfg.mgmt_sbuf_num = req_payload->cfg->mgmt_sbuf_num ; - cfg.feature_caps = req_payload->cfg->feature_caps ; - cfg.sta_disconnected_pm = req_payload->cfg->sta_disconnected_pm ; - cfg.espnow_max_encrypt_num = req_payload->cfg->espnow_max_encrypt_num ; - cfg.magic = req_payload->cfg->magic ; - - RPC_RET_FAIL_IF(esp_wifi_init(&cfg)); + RPC_RET_FAIL_IF(esp_wifi_init(get_merged_init_config(&cfg, req_payload->cfg))); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, diff --git a/slave/main/stats.c b/slave/main/stats.c index 260c4db9..aa31062a 100644 --- a/slave/main/stats.c +++ b/slave/main/stats.c @@ -19,7 +19,7 @@ #include "esp_log.h" #include "esp_hosted_transport_init.h" -#if TEST_RAW_TP || ESP_PKT_STATS +#if TEST_RAW_TP || ESP_PKT_STATS || CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS static const char TAG[] = "stats"; #endif From c26ccf6d220757446d5ba8301b6330b1ef3604e9 Mon Sep 17 00:00:00 2001 From: Anton Gerasimov Date: Mon, 14 Jul 2025 12:50:29 +0200 Subject: [PATCH 44/44] Fix processing of received ESP_HCI_IF Decrementing the payload seems to break processing of incoming HCI packets as long as they are well-formed H4 packets with esp_payload_header prepended --- slave/main/slave_bt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/slave/main/slave_bt.c b/slave/main/slave_bt.c index 9896b669..f2787887 100644 --- a/slave/main/slave_bt.c +++ b/slave/main/slave_bt.c @@ -88,13 +88,8 @@ static esp_vhci_host_callback_t vhci_host_cb = { void process_hci_rx_pkt(uint8_t *payload, uint16_t payload_len) { - /* VHCI needs one extra byte at the start of payload */ - /* that is accomodated in esp_payload_header */ ESP_HEXLOGV("bt_rx", payload, payload_len); - payload--; - payload_len++; - if (!esp_vhci_host_check_send_available()) { ESP_LOGD(TAG, "VHCI not available"); }