Browse Source

Merge branch 'feature/add_spi_interface' into 'master'

feature: Add SPI interface for ports

Closes ESF-1

See merge request espressif/esp-serial-flasher!60
Roland Dobai 2 years ago
parent
commit
26794d6c07

+ 2 - 0
.gitlab-ci.yml

@@ -36,6 +36,8 @@ variables:
     - idf.py build -DMD5_ENABLED=0
     - idf.py build -DMD5_ENABLED=0
     - cd $CI_PROJECT_DIR/examples/esp32_load_ram_example
     - cd $CI_PROJECT_DIR/examples/esp32_load_ram_example
     - idf.py build
     - idf.py build
+    - cd $CI_PROJECT_DIR/examples/esp32_spi_load_ram_example
+    - idf.py build
 
 
 build_idf_v4.2:
 build_idf_v4.2:
   extends: .build_idf_template
   extends: .build_idf_template

+ 40 - 7
CMakeLists.txt

@@ -1,17 +1,26 @@
 cmake_minimum_required(VERSION 3.5)
 cmake_minimum_required(VERSION 3.5)
 
 
 set(srcs
 set(srcs
-    src/esp_loader.c
     src/esp_targets.c
     src/esp_targets.c
     src/md5_hash.c
     src/md5_hash.c
-    src/protocol.c
-    src/slip.c
+    src/esp_loader.c
+    src/protocol_common.c
 )
 )
 
 
-
 if (DEFINED ESP_PLATFORM)
 if (DEFINED ESP_PLATFORM)
+    if (${CONFIG_SERIAL_FLASHER_INTERFACE_UART})
+        list(APPEND srcs
+            src/protocol_uart.c
+            src/slip.c
+            port/esp32_port.c
+        )
+    elseif (${CONFIG_SERIAL_FLASHER_INTERFACE_SPI})
+        list(APPEND srcs
+            src/protocol_spi.c
+            port/esp32_spi_port.c
+        )
+    endif()
     # Register component to esp-idf build system
     # Register component to esp-idf build system
-    list(APPEND srcs port/esp32_port.c)
     if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.0")
     if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.0")
         # esp_timer component was introduced in v4.2
         # esp_timer component was introduced in v4.2
         set(priv_requires driver)
         set(priv_requires driver)
@@ -36,6 +45,19 @@ if (DEFINED ESP_PLATFORM)
     endif()
     endif()
 
 
 else()
 else()
+    if (NOT DEFINED SERIAL_FLASHER_INTERFACE_UART AND NOT DEFINED SERIAL_FLASHER_INTERFACE_SPI)
+        set(SERIAL_FLASHER_INTERFACE_UART true)
+    endif()
+
+    if (DEFINED SERIAL_FLASHER_INTERFACE_UART)
+        list(APPEND srcs
+            src/protocol_uart.c
+            src/slip.c
+        )
+    elseif (DEFINED SERIAL_FLASHER_INTERFACE_SPI)
+        list(APPEND srcs src/protocol_spi.c)
+    endif()
+
     # Create traditional CMake target
     # Create traditional CMake target
     add_library(flasher ${srcs})
     add_library(flasher ${srcs})
 
 
@@ -56,8 +78,19 @@ else()
 
 
 endif()
 endif()
 
 
-if(DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)
-    target_compile_definitions(${target} PUBLIC -DMD5_ENABLED=1)
+if (DEFINED SERIAL_FLASHER_INTERFACE_UART OR CONFIG_SERIAL_FLASHER_INTERFACE_UART STREQUAL "y")
+    target_compile_definitions(${target}
+    PUBLIC
+        SERIAL_FLASHER_INTERFACE_UART
+    )
+    if (DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)
+        target_compile_definitions(${target} PUBLIC MD5_ENABLED=1)
+    endif()
+elseif (DEFINED SERIAL_FLASHER_INTERFACE_SPI OR CONFIG_SERIAL_FLASHER_INTERFACE_SPI STREQUAL "y")
+    target_compile_definitions(${target}
+    PUBLIC
+        SERIAL_FLASHER_INTERFACE_SPI
+    )
 endif()
 endif()
 
 
 if(DEFINED CONFIG_SERIAL_FLASHER_RESET_HOLD_TIME_MS AND DEFINED CONFIG_SERIAL_FLASHER_BOOT_HOLD_TIME_MS)
 if(DEFINED CONFIG_SERIAL_FLASHER_RESET_HOLD_TIME_MS AND DEFINED CONFIG_SERIAL_FLASHER_BOOT_HOLD_TIME_MS)

+ 14 - 0
Kconfig

@@ -5,6 +5,20 @@ menu "ESP serial flasher"
         help
         help
             Select this option to enable MD5 hashsum check after flashing.
             Select this option to enable MD5 hashsum check after flashing.
 
 
+    choice SERIAL_FLASHER_INTERFACE
+        prompt "Hardware interface to use for firmware download"
+        default SERIAL_FLASHER_INTERFACE_UART
+        help
+            esp-serial-flasher can work with UART and SPI interfaces.
+
+        config SERIAL_FLASHER_INTERFACE_UART
+            bool "UART"
+
+        config SERIAL_FLASHER_INTERFACE_SPI
+            bool "SPI (Only supports downloading to RAM, experimental)"
+
+    endchoice
+
     config SERIAL_FLASHER_RESET_HOLD_TIME_MS
     config SERIAL_FLASHER_RESET_HOLD_TIME_MS
         int "Time for which the reset pin is asserted when doing a hard reset"
         int "Time for which the reset pin is asserted when doing a hard reset"
         default 100
         default 100

+ 27 - 13
README.md

@@ -21,43 +21,57 @@ Supported **target** microcontrollers:
 - ESP32-C2
 - ESP32-C2
 - ESP32-H2
 - ESP32-H2
 
 
+Supported hardware interfaces:
+- UART
+- SPI (only for RAM download, experimental)
+
 ## Supporting new host target
 ## Supporting new host target
 
 
 In order to support new target, following function has to be implemented by user:
 In order to support new target, following function has to be implemented by user:
 
 
-- loader_port_read()
-- loader_port_write()
-- loader_port_enter_bootloader()
-- loader_port_delay_ms()
-- loader_port_start_timer()
-- loader_port_remaining_time()
+- `loader_port_read()`
+- `loader_port_write()`
+- `loader_port_enter_bootloader()`
+- `loader_port_delay_ms()`
+- `loader_port_start_timer()`
+- `loader_port_remaining_time()`
 
 
 Following functions are part of io.h header for convenience, however, user does not have to strictly follow function signatures, as there are not called directly from library.
 Following functions are part of io.h header for convenience, however, user does not have to strictly follow function signatures, as there are not called directly from library.
 
 
-- loader_port_change_transmission_rate()
-- loader_port_reset_target()
-- loader_port_debug_print()
+- `loader_port_change_transmission_rate()`
+- `loader_port_reset_target()`
+- `loader_port_debug_print()`
+
+For the SPI interface ports
+- `loader_port_spi_set_cs()`
+needs to be implemented as well.
 
 
 Prototypes of all function mentioned above can be found in [io.h](include/io.h).
 Prototypes of all function mentioned above can be found in [io.h](include/io.h).
 Please refer to ports in `port` directory. Currently, ports for [ESP32](port/esp32_port.c), [STM32](port/stm32_port.c), and [Zephyr](port/zephyr_port.c) are available.
 Please refer to ports in `port` directory. Currently, ports for [ESP32](port/esp32_port.c), [STM32](port/stm32_port.c), and [Zephyr](port/zephyr_port.c) are available.
 
 
 ## Configuration
 ## Configuration
 
 
+* `SERIAL_FLASHER_INTERFACE_UART/SERIAL_FLASHER_INTERFACE_SPI`
+
+This defines the hardware interface to use. SPI interface only supports RAM download mode and is in experimental stage and can undergo changes.
+
+Default: SERIAL_FLASHER_INTERFACE_UART
+
 These are the configuration toggles available to the user:
 These are the configuration toggles available to the user:
-* MD5_ENABLED
+* `MD5_ENABLED`
 
 
-If enabled, serial flasher is capable of verifying flash integrity after writing to memory.
+If enabled, serial flasher is capable of verifying flash integrity after writing to flash.
 
 
 Default: Enabled
 Default: Enabled
 > Warning: As ROM bootloader of ESP8266 does not support MD5_CHECK, this option has to be disabled!
 > Warning: As ROM bootloader of ESP8266 does not support MD5_CHECK, this option has to be disabled!
 
 
-* SERIAL_FLASHER_RESET_HOLD_TIME_MS
+* `SERIAL_FLASHER_RESET_HOLD_TIME_MS`
 
 
 This is the time for which the reset pin is asserted when doing a hard reset in milliseconds.
 This is the time for which the reset pin is asserted when doing a hard reset in milliseconds.
 
 
 Default: 100
 Default: 100
 
 
-* SERIAL_FLASHER_BOOT_HOLD_TIME_MS
+* `SERIAL_FLASHER_BOOT_HOLD_TIME_MS`
 
 
 This is the time for which the boot pin is asserted when doing a hard reset in milliseconds.
 This is the time for which the boot pin is asserted when doing a hard reset in milliseconds.
 
 

+ 6 - 4
examples/common/example_common.c

@@ -189,6 +189,7 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
     }
     }
     printf("Connected to target\n");
     printf("Connected to target\n");
 
 
+#ifdef SERIAL_FLASHER_INTERFACE_UART
     if (higher_transmission_rate && esp_loader_get_target() != ESP8266_CHIP) {
     if (higher_transmission_rate && esp_loader_get_target() != ESP8266_CHIP) {
         err = esp_loader_change_transmission_rate(higher_transmission_rate);
         err = esp_loader_change_transmission_rate(higher_transmission_rate);
         if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
         if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
@@ -206,11 +207,12 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
             printf("Transmission rate changed changed\n");
             printf("Transmission rate changed changed\n");
         }
         }
     }
     }
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
 
 
     return ESP_LOADER_SUCCESS;
     return ESP_LOADER_SUCCESS;
 }
 }
 
 
-
+#ifdef SERIAL_FLASHER_INTERFACE_UART
 esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
 esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
 {
 {
     esp_loader_error_t err;
     esp_loader_error_t err;
@@ -263,14 +265,14 @@ esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
 
 
     return ESP_LOADER_SUCCESS;
     return ESP_LOADER_SUCCESS;
 }
 }
-
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
 
 
 esp_loader_error_t load_ram_binary(const uint8_t *bin)
 esp_loader_error_t load_ram_binary(const uint8_t *bin)
 {
 {
     printf("Start loading\n");
     printf("Start loading\n");
     esp_loader_error_t err;
     esp_loader_error_t err;
-    const example_bin_header_t *header = (const example_bin_header_t *)bin;
-    example_bin_segment_t segments[header->segments];
+    const esp_loader_bin_header_t *header = (const esp_loader_bin_header_t *)bin;
+    esp_loader_bin_segment_t segments[header->segments];
 
 
     // Parse segments
     // Parse segments
     uint32_t seg;
     uint32_t seg;

+ 7 - 0
examples/esp32_spi_load_ram_example/CMakeLists.txt

@@ -0,0 +1,7 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS ../../)
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(esp32-spi-ram-loader)

+ 80 - 0
examples/esp32_spi_load_ram_example/README.md

@@ -0,0 +1,80 @@
+# Example of loading the program into RAM through SPI
+
+## Overview
+
+This example demonstrates how to upload an app to RAM of an Espressif MCU with SPI download support from another (host) MCU using the `esp_serial_flash` component API. In this case, another Espressif MCU is used as the host. Binaries to be uploaded to RAM from host MCU to the target MCU can be found in `binaries/RAM_APP` folder and are converted into C-array during build process.
+
+Following steps are performed in order to re-program the target's memory:
+
+1. SPI2 through which the binary will be transfered is initialized.
+2. Host puts slave device into SPI download mode tries to connect by calling `esp_loader_connect()`.
+3. Then `esp_loader_mem_start()` is called for each segment in RAM.
+4. `esp_loader_flash_write()` function is called repeatedly for every segment until the whole binary image is transfered.
+5. `esp_loader_mem_finish()` is called with the binary entrypoint, telling the chip to start the uploaded program.
+6. UART2 is initialized for the connection to the target
+7. Target output is continually read and printed
+
+## Hardware Required
+
+* Two development boards, one with any Espressif MCU (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) and one with an Espressif MCU with SPI download support. Here is a short list of supported MCUs:
+1. ESP32-C3
+2. ESP32-C2
+3. ESP32-S3
+4. ESP32-S2
+5. ESP32-H2
+* One or two USB cables for power supply and programming.
+
+## Hardware connection
+
+Table below shows connection between two Espressif MCUs.
+
+| Host         | Slave         |
+|    IO_5      |    RESET      |
+|    IO_12     |    CLK        |
+|    IO_10     |    CS         |
+|    IO_13     |    MISO       |
+|    IO_11     |    MOSI       |
+|    IO_14     |    QUADWP     |
+|    IO_9      |    QUADHD     |
+|    IO_13     |    STRAP_B0   |
+|    IO_2      |    STRAP_B1   |
+|    IO_3      |    STRAP_B2   |
+|    IO_4      |    STRAP_B3   |
+|    IO_6      |    UART0_RX   |
+|    IO_7      |    UART0_TX   |
+
+> Note 1: Strapping bit pins are documented in the TRM for each respective chip
+
+> Note 2: For achieving highest speeds, check which pins go through the IO MUX bypassing the GPIO matrix and use those. Pins chosen here are IO MUX pins for ESP32S3 and ESP32S2 chips.
+
+## Build and flash
+
+To run the example, type the following command:
+
+```CMake
+idf.py -p PORT flash monitor
+```
+
+(To exit the serial monitor, type ``Ctrl-]``.)
+
+See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
+
+## Example output
+
+Here is the example's console output:
+
+```
+Connected to target
+I (682) spi_ram_loader: Loading app to RAM ...
+Start loading
+Downloading 7840 bytes at 0x3fc96e00...
+Downloading 312 bytes at 0x3fca0020...
+Downloading 93164 bytes at 0x40380000...
+Finished loading
+I (802) spi_ram_loader: ********************************************
+I (802) spi_ram_loader: *** Logs below are print from slave .... ***
+I (812) spi_ram_loader: ********************************************
+Hello world!
+Hello world!
+...
+```

+ 14 - 0
examples/esp32_spi_load_ram_example/main/CMakeLists.txt

@@ -0,0 +1,14 @@
+set(srcs main.c ../../common/example_common.c)
+set(include_dirs . ../../common)
+
+idf_component_register(SRCS ${srcs}
+                       INCLUDE_DIRS ${include_dirs})
+set(target ${COMPONENT_LIB})
+
+# Embed binaries into the app.
+# In ESP-IDF this can also be done using EMBED_FILES option of idf_component_register.
+# Here an external tool is used to make file embedding similar with other ports.
+include(${CMAKE_CURRENT_LIST_DIR}/../../common/bin2array.cmake)
+create_resources(${CMAKE_CURRENT_LIST_DIR}/../../binaries/RAM_APP ${CMAKE_BINARY_DIR}/binaries.c)
+set_property(SOURCE ${CMAKE_BINARY_DIR}/binaries.c PROPERTY GENERATED 1)
+target_sources(${target} PRIVATE ${CMAKE_BINARY_DIR}/binaries.c)

+ 97 - 0
examples/esp32_spi_load_ram_example/main/main.c

@@ -0,0 +1,97 @@
+/* Example of loading the program into RAM through SPI
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <sys/param.h>
+#include <string.h>
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_task_wdt.h"
+#include "driver/spi_master.h"
+#include "driver/uart.h"
+#include "driver/gpio.h"
+#include "esp32_spi_port.h"
+#include "esp_loader.h"
+#include "example_common.h"
+#include "freertos/FreeRTOS.h"
+
+static const char *TAG = "spi_ram_loader";
+
+// Max line size
+#define BUF_LEN 128
+static uint8_t buf[BUF_LEN] = {0};
+
+void slave_monitor(void *arg)
+{
+    // Initialize UART
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity    = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
+        .source_clk = UART_SCLK_DEFAULT,
+#endif
+    };
+
+    ESP_ERROR_CHECK(uart_param_config(UART_NUM_2, &uart_config));
+
+    ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, GPIO_NUM_6, GPIO_NUM_7,
+                                 UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
+
+    ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, BUF_LEN * 4, BUF_LEN * 4, 0, NULL, 0));
+
+    while (1) {
+        int rxBytes = uart_read_bytes(UART_NUM_2, buf, BUF_LEN, 100 / portTICK_PERIOD_MS);
+        buf[rxBytes] = '\0';
+        printf("%s", buf);
+    }
+}
+
+void app_main(void)
+{
+    example_ram_app_binary_t bin;
+
+    const loader_esp32_spi_config_t config = {
+        .spi_bus = SPI2_HOST,
+        .frequency = 20 * 1000000,
+        .reset_trigger_pin = GPIO_NUM_5,
+        .spi_clk_pin = GPIO_NUM_12,
+        .spi_cs_pin = GPIO_NUM_10,
+        .spi_miso_pin = GPIO_NUM_13,
+        .spi_mosi_pin = GPIO_NUM_11,
+        .spi_quadwp_pin = GPIO_NUM_14,
+        .spi_quadhd_pin = GPIO_NUM_9,
+        .strap_bit0_pin = GPIO_NUM_13,
+        .strap_bit1_pin = GPIO_NUM_2,
+        .strap_bit2_pin = GPIO_NUM_3,
+        .strap_bit3_pin = GPIO_NUM_4,
+    };
+
+    if (loader_port_esp32_spi_init(&config) != ESP_LOADER_SUCCESS) {
+        ESP_LOGE(TAG, "SPI initialization failed.");
+        abort();
+    }
+
+    if (connect_to_target(0) == ESP_LOADER_SUCCESS) {
+        get_example_ram_app_binary(esp_loader_get_target(), &bin);
+        ESP_LOGI(TAG, "Loading app to RAM ...");
+        esp_loader_error_t err = load_ram_binary(bin.ram_app.data);
+        if (err == ESP_LOADER_SUCCESS) {
+            // Forward slave's serial output
+            ESP_LOGI(TAG, "********************************************");
+            ESP_LOGI(TAG, "*** Logs below are print from slave .... ***");
+            ESP_LOGI(TAG, "********************************************");
+            xTaskCreate(slave_monitor, "slave_monitor", 2048, NULL, configMAX_PRIORITIES, NULL);
+        } else {
+            ESP_LOGE(TAG, "Loading to RAM failed ...");
+        }
+    }
+    vTaskDelete(NULL);
+}

+ 4 - 0
examples/esp32_spi_load_ram_example/sdkconfig.defaults

@@ -0,0 +1,4 @@
+# This file was generated using idf.py save-defconfig. It can be edited manually.
+# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
+#
+CONFIG_SERIAL_FLASHER_INTERFACE_SPI=y

+ 3 - 0
examples/zephyr_example/CMakeLists.txt

@@ -16,6 +16,9 @@ zephyr_library_include_directories(
   ../binaries
   ../binaries
   )
   )
 
 
+# Needed for example_common
+add_compile_definitions(SERIAL_FLASHER_INTERFACE_UART)
+
 # Embed binaries into the app.
 # Embed binaries into the app.
 include(${CMAKE_CURRENT_LIST_DIR}/../common/bin2array.cmake)
 include(${CMAKE_CURRENT_LIST_DIR}/../common/bin2array.cmake)
 create_resources(${CMAKE_CURRENT_LIST_DIR}/../binaries/Hello-world ${CMAKE_BINARY_DIR}/binaries.c)
 create_resources(${CMAKE_CURRENT_LIST_DIR}/../binaries/Hello-world ${CMAKE_BINARY_DIR}/binaries.c)

+ 1 - 1
idf_component.yml

@@ -1,3 +1,3 @@
-version: "0.0.11"
+version: "0.1.0"
 description: Serial flasher component provides portable library for flashing or loading ram loadble app to Espressif SoCs from other host microcontroller
 description: Serial flasher component provides portable library for flashing or loading ram loadble app to Espressif SoCs from other host microcontroller
 url: https://github.com/espressif/esp-serial-flasher
 url: https://github.com/espressif/esp-serial-flasher

+ 24 - 1
include/esp_loader.h

@@ -68,6 +68,26 @@ typedef enum {
     ESP_UNKNOWN_CHIP = 8
     ESP_UNKNOWN_CHIP = 8
 } target_chip_t;
 } target_chip_t;
 
 
+/**
+ * @brief Application binary header
+ */
+typedef struct {
+  uint8_t magic;
+  uint8_t segments;
+  uint8_t flash_mode;
+  uint8_t flash_size_freq;
+  uint32_t entrypoint;
+} esp_loader_bin_header_t;
+
+/**
+ * @brief Segment binary header
+ */
+typedef struct {
+    uint32_t addr;
+    uint32_t size;
+    uint8_t *data;
+} esp_loader_bin_segment_t;
+
 /**
 /**
  * @brief SPI pin configuration arguments
  * @brief SPI pin configuration arguments
  */
  */
@@ -119,6 +139,8 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args);
   */
   */
 target_chip_t esp_loader_get_target(void);
 target_chip_t esp_loader_get_target(void);
 
 
+
+#ifdef SERIAL_FLASHER_INTERFACE_UART
 /**
 /**
   * @brief Initiates flash operation
   * @brief Initiates flash operation
   *
   *
@@ -165,6 +187,7 @@ esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size);
   *     - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
   *     - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
   */
   */
 esp_loader_error_t esp_loader_flash_finish(bool reboot);
 esp_loader_error_t esp_loader_flash_finish(bool reboot);
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
 
 
 
 
 /**
 /**
@@ -192,7 +215,7 @@ esp_loader_error_t esp_loader_mem_start(uint32_t offset, uint32_t size, uint32_t
   * @param size[in]         Size of data in bytes.
   * @param size[in]         Size of data in bytes.
   *
   *
   * @note  size must not be greater that block_size supplied to previously called
   * @note  size must not be greater that block_size supplied to previously called
-  *        esp_loader_mem_start function. 
+  *        esp_loader_mem_start function.
   *        Therefore, size of data buffer has to be equal or greater than block_size.
   *        Therefore, size of data buffer has to be equal or greater than block_size.
   *
   *
   * @return
   * @return

+ 7 - 0
include/esp_loader_io.h

@@ -100,6 +100,13 @@ void loader_port_reset_target(void);
   */
   */
 void loader_port_debug_print(const char *str);
 void loader_port_debug_print(const char *str);
 
 
+#ifdef SERIAL_FLASHER_INTERFACE_SPI
+/**
+  * @brief Sets the chip select to a defined level
+  */
+void loader_port_spi_set_cs(uint32_t level);
+#endif /* SERIAL_FLASHER_INTERFACE_SPI */
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 298 - 0
port/esp32_spi_port.c

@@ -0,0 +1,298 @@
+/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "esp32_spi_port.h"
+#include "esp_log.h"
+#include "driver/gpio.h"
+#include "esp_timer.h"
+#include "esp_log.h"
+#include "esp_idf_version.h"
+#include <unistd.h>
+
+// #define SERIAL_DEBUG_ENABLE
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
+#define DMA_CHAN SPI_DMA_CH_AUTO
+#else
+#define DMA_CHAN 1
+#endif
+
+#define WORD_ALIGNED(ptr) ((size_t)ptr % sizeof(size_t) == 0)
+
+#ifdef SERIAL_DEBUG_ENABLE
+
+static void dec_to_hex_str(const uint8_t dec, uint8_t hex_str[3])
+{
+    static const uint8_t dec_to_hex[] = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    hex_str[0] = dec_to_hex[dec >> 4];
+    hex_str[1] = dec_to_hex[dec & 0xF];
+    hex_str[2] = '\0';
+}
+
+static void serial_debug_print(const uint8_t *data, uint16_t size, bool write)
+{
+    static bool write_prev = false;
+    uint8_t hex_str[3];
+
+    if(write_prev != write) {
+        write_prev = write;
+        printf("\n--- %s ---\n", write ? "WRITE" : "READ");
+    }
+
+    for(uint32_t i = 0; i < size; i++) {
+        dec_to_hex_str(data[i], hex_str);
+        printf("%s ", hex_str);
+    }
+}
+
+#else
+static void serial_debug_print(const uint8_t *data, uint16_t size, bool write) { }
+#endif
+
+static spi_host_device_t s_spi_bus;
+static spi_bus_config_t s_spi_config;
+static spi_device_handle_t s_device_h;
+static spi_device_interface_config_t s_device_config;
+static int64_t s_time_end;
+static uint32_t s_reset_trigger_pin;
+static uint32_t s_strap_bit0_pin;
+static uint32_t s_strap_bit1_pin;
+static uint32_t s_strap_bit2_pin;
+static uint32_t s_strap_bit3_pin;
+static uint32_t s_spi_cs_pin;
+
+esp_loader_error_t loader_port_esp32_spi_init(const loader_esp32_spi_config_t *config)
+{
+    /* Initialize the global static variables*/
+    s_spi_bus = config->spi_bus;
+    s_reset_trigger_pin = config->reset_trigger_pin;
+    s_strap_bit0_pin = config->strap_bit0_pin;
+    s_strap_bit1_pin = config->strap_bit1_pin;
+    s_strap_bit2_pin = config->strap_bit2_pin;
+    s_strap_bit3_pin = config->strap_bit3_pin;
+    s_spi_cs_pin = config->spi_cs_pin;
+
+    /* Configure and initialize the SPI bus*/
+    s_spi_config.mosi_io_num = config->spi_mosi_pin;
+    s_spi_config.miso_io_num = config->spi_miso_pin;
+    s_spi_config.sclk_io_num = config->spi_clk_pin;
+    s_spi_config.quadwp_io_num = config->spi_quadwp_pin;
+    s_spi_config.quadhd_io_num = config->spi_quadhd_pin;
+    s_spi_config.max_transfer_sz = 4096 * 4;
+
+    if (spi_bus_initialize(s_spi_bus, &s_spi_config, DMA_CHAN) != ESP_OK) {
+        return ESP_LOADER_ERROR_FAIL;
+    }
+
+    /* Configure and add the device */
+    s_device_config.clock_speed_hz = config->frequency;
+    s_device_config.spics_io_num = -1; /* We're using the chip select pin as GPIO as we need to
+                                          chain multiple transactions with CS pulled down */
+    s_device_config.flags = SPI_DEVICE_HALFDUPLEX;
+    s_device_config.queue_size = 16;
+
+    if (spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h) != ESP_OK) {
+        return ESP_LOADER_ERROR_FAIL;
+    }
+
+    /* Initialize the pins except for the strapping ones */
+    gpio_reset_pin(s_reset_trigger_pin);
+    gpio_set_pull_mode(s_reset_trigger_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_reset_trigger_pin, GPIO_MODE_OUTPUT);
+    gpio_set_level(s_reset_trigger_pin, 1);
+
+    gpio_reset_pin(s_spi_cs_pin);
+    gpio_set_pull_mode(s_spi_cs_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_spi_cs_pin, GPIO_MODE_OUTPUT);
+    gpio_set_level(s_spi_cs_pin, 1);
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+void loader_port_esp32_spi_deinit(void)
+{
+    gpio_reset_pin(s_reset_trigger_pin);
+    gpio_reset_pin(s_spi_cs_pin);
+    spi_bus_remove_device(s_device_h);
+    spi_bus_free(s_spi_bus);
+}
+
+
+void loader_port_spi_set_cs(const uint32_t level) {
+    gpio_set_level(s_spi_cs_pin, level);
+}
+
+
+esp_loader_error_t loader_port_write(const uint8_t *data, const uint16_t size, const uint32_t timeout)
+{
+    /* Due to the fact that the SPI driver uses DMA for larger transfers,
+       and the DMA requirements, the buffer must be word aligned */
+    if (data == NULL || !WORD_ALIGNED(data)) {
+        return ESP_LOADER_ERROR_INVALID_PARAM;
+    }
+
+    serial_debug_print(data, size, true);
+
+    spi_transaction_t transaction = {
+        .tx_buffer = data,
+        .rx_buffer = NULL,
+        .length = size * 8U,
+        .rxlength = 0,
+    };
+
+    esp_err_t err = spi_device_transmit(s_device_h, &transaction);
+
+    if (err == ESP_OK) {
+        serial_debug_print(data, size, false);
+        return ESP_LOADER_SUCCESS;
+    } else if (err == ESP_ERR_TIMEOUT) {
+        return ESP_LOADER_ERROR_TIMEOUT;
+    } else {
+        return ESP_LOADER_ERROR_FAIL;
+    }
+}
+
+
+esp_loader_error_t loader_port_read(uint8_t *data, const uint16_t size, const uint32_t timeout)
+{
+    /* Due to the fact that the SPI driver uses DMA for larger transfers,
+       and the DMA requirements, the buffer must be word aligned */
+    if (data == NULL || !WORD_ALIGNED(data)) {
+        return ESP_LOADER_ERROR_INVALID_PARAM;
+    }
+
+    serial_debug_print(data, size, true);
+
+    spi_transaction_t transaction = {
+        .tx_buffer = NULL,
+        .rx_buffer = data,
+        .rxlength = size * 8,
+    };
+
+    esp_err_t err = spi_device_transmit(s_device_h, &transaction);
+
+    if (err == ESP_OK) {
+        serial_debug_print(data, size, false);
+        return ESP_LOADER_SUCCESS;
+    } else if (err == ESP_ERR_TIMEOUT) {
+        return ESP_LOADER_ERROR_TIMEOUT;
+    } else {
+        return ESP_LOADER_ERROR_FAIL;
+    }
+}
+
+
+void loader_port_enter_bootloader(void)
+{
+    /*
+        We have to initialize the GPIO pins for the target strapping pins here,
+        as they may overlap with target SPI pins.
+        For instance in the case of ESP32C3 MISO and strapping bit 0 pins overlap.
+    */
+    spi_bus_remove_device(s_device_h);
+    spi_bus_free(s_spi_bus);
+
+    gpio_reset_pin(s_strap_bit0_pin);
+    gpio_set_pull_mode(s_strap_bit0_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_strap_bit0_pin, GPIO_MODE_OUTPUT);
+
+    gpio_reset_pin(s_strap_bit1_pin);
+    gpio_set_pull_mode(s_strap_bit1_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_strap_bit1_pin, GPIO_MODE_OUTPUT);
+
+    gpio_reset_pin(s_strap_bit2_pin);
+    gpio_set_pull_mode(s_strap_bit2_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_strap_bit2_pin, GPIO_MODE_OUTPUT);
+
+    gpio_reset_pin(s_strap_bit3_pin);
+    gpio_set_pull_mode(s_strap_bit3_pin, GPIO_PULLUP_ONLY);
+    gpio_set_direction(s_strap_bit3_pin, GPIO_MODE_OUTPUT);
+
+    /* Set the strapping pins and perform the reset sequence */
+    gpio_set_level(s_strap_bit0_pin, 1);
+    gpio_set_level(s_strap_bit1_pin, 0);
+    gpio_set_level(s_strap_bit2_pin, 0);
+    gpio_set_level(s_strap_bit3_pin, 0);
+    loader_port_reset_target();
+    loader_port_delay_ms(SERIAL_FLASHER_BOOT_HOLD_TIME_MS);
+    gpio_set_level(s_strap_bit3_pin, 1);
+    gpio_set_level(s_strap_bit0_pin, 0);
+
+    /* Disable the strapping pins so they can be used by the slave later */
+    gpio_reset_pin(s_strap_bit0_pin);
+    gpio_reset_pin(s_strap_bit1_pin);
+    gpio_reset_pin(s_strap_bit2_pin);
+    gpio_reset_pin(s_strap_bit3_pin);
+
+    /* Restore the SPI bus pins */
+    spi_bus_initialize(s_spi_bus, &s_spi_config, DMA_CHAN);
+    spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h);
+}
+
+
+void loader_port_reset_target(void)
+{
+    gpio_set_level(s_reset_trigger_pin, 0);
+    loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS);
+    gpio_set_level(s_reset_trigger_pin, 1);
+}
+
+
+void loader_port_delay_ms(const uint32_t ms)
+{
+    usleep(ms * 1000);
+}
+
+
+void loader_port_start_timer(const uint32_t ms)
+{
+    s_time_end = esp_timer_get_time() + ms * 1000;
+}
+
+
+uint32_t loader_port_remaining_time(void)
+{
+    int64_t remaining = (s_time_end - esp_timer_get_time()) / 1000;
+    return (remaining > 0) ? (uint32_t)remaining : 0;
+}
+
+
+void loader_port_debug_print(const char *str)
+{
+    printf("DEBUG: %s\n", str);
+}
+
+
+esp_loader_error_t loader_port_change_transmission_rate(const uint32_t frequency)
+{
+    if (spi_bus_remove_device(s_device_h) != ESP_OK) {
+        return ESP_LOADER_ERROR_FAIL;
+    }
+
+    uint32_t old_frequency = s_device_config.clock_speed_hz;
+    s_device_config.clock_speed_hz = frequency;
+
+    if (spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h) != ESP_OK) {
+        s_device_config.clock_speed_hz = old_frequency;
+        return ESP_LOADER_ERROR_FAIL;
+    }
+
+    return ESP_LOADER_SUCCESS;
+}

+ 60 - 0
port/esp32_spi_port.h

@@ -0,0 +1,60 @@
+/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #pragma once
+
+#include "esp_loader_io.h"
+#include "driver/spi_master.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+    spi_host_device_t spi_bus;
+    uint32_t frequency;
+    uint32_t spi_clk_pin;
+    uint32_t spi_miso_pin;
+    uint32_t spi_mosi_pin;
+    uint32_t spi_cs_pin;
+    uint32_t spi_quadwp_pin;
+    uint32_t spi_quadhd_pin;
+    uint32_t reset_trigger_pin;
+    uint32_t strap_bit0_pin;
+    uint32_t strap_bit1_pin;
+    uint32_t strap_bit2_pin;
+    uint32_t strap_bit3_pin;
+} loader_esp32_spi_config_t;
+
+/**
+  * @brief Initializes the SPI interface.
+  *
+  * @param config[in] Configuration structure
+  *
+  * @return
+  *     - ESP_LOADER_SUCCESS Success
+  *     - ESP_LOADER_ERROR_FAIL Initialization failure
+  */
+esp_loader_error_t loader_port_esp32_spi_init(const loader_esp32_spi_config_t *config);
+
+/**
+  * @brief Deinitializes the SPI interface.
+  */
+void loader_port_esp32_spi_deinit(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 12 - 8
private_include/protocol.h

@@ -195,12 +195,24 @@ typedef struct __attribute__((packed))
     uint32_t status_mask;
     uint32_t status_mask;
 } write_spi_command_t;
 } write_spi_command_t;
 
 
+esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args);
+
+#ifdef SERIAL_FLASHER_INTERFACE_UART
 esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write, bool encryption);
 esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write, bool encryption);
 
 
 esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size);
 esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size);
 
 
 esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader);
 esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader);
 
 
+esp_loader_error_t loader_sync_cmd(void);
+
+esp_loader_error_t loader_spi_attach_cmd(uint32_t config);
+
+esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out);
+
+esp_loader_error_t loader_spi_parameters(uint32_t total_size);
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
+
 esp_loader_error_t loader_mem_begin_cmd(uint32_t offset, uint32_t size, uint32_t blocks_to_write, uint32_t block_size);
 esp_loader_error_t loader_mem_begin_cmd(uint32_t offset, uint32_t size, uint32_t blocks_to_write, uint32_t block_size);
 
 
 esp_loader_error_t loader_mem_data_cmd(const uint8_t *data, uint32_t size);
 esp_loader_error_t loader_mem_data_cmd(const uint8_t *data, uint32_t size);
@@ -217,16 +229,8 @@ esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value, uint32
 
 
 esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg);
 esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg);
 
 
-esp_loader_error_t loader_sync_cmd(void);
-
-esp_loader_error_t loader_spi_attach_cmd(uint32_t config);
-
 esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate);
 esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate);
 
 
-esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out);
-
-esp_loader_error_t loader_spi_parameters(uint32_t total_size);
-
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 31 - 0
private_include/protocol_prv.h

@@ -0,0 +1,31 @@
+/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include "esp_loader.h"
+#include "protocol.h"
+
+void log_loader_internal_error(error_code_t error);
+
+esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value);
+
+esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size,
+                                      const void *data, size_t data_size);
+
+esp_loader_error_t send_cmd_md5(const void *cmd_data, size_t cmd_size, uint8_t md5_out[MD5_SIZE]);

+ 15 - 24
src/esp_loader.c

@@ -35,15 +35,12 @@
 
 
 static const uint32_t DEFAULT_TIMEOUT = 1000;
 static const uint32_t DEFAULT_TIMEOUT = 1000;
 static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000;       // timeout for most flash operations
 static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000;       // timeout for most flash operations
-static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 10000; // timeout (per megabyte) for erasing a region
 static const uint32_t LOAD_RAM_TIMEOUT_PER_MB = 2000000; // timeout (per megabyte) for erasing a region
 static const uint32_t LOAD_RAM_TIMEOUT_PER_MB = 2000000; // timeout (per megabyte) for erasing a region
-static const uint8_t  PADDING_PATTERN = 0xFF;
 
 
 typedef enum {
 typedef enum {
     SPI_FLASH_READ_ID = 0x9F
     SPI_FLASH_READ_ID = 0x9F
 } spi_flash_cmd_t;
 } spi_flash_cmd_t;
 
 
-static uint32_t s_flash_write_size = 0;
 static const target_registers_t *s_reg = NULL;
 static const target_registers_t *s_reg = NULL;
 static target_chip_t s_target = ESP_UNKNOWN_CHIP;
 static target_chip_t s_target = ESP_UNKNOWN_CHIP;
 
 
@@ -88,27 +85,15 @@ static uint32_t timeout_per_mb(uint32_t size_bytes, uint32_t time_per_mb)
 
 
 esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
 esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
 {
 {
-    uint32_t spi_config;
-    esp_loader_error_t err;
-    int32_t trials = connect_args->trials;
-
     loader_port_enter_bootloader();
     loader_port_enter_bootloader();
 
 
-    do {
-        loader_port_start_timer(connect_args->sync_timeout);
-        err = loader_sync_cmd();
-        if (err == ESP_LOADER_ERROR_TIMEOUT) {
-            if (--trials == 0) {
-                return ESP_LOADER_ERROR_TIMEOUT;
-            }
-            loader_port_delay_ms(100);
-        } else if (err != ESP_LOADER_SUCCESS) {
-            return err;
-        }
-    } while (err != ESP_LOADER_SUCCESS);
+    RETURN_ON_ERROR(loader_initialize_conn(connect_args));
 
 
-    RETURN_ON_ERROR( loader_detect_chip(&s_target, &s_reg) );
+    RETURN_ON_ERROR(loader_detect_chip(&s_target, &s_reg));
 
 
+#ifdef SERIAL_FLASHER_INTERFACE_UART
+    esp_loader_error_t err;
+    uint32_t spi_config;
     if (s_target == ESP8266_CHIP) {
     if (s_target == ESP8266_CHIP) {
         err = loader_flash_begin_cmd(0, 0, 0, 0, s_target);
         err = loader_flash_begin_cmd(0, 0, 0, 0, s_target);
     } else {
     } else {
@@ -116,8 +101,9 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
         loader_port_start_timer(DEFAULT_TIMEOUT);
         loader_port_start_timer(DEFAULT_TIMEOUT);
         err = loader_spi_attach_cmd(spi_config);
         err = loader_spi_attach_cmd(spi_config);
     }
     }
-
     return err;
     return err;
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
+    return ESP_LOADER_SUCCESS;
 }
 }
 
 
 target_chip_t esp_loader_get_target(void)
 target_chip_t esp_loader_get_target(void)
@@ -125,6 +111,9 @@ target_chip_t esp_loader_get_target(void)
     return s_target;
     return s_target;
 }
 }
 
 
+#ifdef SERIAL_FLASHER_INTERFACE_UART
+static uint32_t s_flash_write_size = 0;
+
 static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
 static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
 {
 {
     if (mosi_bits > 0) {
     if (mosi_bits > 0) {
@@ -280,7 +269,8 @@ esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size,
     const uint32_t erase_size = calc_erase_size(esp_loader_get_target(), offset, image_size);
     const uint32_t erase_size = calc_erase_size(esp_loader_get_target(), offset, image_size);
     const uint32_t blocks_to_write = (image_size + block_size - 1) / block_size;
     const uint32_t blocks_to_write = (image_size + block_size - 1) / block_size;
 
 
-    loader_port_start_timer(timeout_per_mb(erase_size, ERASE_REGION_TIMEOUT_PER_MB));
+    const uint32_t erase_region_timeout_per_mb = 10000;
+    loader_port_start_timer(timeout_per_mb(erase_size, erase_region_timeout_per_mb));
     return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write, encryption_in_cmd);
     return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write, encryption_in_cmd);
 }
 }
 
 
@@ -295,8 +285,9 @@ esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size)
         return ESP_LOADER_ERROR_INVALID_PARAM;
         return ESP_LOADER_ERROR_INVALID_PARAM;
     }
     }
 
 
+    const uint8_t padding_pattern = 0xFF;
     while (padding_bytes--) {
     while (padding_bytes--) {
-        data[padding_index++] = PADDING_PATTERN;
+        data[padding_index++] = padding_pattern;
     }
     }
 
 
     md5_update(payload, (size + 3) & ~3);
     md5_update(payload, (size + 3) & ~3);
@@ -313,7 +304,7 @@ esp_loader_error_t esp_loader_flash_finish(bool reboot)
 
 
     return loader_flash_end_cmd(!reboot);
     return loader_flash_end_cmd(!reboot);
 }
 }
-
+#endif /* SERIAL_FLASHER_INTERFACE_UART */
 
 
 esp_loader_error_t esp_loader_mem_start(uint32_t offset, uint32_t size, uint32_t block_size)
 esp_loader_error_t esp_loader_mem_start(uint32_t offset, uint32_t size, uint32_t block_size)
 {
 {

+ 2 - 75
src/protocol.c → src/protocol_common.c

@@ -14,8 +14,8 @@
  */
  */
 
 
 #include "protocol.h"
 #include "protocol.h"
+#include "protocol_prv.h"
 #include "esp_loader_io.h"
 #include "esp_loader_io.h"
-#include "slip.h"
 #include <stddef.h>
 #include <stddef.h>
 #include <string.h>
 #include <string.h>
 
 
@@ -23,8 +23,6 @@
 
 
 static uint32_t s_sequence_number = 0;
 static uint32_t s_sequence_number = 0;
 
 
-static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size);
-
 static uint8_t compute_checksum(const uint8_t *data, uint32_t size)
 static uint8_t compute_checksum(const uint8_t *data, uint32_t size)
 {
 {
     uint8_t checksum = 0xEF;
     uint8_t checksum = 0xEF;
@@ -36,52 +34,7 @@ static uint8_t compute_checksum(const uint8_t *data, uint32_t size)
     return checksum;
     return checksum;
 }
 }
 
 
-static esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value)
-{
-    response_t response;
-    command_t command = ((const command_common_t *)cmd_data)->command;
-
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, size) );
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-
-    return check_response(command, reg_value, &response, sizeof(response));
-}
-
-
-static esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size,
-                                             const void *data, size_t data_size)
-{
-    response_t response;
-    command_t command = ((const command_common_t *)cmd_data)->command;
-
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) );
-    RETURN_ON_ERROR( SLIP_send(data, data_size) );
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-
-    return check_response(command, NULL, &response, sizeof(response));
-}
-
-
-static esp_loader_error_t send_cmd_md5(const void *cmd_data, size_t cmd_size, uint8_t md5_out[MD5_SIZE])
-{
-    rom_md5_response_t response;
-    command_t command = ((const command_common_t *)cmd_data)->command;
-
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) );
-    RETURN_ON_ERROR( SLIP_send_delimiter() );
-
-    RETURN_ON_ERROR( check_response(command, NULL, &response, sizeof(response)) );
-
-    memcpy(md5_out, response.md5, MD5_SIZE);
-
-    return ESP_LOADER_SUCCESS;
-}
-
-
-static void log_loader_internal_error(error_code_t error)
+void log_loader_internal_error(error_code_t error)
 {
 {
     loader_port_debug_print("Error: ");
     loader_port_debug_print("Error: ");
 
 
@@ -100,32 +53,6 @@ static void log_loader_internal_error(error_code_t error)
 }
 }
 
 
 
 
-static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size)
-{
-    esp_loader_error_t err;
-    common_response_t *response = (common_response_t *)resp;
-
-    do {
-        err = SLIP_receive_packet(resp, resp_size);
-        if (err != ESP_LOADER_SUCCESS) {
-            return err;
-        }
-    } while ((response->direction != READ_DIRECTION) || (response->command != cmd));
-
-    response_status_t *status = (response_status_t *)((uint8_t *)resp + resp_size - sizeof(response_status_t));
-
-    if (status->failed) {
-        log_loader_internal_error(status->error);
-        return ESP_LOADER_ERROR_INVALID_RESPONSE;
-    }
-
-    if (reg_value != NULL) {
-        *reg_value = response->value;
-    }
-
-    return ESP_LOADER_SUCCESS;
-}
-
 esp_loader_error_t loader_flash_begin_cmd(uint32_t offset,
 esp_loader_error_t loader_flash_begin_cmd(uint32_t offset,
                                           uint32_t erase_size,
                                           uint32_t erase_size,
                                           uint32_t block_size,
                                           uint32_t block_size,

+ 312 - 0
src/protocol_spi.c

@@ -0,0 +1,312 @@
+/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "protocol.h"
+#include "protocol_prv.h"
+#include "esp_loader_io.h"
+#include <stddef.h>
+#include <assert.h>
+
+typedef struct __attribute__((packed)) {
+    uint8_t cmd;
+    uint8_t addr;
+    uint8_t dummy;
+} transaction_preamble_t;
+
+typedef enum {
+    TRANS_CMD_WRBUF = 0x01,
+    TRANS_CMD_RDBUF = 0x02,
+    TRANS_CMD_WRDMA = 0x03,
+    TRANS_CMD_RDDMA = 0x04,
+    TRANS_CMD_SEG_DONE = 0x05,
+    TRANS_CMD_ENQPI = 0x06,
+    TRANS_CMD_WR_DONE = 0x07,
+    TRANS_CMD_CMD8 = 0x08,
+    TRANS_CMD_CMD9 = 0x09,
+    TRANS_CMD_CMDA = 0x0A,
+    TRANS_CMD_EXQPI = 0xDD,
+} transaction_cmd_t;
+
+/* Slave protocol registers */
+typedef enum {
+    SLAVE_REGISTER_VER = 0,
+    SLAVE_REGISTER_RXSTA = 4,
+    SLAVE_REGISTER_TXSTA = 8,
+    SLAVE_REGISTER_CMD = 12,
+} slave_register_addr_t;
+
+#define SLAVE_STA_TOGGLE_BIT (0x01U << 0)
+#define SLAVE_STA_INIT_BIT (0x01U << 1)
+#define SLAVE_STA_BUF_LENGTH_POS 2U
+
+typedef enum {
+    SLAVE_STATE_INIT = SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT,
+    SLAVE_STATE_FIRST_PACKET = SLAVE_STA_INIT_BIT,
+} slave_state_t;
+
+typedef enum {
+    /* Target to host */
+    SLAVE_CMD_IDLE = 0xAA,
+    SLAVE_CMD_READY = 0xA5,
+    /* Host to target */
+    SLAVE_CMD_REBOOT = 0xFE,
+    SLAVE_CMD_COMM_REINIT = 0x5A,
+    SLAVE_CMD_DONE = 0x55,
+} slave_cmd_t;
+
+static uint8_t s_slave_seq_tx;
+static uint8_t s_slave_seq_rx;
+
+static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr,
+                                                 const uint8_t size);
+static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr,
+                                                const uint8_t size);
+static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state,
+                                             bool *slave_ready, uint32_t *buf_size);
+static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value);
+
+esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args)
+{
+    for (uint8_t trial = 0; trial < connect_args->trials; trial++) {
+        uint8_t slave_ready_flag;
+        RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD,
+                                              sizeof(slave_ready_flag)));
+
+        if (slave_ready_flag != SLAVE_CMD_IDLE) {
+            loader_port_debug_print("Waiting for Slave to be idle...\n");
+            loader_port_delay_ms(100);
+        } else {
+            break;
+        }
+    }
+
+    const uint8_t reg_val = SLAVE_CMD_READY;
+    RETURN_ON_ERROR(write_slave_reg(&reg_val, SLAVE_REGISTER_CMD, sizeof(reg_val)));
+
+    for (uint8_t trial = 0; trial < connect_args->trials; trial++) {
+        uint8_t slave_ready_flag;
+        RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD,
+                                              sizeof(slave_ready_flag)));
+
+        if (slave_ready_flag != SLAVE_CMD_READY) {
+            loader_port_debug_print("Waiting for Slave to be ready...\n");
+            loader_port_delay_ms(100);
+        } else {
+            break;
+        }
+    }
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value)
+{
+    command_t command = ((const command_common_t *)cmd_data)->command;
+
+    uint32_t buf_size;
+    bool slave_ready = false;
+    while (!slave_ready) {
+        RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready,
+                                           &buf_size));
+    }
+
+    if (size > buf_size) {
+        return ESP_LOADER_ERROR_INVALID_PARAM;
+    }
+
+    /* Start and write the command */
+    transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA};
+
+    loader_port_spi_set_cs(0);
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, size,
+                                      loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    /* Terminate the write */
+    loader_port_spi_set_cs(0);
+    preamble.cmd = TRANS_CMD_WR_DONE;
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    return check_response(command, reg_value);
+}
+
+
+esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size,
+                                      const void *data, size_t data_size)
+{
+    uint32_t buf_size;
+    bool slave_ready = false;
+    while (!slave_ready) {
+        RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready,
+                                           &buf_size));
+    }
+
+    if (cmd_size + data_size > buf_size) {
+        return ESP_LOADER_ERROR_INVALID_PARAM;
+    }
+
+    /* Start and write the command and the data */
+    transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA};
+
+    loader_port_spi_set_cs(0);
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, cmd_size,
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)data, data_size,
+                                      loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    /* Terminate the write */
+    loader_port_spi_set_cs(0);
+    preamble.cmd = TRANS_CMD_WR_DONE;
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    command_t command = ((const command_common_t *)cmd_data)->command;
+    return check_response(command, NULL);
+}
+
+
+static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr,
+                                         const uint8_t size)
+{
+    transaction_preamble_t preamble = {
+        .cmd = TRANS_CMD_RDBUF,
+        .addr = addr,
+    };
+
+    loader_port_spi_set_cs(0);
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_read(out_data, size, loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr,
+                                          const uint8_t size)
+{
+    transaction_preamble_t preamble = {
+        .cmd = TRANS_CMD_WRBUF,
+        .addr = addr,
+    };
+
+    loader_port_spi_set_cs(0);
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_write(data, size, loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state,
+                                             bool *slave_ready, uint32_t *buf_size)
+{
+    uint32_t status_reg;
+    RETURN_ON_ERROR(read_slave_reg((uint8_t *)&status_reg, status_reg_addr,
+                                   sizeof(status_reg)));
+    const slave_state_t state = status_reg & (SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT);
+
+    switch(state) {
+        case SLAVE_STATE_INIT: {
+            const uint32_t initial = 0U;
+            RETURN_ON_ERROR(write_slave_reg((uint8_t *)&initial, status_reg_addr, sizeof(initial)));
+            break;
+        }
+
+        case SLAVE_STATE_FIRST_PACKET: {
+            *seq_state = state & SLAVE_STA_TOGGLE_BIT;
+            *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS;
+            *slave_ready = true;
+            break;
+        }
+
+        default: {
+            const uint8_t new_seq = state & SLAVE_STA_TOGGLE_BIT;
+            if (new_seq != *seq_state) {
+                *seq_state = new_seq;
+                *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS;
+                *slave_ready = true;
+            }
+            break;
+        }
+    }
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value)
+{
+    response_t resp;
+
+    uint32_t buf_size;
+    bool slave_ready = false;
+    while (!slave_ready) {
+        RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_TXSTA, &s_slave_seq_tx, &slave_ready,
+                                           &buf_size));
+    }
+
+    if (sizeof(resp) > buf_size) {
+        return ESP_LOADER_ERROR_INVALID_PARAM;
+    }
+
+    transaction_preamble_t preamble = {
+        .cmd = TRANS_CMD_RDDMA,
+    };
+
+    loader_port_spi_set_cs(0);
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    RETURN_ON_ERROR(loader_port_read((uint8_t *)&resp, sizeof(resp),
+                                     loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    /* Terminate the read */
+    loader_port_spi_set_cs(0);
+    preamble.cmd = TRANS_CMD_CMD8;
+    RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
+                                      loader_port_remaining_time()));
+    loader_port_spi_set_cs(1);
+
+    common_response_t *common = (common_response_t *)&resp;
+    if ((common->direction != READ_DIRECTION) || (common->command != cmd)) {
+        return ESP_LOADER_ERROR_INVALID_RESPONSE;
+    }
+
+    response_status_t *status =
+        (response_status_t *)((uint8_t *)&resp + sizeof(resp) - sizeof(response_status_t));
+    if (status->failed) {
+        log_loader_internal_error(status->error);
+        return ESP_LOADER_ERROR_INVALID_RESPONSE;
+    }
+
+    if (reg_value != NULL) {
+        *reg_value = common->value;
+    }
+
+    return ESP_LOADER_SUCCESS;
+}

+ 114 - 0
src/protocol_uart.c

@@ -0,0 +1,114 @@
+/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "protocol.h"
+#include "protocol_prv.h"
+#include "esp_loader_io.h"
+#include "slip.h"
+#include <stddef.h>
+#include <string.h>
+
+static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size);
+
+esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args) {
+    esp_loader_error_t err;
+    int32_t trials = connect_args->trials;
+
+    do {
+        loader_port_start_timer(connect_args->sync_timeout);
+        err = loader_sync_cmd();
+        if (err == ESP_LOADER_ERROR_TIMEOUT) {
+            if (--trials == 0) {
+                return ESP_LOADER_ERROR_TIMEOUT;
+            }
+            loader_port_delay_ms(100);
+        } else if (err != ESP_LOADER_SUCCESS) {
+            return err;
+        }
+    } while (err != ESP_LOADER_SUCCESS);
+
+    return err;
+}
+
+esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value)
+{
+    response_t response;
+    command_t command = ((const command_common_t *)cmd_data)->command;
+
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, size) );
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+
+    return check_response(command, reg_value, &response, sizeof(response));
+}
+
+
+esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size,
+                                      const void *data, size_t data_size)
+{
+    response_t response;
+    command_t command = ((const command_common_t *)cmd_data)->command;
+
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) );
+    RETURN_ON_ERROR( SLIP_send(data, data_size) );
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+
+    return check_response(command, NULL, &response, sizeof(response));
+}
+
+
+esp_loader_error_t send_cmd_md5(const void *cmd_data, size_t cmd_size, uint8_t md5_out[MD5_SIZE])
+{
+    rom_md5_response_t response;
+    command_t command = ((const command_common_t *)cmd_data)->command;
+
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+    RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) );
+    RETURN_ON_ERROR( SLIP_send_delimiter() );
+
+    RETURN_ON_ERROR( check_response(command, NULL, &response, sizeof(response)) );
+
+    memcpy(md5_out, response.md5, MD5_SIZE);
+
+    return ESP_LOADER_SUCCESS;
+}
+
+
+static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size)
+{
+    esp_loader_error_t err;
+    common_response_t *response = (common_response_t *)resp;
+
+    do {
+        err = SLIP_receive_packet(resp, resp_size);
+        if (err != ESP_LOADER_SUCCESS) {
+            return err;
+        }
+    } while ((response->direction != READ_DIRECTION) || (response->command != cmd));
+
+    response_status_t *status = (response_status_t *)((uint8_t *)resp + resp_size - sizeof(response_status_t));
+
+    if (status->failed) {
+        log_loader_internal_error(status->error);
+        return ESP_LOADER_ERROR_INVALID_RESPONSE;
+    }
+
+    if (reg_value != NULL) {
+        *reg_value = response->value;
+    }
+
+    return ESP_LOADER_SUCCESS;
+}

+ 3 - 2
test/CMakeLists.txt

@@ -6,7 +6,8 @@ add_executable( ${PROJECT_NAME}
 	../src/esp_loader.c
 	../src/esp_loader.c
 	../src/esp_targets.c
 	../src/esp_targets.c
 	../src/md5_hash.c
 	../src/md5_hash.c
-	../src/protocol.c
+	../src/protocol_common.c
+	../src/protocol_uart.c
 	../src/slip.c)
 	../src/slip.c)
 
 
 target_include_directories(${PROJECT_NAME} PRIVATE ../include ../private_include ../test ../port)
 target_include_directories(${PROJECT_NAME} PRIVATE ../include ../private_include ../test ../port)
@@ -21,4 +22,4 @@ else()
     target_sources(${PROJECT_NAME} PRIVATE serial_io_mock.cpp test.cpp)
     target_sources(${PROJECT_NAME} PRIVATE serial_io_mock.cpp test.cpp)
 endif()
 endif()
 
 
-target_compile_definitions(${PROJECT_NAME} PRIVATE -DMD5_ENABLED=1)
+target_compile_definitions(${PROJECT_NAME} PRIVATE MD5_ENABLED=1 SERIAL_FLASHER_INTERFACE_UART)

+ 4 - 1
zephyr/CMakeLists.txt

@@ -13,12 +13,15 @@ if (CONFIG_ESP_SERIAL_FLASHER)
 
 
     zephyr_library_sources(${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_loader.c
     zephyr_library_sources(${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_loader.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_targets.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_targets.c
-                ${ZEPHYR_CURRENT_MODULE_DIR}/src/protocol.c
+                ${ZEPHYR_CURRENT_MODULE_DIR}/src/protocol_common.c
+                ${ZEPHYR_CURRENT_MODULE_DIR}/src/protocol_uart.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/slip.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/slip.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/md5_hash.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/src/md5_hash.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/port/zephyr_port.c
                 ${ZEPHYR_CURRENT_MODULE_DIR}/port/zephyr_port.c
     )
     )
 
 
+    target_compile_definitions(esp_flasher INTERFACE SERIAL_FLASHER_INTERFACE_UART)
+
     zephyr_library_link_libraries(esp_flasher)
     zephyr_library_link_libraries(esp_flasher)
 
 
     if(DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)
     if(DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)