add some code
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#include "esp_check.h"
|
||||
#include "sscma_client_flasher.h"
|
||||
#include "sscma_client_flasher_interface.h"
|
||||
|
||||
static const char *TAG = "sscma_client.flasher";
|
||||
|
||||
esp_err_t sscma_client_flasher_start(sscma_client_flasher_handle_t handle, size_t offset)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(handle->start, ESP_ERR_NOT_SUPPORTED, TAG, "not supported");
|
||||
return handle->start(handle, offset);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_write(sscma_client_flasher_handle_t handle, const void *data, size_t len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(handle->write, ESP_ERR_NOT_SUPPORTED, TAG, "not supported");
|
||||
return handle->write(handle, data, len);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_finish(sscma_client_flasher_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(handle->finish, ESP_ERR_NOT_SUPPORTED, TAG, "not supported");
|
||||
return handle->finish(handle);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_abort(sscma_client_flasher_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(handle->abort, ESP_ERR_NOT_SUPPORTED, TAG, "not supported");
|
||||
return handle->abort(handle);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_delete(sscma_client_flasher_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(handle->del, ESP_ERR_NOT_SUPPORTED, TAG, "not supported");
|
||||
return handle->del(handle);
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "sscma_client_commands.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "sscma_client_io_interface.h"
|
||||
#include "sscma_client_flasher.h"
|
||||
#include "sscma_client_flasher_interface.h"
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
static const char *TAG = "sscma_client.flasher.we2.spi";
|
||||
|
||||
typedef enum {
|
||||
REG_OFF = 0x00,
|
||||
REG_BYPASS_CACHE = 0x00, // removeds
|
||||
REG_ISP_WRITE_EN = 0x01,
|
||||
REG_XIP_EN = 0x02,
|
||||
REG_ISP_TEST_MODE = 0x04,
|
||||
REG_D8_ISP_EN = 0x01,
|
||||
REG_D8_TEST_MODE = 0x02,
|
||||
REG_D8_SPI_DO_EN = 0x04,
|
||||
} FlashSubMod_t;
|
||||
|
||||
#define OTA_MAX_OFFSET (0x1000000) // 16 MB
|
||||
|
||||
#define OTA_BASE_ADDR (0x38000000)
|
||||
#define OTA_START_OFFSET (0x400000)
|
||||
|
||||
#define OTA_CONTROL_ADDR (0x51010000)
|
||||
#define OTA_PPDONE_COUNT_ADDR (OTA_CONTROL_ADDR + 0x40)
|
||||
#define OTA_CRC_WRITTEN_ADDR (OTA_CONTROL_ADDR + 0x04)
|
||||
#define OTA_CRC_READ_ADDR (OTA_CONTROL_ADDR + 0x08)
|
||||
#define OTA_CRC_CLEAR_ADDR (OTA_CONTROL_ADDR + 0x30)
|
||||
|
||||
#define OTA_CONTROL_REG_DEFAULT (0x028C208B)
|
||||
|
||||
#define OTA_CHUNKED_SIZE (256)
|
||||
|
||||
#define OTA_CMD_WRITE (0xF2)
|
||||
#define OTA_CMD_READ (0xF3)
|
||||
#define OTA_ENABLE_REG (0xD8)
|
||||
#define OTA_STATUS_REG (0x0C)
|
||||
#define OTA_BURST_MODE_REG (0x0D)
|
||||
#define OTA_BURST_ENABLE_REG (0x13)
|
||||
#define OTA_BURST_WRITE_REG (0x00)
|
||||
#define OTA_PP_STATUS_REG (0x1D)
|
||||
|
||||
static esp_err_t sscma_client_flasher_we2_start(sscma_client_flasher_handle_t flasher, size_t offset);
|
||||
static esp_err_t sscma_client_flasher_we2_write(sscma_client_flasher_handle_t flasher, const void *data, size_t len);
|
||||
static esp_err_t sscma_client_flasher_we2_finish(sscma_client_flasher_handle_t flasher);
|
||||
static esp_err_t sscma_client_flasher_we2_abort(sscma_client_flasher_handle_t flasher);
|
||||
static esp_err_t sscma_client_flasher_we2_del(sscma_client_flasher_handle_t flasher);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sscma_client_flasher_t base; /*!< The base class. */
|
||||
sscma_client_io_t *io; /*!< The IO interface. */
|
||||
esp_io_expander_handle_t io_expander; /* !< IO expander handle */
|
||||
int reset_gpio_num; /*!< The reset GPIO number. */
|
||||
bool reset_level; /*!< The reset GPIO level. */
|
||||
size_t offset; /*!< The offset. */
|
||||
uint32_t count; /*!< The Page Program Count. */
|
||||
void *user_ctx; /* !< User context */
|
||||
uint8_t data[OTA_CHUNKED_SIZE + 6]; /* !< The data buffer. */
|
||||
SemaphoreHandle_t lock; /*!< The lock. */
|
||||
} sscma_client_flasher_we2_spi_t;
|
||||
|
||||
esp_err_t sscma_client_new_flasher_we2_spi(const sscma_client_io_handle_t io, const sscma_client_flasher_we2_config_t *config, sscma_client_flasher_handle_t *ret_flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = NULL;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io && config && ret_flasher, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
flasher_we2 = (sscma_client_flasher_we2_spi_t *)calloc(1, sizeof(sscma_client_flasher_we2_spi_t));
|
||||
|
||||
ESP_RETURN_ON_FALSE(flasher_we2, ESP_ERR_NO_MEM, TAG, "no mem for flasher");
|
||||
|
||||
flasher_we2->reset_gpio_num = config->reset_gpio_num;
|
||||
flasher_we2->reset_level = config->flags.reset_high_active ? 1 : 0;
|
||||
|
||||
flasher_we2->io = io;
|
||||
|
||||
flasher_we2->base.start = sscma_client_flasher_we2_start;
|
||||
flasher_we2->base.write = sscma_client_flasher_we2_write;
|
||||
flasher_we2->base.finish = sscma_client_flasher_we2_finish;
|
||||
flasher_we2->base.abort = sscma_client_flasher_we2_abort;
|
||||
flasher_we2->base.del = sscma_client_flasher_we2_del;
|
||||
|
||||
if (config->flags.reset_use_expander)
|
||||
{
|
||||
flasher_we2->io_expander = config->io_expander;
|
||||
ESP_GOTO_ON_FALSE(flasher_we2->io_expander, ESP_ERR_INVALID_ARG, err, TAG, "invalid io expander");
|
||||
ESP_GOTO_ON_ERROR(esp_io_expander_set_dir(flasher_we2->io_expander, config->reset_gpio_num, IO_EXPANDER_OUTPUT), err, TAG, "set GPIO direction failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pin_bit_mask = 1ULL << config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
flasher_we2->lock = xSemaphoreCreateMutex();
|
||||
|
||||
ESP_GOTO_ON_FALSE(flasher_we2->lock, ESP_ERR_NO_MEM, err, TAG, "no mem for flasher lock");
|
||||
|
||||
*ret_flasher = &flasher_we2->base;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (flasher_we2->lock != NULL)
|
||||
{
|
||||
vSemaphoreDelete(flasher_we2->lock);
|
||||
}
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_dir(flasher_we2->io_expander, flasher_we2->reset_gpio_num, IO_EXPANDER_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
if (flasher_we2 != NULL)
|
||||
{
|
||||
free(flasher_we2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_we2_del(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_spi_t, base);
|
||||
if (flasher_we2->lock != NULL)
|
||||
{
|
||||
vSemaphoreDelete(flasher_we2->lock);
|
||||
}
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_dir(flasher_we2->io_expander, flasher_we2->reset_gpio_num, IO_EXPANDER_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
if (flasher != NULL)
|
||||
{
|
||||
free(flasher);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sscma_client_flasher_we2_start(sscma_client_flasher_handle_t flasher, size_t offset)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
int64_t start = 0;
|
||||
uint32_t addr = 0;
|
||||
size_t remain = 0;
|
||||
uint32_t status = 0;
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_spi_t, base);
|
||||
spi_transaction_t spi_trans = {};
|
||||
|
||||
// assert(offset >= OTA_START_OFFSET);
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
flasher_we2->count = 0;
|
||||
flasher_we2->offset = offset;
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_device_acquire_bus(flasher_we2->io->handle, portMAX_DELAY), err, TAG, "acquire spi bus failed");
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_ENABLE_REG;
|
||||
flasher_we2->data[2] = REG_D8_ISP_EN + REG_D8_TEST_MODE + REG_D8_SPI_DO_EN;
|
||||
spi_trans.length = 3 * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_BURST_ENABLE_REG;
|
||||
flasher_we2->data[2] = 0x31;
|
||||
spi_trans.length = 3 * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_BURST_MODE_REG;
|
||||
flasher_we2->data[2] = 0x11;
|
||||
spi_trans.length = 3 * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = 0x00;
|
||||
flasher_we2->data[2] = (OTA_CONTROL_ADDR & 0xFF);
|
||||
flasher_we2->data[3] = (OTA_CONTROL_ADDR >> 8) & 0xFF;
|
||||
flasher_we2->data[4] = (OTA_CONTROL_ADDR >> 16) & 0xFF;
|
||||
flasher_we2->data[5] = (OTA_CONTROL_ADDR >> 24) & 0xFF;
|
||||
flasher_we2->data[6] = (OTA_CONTROL_REG_DEFAULT & 0xFF);
|
||||
flasher_we2->data[7] = (OTA_CONTROL_REG_DEFAULT >> 8) & 0xFF;
|
||||
flasher_we2->data[8] = (OTA_CONTROL_REG_DEFAULT >> 16) & 0xFF;
|
||||
flasher_we2->data[9] = (OTA_CONTROL_REG_DEFAULT >> 24) & 0xFF;
|
||||
spi_trans.length = 10 * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
// if offset == 0, clear boot slot magic first
|
||||
if (offset == 0)
|
||||
{
|
||||
ESP_LOGW(TAG, "Writing firmware... clearing magic for boot from slot 0");
|
||||
remain = 4096; // magic partition size 4K
|
||||
addr = OTA_BASE_ADDR + OTA_MAX_OFFSET - remain;
|
||||
// write chunk
|
||||
do
|
||||
{
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = 0x00;
|
||||
flasher_we2->data[2] = (addr & 0xFF);
|
||||
flasher_we2->data[3] = (addr >> 8) & 0xFF;
|
||||
flasher_we2->data[4] = (addr >> 16) & 0xFF;
|
||||
flasher_we2->data[5] = (addr >> 24) & 0xFF;
|
||||
memset(flasher_we2->data + 6, 0xFF, OTA_CHUNKED_SIZE);
|
||||
spi_trans.length = (6 + OTA_CHUNKED_SIZE) * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
remain -= OTA_CHUNKED_SIZE;
|
||||
addr += OTA_CHUNKED_SIZE;
|
||||
flasher_we2->count++;
|
||||
|
||||
// check status
|
||||
start = esp_timer_get_time();
|
||||
status = 0xFFFFFFFF;
|
||||
do
|
||||
{
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = 0x00;
|
||||
flasher_we2->data[2] = (OTA_PPDONE_COUNT_ADDR & 0xFF);
|
||||
flasher_we2->data[3] = (OTA_PPDONE_COUNT_ADDR >> 8) & 0xFF;
|
||||
flasher_we2->data[4] = (OTA_PPDONE_COUNT_ADDR >> 16) & 0xFF;
|
||||
flasher_we2->data[5] = (OTA_PPDONE_COUNT_ADDR >> 24) & 0xFF;
|
||||
spi_trans.length = (6) * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_STATUS_REG;
|
||||
flasher_we2->data[2] = 0;
|
||||
spi_trans.length = (3) * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
status = 0;
|
||||
memset(&flasher_we2->data[0], 0x00, 7);
|
||||
memset(&flasher_we2->data[7], 0xFF, 7);
|
||||
flasher_we2->data[0] = OTA_CMD_READ;
|
||||
flasher_we2->data[1] = 0x08;
|
||||
flasher_we2->data[2] = 0x00;
|
||||
spi_trans.length = 7 * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data[0];
|
||||
spi_trans.rx_buffer = &flasher_we2->data[7];
|
||||
spi_trans.rxlength = 7 * 8;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
memcpy(&status, &flasher_we2->data[10], 4);
|
||||
if ((esp_timer_get_time() - start) > 10000000)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
ESP_LOGE(TAG, "Timeout");
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
goto err;
|
||||
}
|
||||
taskYIELD();
|
||||
}
|
||||
while (!((status >> 28) == 1 || (status & 0xFFFFF) == flasher_we2->count));
|
||||
// CRC Error
|
||||
if (((status >> 28) == 1) || (status >> 28) == 3)
|
||||
{
|
||||
ESP_LOGE(TAG, "CRC Error");
|
||||
ret = ESP_FAIL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
while (remain > 0);
|
||||
}
|
||||
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_write(sscma_client_flasher_handle_t flasher, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
int64_t start = 0;
|
||||
size_t remain = len;
|
||||
uint32_t addr = 0;
|
||||
uint32_t status = 0;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_spi_t, base);
|
||||
assert(len % OTA_CHUNKED_SIZE == 0);
|
||||
assert(flasher_we2->offset + len <= OTA_MAX_OFFSET);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
ESP_GOTO_ON_ERROR(spi_device_acquire_bus(flasher_we2->io->handle, portMAX_DELAY), err, TAG, "acquire spi bus failed");
|
||||
do
|
||||
{
|
||||
// write chunk
|
||||
addr = OTA_BASE_ADDR + flasher_we2->offset;
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = 0x00;
|
||||
flasher_we2->data[2] = (addr & 0xFF);
|
||||
flasher_we2->data[3] = (addr >> 8) & 0xFF;
|
||||
flasher_we2->data[4] = (addr >> 16) & 0xFF;
|
||||
flasher_we2->data[5] = (addr >> 24) & 0xFF;
|
||||
memcpy(&flasher_we2->data[6], (uint8_t *)data + (len - remain), OTA_CHUNKED_SIZE);
|
||||
spi_trans.length = (6 + OTA_CHUNKED_SIZE) * 8;
|
||||
spi_trans.tx_buffer = flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
remain -= OTA_CHUNKED_SIZE;
|
||||
flasher_we2->offset += OTA_CHUNKED_SIZE;
|
||||
flasher_we2->count++;
|
||||
|
||||
// check status
|
||||
start = esp_timer_get_time();
|
||||
status = 0xFFFFFFFF;
|
||||
do
|
||||
{
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = 0x00;
|
||||
flasher_we2->data[2] = (OTA_PPDONE_COUNT_ADDR & 0xFF);
|
||||
flasher_we2->data[3] = (OTA_PPDONE_COUNT_ADDR >> 8) & 0xFF;
|
||||
flasher_we2->data[4] = (OTA_PPDONE_COUNT_ADDR >> 16) & 0xFF;
|
||||
flasher_we2->data[5] = (OTA_PPDONE_COUNT_ADDR >> 24) & 0xFF;
|
||||
spi_trans.length = (6) * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_STATUS_REG;
|
||||
flasher_we2->data[2] = 0;
|
||||
spi_trans.length = (3) * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
status = 0;
|
||||
memset(&flasher_we2->data[0], 0x00, 7);
|
||||
memset(&flasher_we2->data[7], 0xFF, 7);
|
||||
flasher_we2->data[0] = OTA_CMD_READ;
|
||||
flasher_we2->data[1] = 0x08;
|
||||
flasher_we2->data[2] = 0x00;
|
||||
spi_trans.length = 7 * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data[0];
|
||||
spi_trans.rx_buffer = &flasher_we2->data[7];
|
||||
spi_trans.rxlength = 7 * 8;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
goto err;
|
||||
}
|
||||
memcpy(&status, &flasher_we2->data[10], 4);
|
||||
if ((esp_timer_get_time() - start) > 3000000)
|
||||
{
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
ESP_LOGE(TAG, "Timeout");
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
goto err;
|
||||
}
|
||||
taskYIELD();
|
||||
}
|
||||
while (!((status >> 28) == 1 || (status & 0xFFFFF) == flasher_we2->count));
|
||||
// CRC Error
|
||||
if (((status >> 28) == 1) || (status >> 28) == 3)
|
||||
{
|
||||
ESP_LOGE(TAG, "CRC Error");
|
||||
ret = ESP_FAIL;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
while (remain > 0);
|
||||
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
err:
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_finish(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_spi_t, base);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
ESP_GOTO_ON_ERROR(spi_device_acquire_bus(flasher_we2->io->handle, portMAX_DELAY), err, TAG, "acquire spi bus failed");
|
||||
|
||||
flasher_we2->data[0] = OTA_CMD_WRITE;
|
||||
flasher_we2->data[1] = OTA_ENABLE_REG;
|
||||
flasher_we2->data[2] = REG_OFF;
|
||||
spi_trans.length = 3 * 8;
|
||||
spi_trans.tx_buffer = &flasher_we2->data;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.rxlength = 0;
|
||||
ret = spi_device_transmit(flasher_we2->io->handle, &spi_trans);
|
||||
|
||||
spi_device_release_bus(flasher_we2->io->handle);
|
||||
|
||||
err:
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_abort(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_flasher_we2_spi_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_spi_t, base);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,890 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "sscma_client_commands.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "sscma_client_io_interface.h"
|
||||
#include "sscma_client_flasher.h"
|
||||
#include "sscma_client_flasher_interface.h"
|
||||
|
||||
static const char *TAG = "sscma_client.flasher.we2.uart";
|
||||
|
||||
#define OTA_ENTER_CMD "1"
|
||||
#define OTA_ENTER_HINT "Send data using the xmodem protocol from your terminal"
|
||||
#define OTA_ENTER_TIMEOUT 5000
|
||||
#define OTA_DONE_HINT "Do you want to end file transmission and reboot system?"
|
||||
#define OTA_DONE_TIMEOUT 60000
|
||||
|
||||
#define XSOH 0x01 // Start Of Header
|
||||
#define XSTX 0x02
|
||||
#define XETX 0x03
|
||||
#define XEOT 0x04 // End Of Transfer
|
||||
#define XENQ 0x05
|
||||
#define XACK 0x06 // ACK
|
||||
#define XNACK 0x15 // NACK
|
||||
#define XETB 0x17 //
|
||||
#define XCAN 0x18 // CANCEL
|
||||
#define XC 0x43
|
||||
#define XEOF 0x1A
|
||||
|
||||
#define XMODEM_BLOCK_SIZE 128
|
||||
#define XMODEM_RX_BUFFER_SIZE 1024
|
||||
|
||||
#define WRITE_BLOCK_MAX_RETRIES 15
|
||||
#define TRANSFER_ACK_TIMEOUT 30000 // 30 seconds
|
||||
#define TRANSFER_EOT_TIMEOUT 30000 // 30 seconds
|
||||
#define TRANSFER_ETB_TIMEOUT 30000 // 30 seconds
|
||||
#define TRANSFER_WRITE_BLOCK_TIMEOUT 30000 // 30 seconds
|
||||
|
||||
static esp_err_t sscma_client_flasher_we2_start(sscma_client_flasher_handle_t flasher, size_t offset);
|
||||
static esp_err_t sscma_client_flasher_we2_write(sscma_client_flasher_handle_t flasher, const void *data, size_t len);
|
||||
static esp_err_t sscma_client_flasher_we2_finish(sscma_client_flasher_handle_t flasher);
|
||||
static esp_err_t sscma_client_flasher_we2_abort(sscma_client_flasher_handle_t flasher);
|
||||
static esp_err_t sscma_client_flasher_we2_del(sscma_client_flasher_handle_t flasher);
|
||||
|
||||
typedef enum {
|
||||
INITIAL,
|
||||
WAIT_FOR_C,
|
||||
WAIT_FOR_C_TIMEOUT,
|
||||
WAIT_FOR_C_ACK,
|
||||
WRITE_BLOCK_FAILED,
|
||||
ABORT_TRANSFER,
|
||||
WRITE_BLOCK,
|
||||
C_ACK_RECEIVED,
|
||||
COMPLETE,
|
||||
WRITE_EOT,
|
||||
WAIT_FOR_EOT_ACK,
|
||||
TIMEOUT_EOT,
|
||||
WRITE_BLOCK_TIMEOUT,
|
||||
WRITE_ETB,
|
||||
WAIT_FOR_ETB_ACK,
|
||||
TIMEOUT_ETB,
|
||||
WAIT_WRITE_BLOCK,
|
||||
FAILED,
|
||||
FINAL,
|
||||
} xmodem_state_t;
|
||||
|
||||
typedef struct xmodem_packet_t
|
||||
{
|
||||
uint8_t preamble;
|
||||
uint8_t id;
|
||||
uint8_t id_complement;
|
||||
uint8_t data[XMODEM_BLOCK_SIZE];
|
||||
union
|
||||
{
|
||||
uint8_t data[2];
|
||||
uint16_t value;
|
||||
} crc;
|
||||
} __attribute__((packed, aligned(1))) xmodem_packet_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sscma_client_flasher_t base; /*!< The base class. */
|
||||
sscma_client_io_t *io; /*!< The IO interface. */
|
||||
esp_io_expander_handle_t io_expander; /* !< IO expander handle */
|
||||
int reset_gpio_num; /*!< The reset GPIO number. */
|
||||
bool reset_level; /*!< The reset GPIO level. */
|
||||
void *user_ctx; /* !< User context */
|
||||
SemaphoreHandle_t lock; /*!< The lock. */
|
||||
xmodem_state_t state; /*!< The state of the flasher. */
|
||||
xmodem_packet_t cur_packet; /*!< The current packet being transmitted. */
|
||||
uint8_t cur_packet_id; /*!< The ID of the current packet. */
|
||||
int64_t cur_time; /*!< The current time. */
|
||||
struct
|
||||
{
|
||||
char *data; /* !< Data buffer */
|
||||
size_t len; /* !< Data length */
|
||||
size_t pos; /* !< Data position */
|
||||
} rx_buffer, tx_buffer; /* !< RX and TX buffer */
|
||||
uint32_t xfer_size;
|
||||
uint8_t write_block_retries; /*!< The write block retries. */
|
||||
} sscma_client_flasher_we2_uart_t;
|
||||
|
||||
static inline bool xmodem_calculate_crc(const uint8_t *data, const uint32_t size, uint16_t *result)
|
||||
{
|
||||
uint16_t crc = 0x0;
|
||||
uint32_t count = size;
|
||||
bool status = false;
|
||||
uint8_t i = 0;
|
||||
|
||||
if (0 != data && 0 != result)
|
||||
{
|
||||
status = true;
|
||||
|
||||
while (0 < count--)
|
||||
{
|
||||
crc = crc ^ (uint16_t)*data << 8;
|
||||
data++;
|
||||
i = 8;
|
||||
|
||||
do
|
||||
{
|
||||
if (0x8000 & crc)
|
||||
{
|
||||
crc = crc << 1 ^ 0x1021;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc = crc << 1;
|
||||
}
|
||||
}
|
||||
while (0 < --i);
|
||||
}
|
||||
*result = ((crc & 0xFF) << 8) + ((crc >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline bool xmodem_verify_packet(const xmodem_packet_t packet, uint8_t expected_packet_id)
|
||||
{
|
||||
bool status = false;
|
||||
uint8_t crc_status = false;
|
||||
uint16_t calculated_crc = 0;
|
||||
|
||||
crc_status = xmodem_calculate_crc(packet.data, XMODEM_BLOCK_SIZE, &calculated_crc);
|
||||
|
||||
if (packet.preamble == XSOH && packet.id == expected_packet_id && packet.id_complement == 0xFF - packet.id && crc_status && calculated_crc == packet.crc.value)
|
||||
{
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static inline bool xmodem_timeout(sscma_client_flasher_we2_uart_t *flasher_we2, int64_t timeout)
|
||||
{
|
||||
if (esp_timer_get_time() - flasher_we2->cur_time >= (timeout * 1000))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static xmodem_state_t xmodem_process(sscma_client_flasher_we2_uart_t *flasher)
|
||||
{
|
||||
uint8_t response = 0;
|
||||
uint8_t ctrl = 0;
|
||||
size_t rlen = 0;
|
||||
uint16_t crc = 0;
|
||||
|
||||
switch (flasher->state)
|
||||
{
|
||||
case INITIAL: {
|
||||
flasher->state = WAIT_FOR_C;
|
||||
flasher->cur_time = esp_timer_get_time();
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_C: {
|
||||
if (sscma_client_io_available(flasher->io, &rlen) == ESP_OK && rlen)
|
||||
{
|
||||
sscma_client_io_read(flasher->io, &response, 1);
|
||||
if (response == XC)
|
||||
{
|
||||
flasher->state = WRITE_BLOCK;
|
||||
flasher->cur_time = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
else if (xmodem_timeout(flasher, TRANSFER_ACK_TIMEOUT))
|
||||
{
|
||||
flasher->state = WAIT_FOR_C_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WRITE_BLOCK: {
|
||||
if (flasher->tx_buffer.data == NULL || flasher->tx_buffer.len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* setup current packet */
|
||||
if (flasher->cur_packet.id != flasher->cur_packet_id || flasher->cur_packet_id == 1)
|
||||
{
|
||||
flasher->cur_packet.preamble = XSOH;
|
||||
flasher->cur_packet.id = flasher->cur_packet_id;
|
||||
flasher->cur_packet.id_complement = 0xFF - flasher->cur_packet_id;
|
||||
memset(flasher->cur_packet.data, 0xFF, XMODEM_BLOCK_SIZE);
|
||||
flasher->xfer_size = flasher->tx_buffer.len - flasher->tx_buffer.pos > XMODEM_BLOCK_SIZE ? XMODEM_BLOCK_SIZE : flasher->tx_buffer.len - flasher->tx_buffer.pos;
|
||||
memcpy(flasher->cur_packet.data, flasher->tx_buffer.data + flasher->tx_buffer.pos, flasher->xfer_size);
|
||||
xmodem_calculate_crc(flasher->cur_packet.data, XMODEM_BLOCK_SIZE, &crc);
|
||||
flasher->cur_packet.crc.value = crc;
|
||||
}
|
||||
sscma_client_io_write(flasher->io, (uint8_t *)&flasher->cur_packet, sizeof(xmodem_packet_t));
|
||||
flasher->cur_packet_id++;
|
||||
flasher->tx_buffer.pos += flasher->xfer_size;
|
||||
flasher->state = WAIT_FOR_C_ACK;
|
||||
flasher->cur_time = esp_timer_get_time();
|
||||
|
||||
break;
|
||||
}
|
||||
case WAIT_WRITE_BLOCK: {
|
||||
if (flasher->tx_buffer.data != NULL && flasher->tx_buffer.len != 0)
|
||||
{
|
||||
flasher->state = WRITE_BLOCK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_C_ACK: {
|
||||
if (sscma_client_io_available(flasher->io, &rlen) == ESP_OK && rlen)
|
||||
{
|
||||
sscma_client_io_read(flasher->io, &response, 1);
|
||||
flasher->cur_time = esp_timer_get_time();
|
||||
switch (response)
|
||||
{
|
||||
case XACK: {
|
||||
flasher->state = C_ACK_RECEIVED;
|
||||
break;
|
||||
}
|
||||
case XNACK: {
|
||||
flasher->state = WRITE_BLOCK_FAILED;
|
||||
break;
|
||||
}
|
||||
case XEOF: {
|
||||
flasher->state = COMPLETE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (xmodem_timeout(flasher, TRANSFER_ACK_TIMEOUT))
|
||||
{
|
||||
flasher->state = WRITE_BLOCK_TIMEOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WRITE_BLOCK_FAILED: {
|
||||
if (flasher->write_block_retries > WRITE_BLOCK_MAX_RETRIES)
|
||||
{
|
||||
flasher->state = ABORT_TRANSFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
flasher->state = WRITE_BLOCK;
|
||||
flasher->cur_packet_id--;
|
||||
flasher->tx_buffer.pos -= flasher->xfer_size;
|
||||
flasher->write_block_retries++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case C_ACK_RECEIVED: {
|
||||
if (flasher->tx_buffer.pos >= flasher->tx_buffer.len)
|
||||
{
|
||||
flasher->tx_buffer.len = 0;
|
||||
flasher->tx_buffer.data = NULL;
|
||||
flasher->state = WAIT_WRITE_BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
flasher->write_block_retries = 0;
|
||||
flasher->state = WRITE_BLOCK;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WRITE_EOT: {
|
||||
ctrl = XEOT;
|
||||
sscma_client_io_write(flasher->io, (uint8_t *)&ctrl, sizeof(char));
|
||||
flasher->state = WAIT_FOR_EOT_ACK;
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_EOT_ACK: {
|
||||
if (sscma_client_io_available(flasher->io, &rlen) == ESP_OK && rlen)
|
||||
{
|
||||
sscma_client_io_read(flasher->io, &response, 1);
|
||||
switch (response)
|
||||
{
|
||||
case XACK:
|
||||
flasher->state = COMPLETE;
|
||||
break;
|
||||
case XNACK:
|
||||
flasher->state = ABORT_TRANSFER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (xmodem_timeout(flasher, TRANSFER_EOT_TIMEOUT))
|
||||
{
|
||||
flasher->state = TIMEOUT_EOT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMPLETE: {
|
||||
ctrl = XEOT;
|
||||
sscma_client_io_write(flasher->io, (uint8_t *)&ctrl, sizeof(char));
|
||||
flasher->state = FINAL;
|
||||
break;
|
||||
}
|
||||
case ABORT_TRANSFER: {
|
||||
ctrl = XCAN;
|
||||
sscma_client_io_write(flasher->io, (uint8_t *)&ctrl, sizeof(char));
|
||||
flasher->state = FINAL;
|
||||
break;
|
||||
}
|
||||
case TIMEOUT_EOT: {
|
||||
flasher->state = ABORT_TRANSFER;
|
||||
break;
|
||||
}
|
||||
case WRITE_BLOCK_TIMEOUT: {
|
||||
flasher->state = WRITE_BLOCK_FAILED;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
flasher->state = ABORT_TRANSFER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flasher->state;
|
||||
}
|
||||
|
||||
esp_err_t xmodem_start(sscma_client_flasher_we2_uart_t *flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
flasher->state = INITIAL;
|
||||
flasher->cur_packet_id = 1;
|
||||
flasher->tx_buffer.data = NULL;
|
||||
flasher->tx_buffer.len = 0;
|
||||
flasher->tx_buffer.pos = 0;
|
||||
flasher->xfer_size = 0;
|
||||
flasher->cur_time = esp_timer_get_time();
|
||||
do
|
||||
{
|
||||
xmodem_process(flasher);
|
||||
if (flasher->state == WRITE_BLOCK)
|
||||
{
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == WAIT_FOR_C_TIMEOUT)
|
||||
{
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((esp_timer_get_time() - flasher->cur_time) / 1000 < TRANSFER_ACK_TIMEOUT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t xmodem_write(sscma_client_flasher_we2_uart_t *flasher, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
flasher->tx_buffer.data = (char *)data;
|
||||
flasher->tx_buffer.pos = 0;
|
||||
flasher->tx_buffer.len = len;
|
||||
flasher->xfer_size = 0;
|
||||
|
||||
do
|
||||
{
|
||||
xmodem_process(flasher);
|
||||
if (flasher->state == WAIT_WRITE_BLOCK)
|
||||
{
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == WRITE_BLOCK_TIMEOUT)
|
||||
{
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == FAILED)
|
||||
{
|
||||
ret = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t xmodem_finish(sscma_client_flasher_we2_uart_t *flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
flasher->state = WRITE_EOT;
|
||||
flasher->cur_packet_id = -1;
|
||||
flasher->tx_buffer.data = NULL;
|
||||
flasher->tx_buffer.len = 0;
|
||||
flasher->tx_buffer.pos = 0;
|
||||
flasher->xfer_size = 0;
|
||||
do
|
||||
{
|
||||
xmodem_process(flasher);
|
||||
if (flasher->state == COMPLETE)
|
||||
{
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == WRITE_BLOCK_TIMEOUT)
|
||||
{
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == FAILED)
|
||||
{
|
||||
ret = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t xmodem_abort(sscma_client_flasher_we2_uart_t *flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
flasher->state = ABORT_TRANSFER;
|
||||
flasher->tx_buffer.data = NULL;
|
||||
flasher->tx_buffer.len = 0;
|
||||
flasher->tx_buffer.pos = 0;
|
||||
flasher->xfer_size = 0;
|
||||
|
||||
do
|
||||
{
|
||||
xmodem_process(flasher);
|
||||
if (flasher->state == FINAL)
|
||||
{
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == TIMEOUT_EOT)
|
||||
{
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
if (flasher->state == FAILED)
|
||||
{
|
||||
ret = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_new_flasher_we2_uart(const sscma_client_io_handle_t io, const sscma_client_flasher_we2_config_t *config, sscma_client_flasher_handle_t *ret_flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = NULL;
|
||||
|
||||
ESP_RETURN_ON_FALSE(io && config && ret_flasher, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
|
||||
flasher_we2 = (sscma_client_flasher_we2_uart_t *)calloc(1, sizeof(sscma_client_flasher_we2_uart_t));
|
||||
|
||||
ESP_RETURN_ON_FALSE(flasher_we2, ESP_ERR_NO_MEM, TAG, "no mem for flasher");
|
||||
|
||||
flasher_we2->reset_gpio_num = config->reset_gpio_num;
|
||||
flasher_we2->reset_level = config->flags.reset_high_active ? 1 : 0;
|
||||
|
||||
flasher_we2->io = io;
|
||||
|
||||
flasher_we2->tx_buffer.data = NULL;
|
||||
flasher_we2->tx_buffer.len = 0;
|
||||
flasher_we2->tx_buffer.pos = 0;
|
||||
flasher_we2->xfer_size = 0;
|
||||
|
||||
flasher_we2->state = INITIAL;
|
||||
flasher_we2->cur_packet_id = 0;
|
||||
flasher_we2->cur_time = esp_timer_get_time();
|
||||
flasher_we2->write_block_retries = 0;
|
||||
|
||||
flasher_we2->base.start = sscma_client_flasher_we2_start;
|
||||
flasher_we2->base.write = sscma_client_flasher_we2_write;
|
||||
flasher_we2->base.finish = sscma_client_flasher_we2_finish;
|
||||
flasher_we2->base.abort = sscma_client_flasher_we2_abort;
|
||||
flasher_we2->base.del = sscma_client_flasher_we2_del;
|
||||
|
||||
if (config->flags.reset_use_expander)
|
||||
{
|
||||
flasher_we2->io_expander = config->io_expander;
|
||||
ESP_GOTO_ON_FALSE(flasher_we2->io_expander, ESP_ERR_INVALID_ARG, err, TAG, "invalid io expander");
|
||||
ESP_GOTO_ON_ERROR(esp_io_expander_set_dir(flasher_we2->io_expander, config->reset_gpio_num, IO_EXPANDER_OUTPUT), err, TAG, "set GPIO direction failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pin_bit_mask = 1ULL << config->reset_gpio_num,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
|
||||
}
|
||||
|
||||
flasher_we2->rx_buffer.data = (char *)malloc(XMODEM_RX_BUFFER_SIZE);
|
||||
ESP_GOTO_ON_FALSE(flasher_we2->rx_buffer.data, ESP_ERR_NO_MEM, err, TAG, "no mem for rx buffer");
|
||||
flasher_we2->rx_buffer.pos = 0;
|
||||
flasher_we2->rx_buffer.len = XMODEM_RX_BUFFER_SIZE;
|
||||
|
||||
flasher_we2->lock = xSemaphoreCreateMutex();
|
||||
|
||||
ESP_GOTO_ON_FALSE(flasher_we2->lock, ESP_ERR_NO_MEM, err, TAG, "no mem for flasher lock");
|
||||
|
||||
*ret_flasher = &flasher_we2->base;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (flasher_we2->rx_buffer.data)
|
||||
{
|
||||
free(flasher_we2->rx_buffer.data);
|
||||
}
|
||||
if (flasher_we2->lock != NULL)
|
||||
{
|
||||
vSemaphoreDelete(flasher_we2->lock);
|
||||
}
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_dir(flasher_we2->io_expander, flasher_we2->reset_gpio_num, IO_EXPANDER_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
if (flasher_we2 != NULL)
|
||||
{
|
||||
free(flasher_we2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_flasher_we2_del(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_uart_t, base);
|
||||
if (flasher_we2->rx_buffer.data)
|
||||
{
|
||||
free(flasher_we2->rx_buffer.data);
|
||||
}
|
||||
if (flasher_we2->lock != NULL)
|
||||
{
|
||||
vSemaphoreDelete(flasher_we2->lock);
|
||||
}
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_dir(flasher_we2->io_expander, flasher_we2->reset_gpio_num, IO_EXPANDER_INPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
}
|
||||
}
|
||||
if (flasher != NULL)
|
||||
{
|
||||
free(flasher);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sscma_client_flasher_we2_start(sscma_client_flasher_handle_t flasher, size_t offset)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
int64_t start = 0;
|
||||
size_t rlen = 0;
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_uart_t, base);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_GOTO_ON_ERROR(sscma_client_io_write(flasher_we2->io, CMD_PREFIX CMD_AT_RESET CMD_SUFFIX, sizeof(CMD_PREFIX CMD_AT_RESET CMD_SUFFIX) - 1), err, TAG, "reset sscma failed");
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS); // wait for sscma to be ready
|
||||
}
|
||||
|
||||
// enter ota mode
|
||||
flasher_we2->rx_buffer.pos = 0;
|
||||
start = esp_timer_get_time();
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
sscma_client_io_flush(flasher_we2->io);
|
||||
do
|
||||
{
|
||||
sscma_client_io_write(flasher_we2->io, OTA_ENTER_CMD, sizeof(OTA_ENTER_CMD) - 1);
|
||||
if (sscma_client_io_available(flasher_we2->io, &rlen) == ESP_OK && rlen > 0)
|
||||
{
|
||||
if (rlen + flasher_we2->rx_buffer.pos > flasher_we2->rx_buffer.len)
|
||||
{
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < rlen; i++)
|
||||
{
|
||||
char c = '\0';
|
||||
sscma_client_io_read(flasher_we2->io, &c, 1);
|
||||
if (isprint(c))
|
||||
{
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos++] = c;
|
||||
}
|
||||
}
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos] = 0;
|
||||
if (strnstr(flasher_we2->rx_buffer.data, OTA_ENTER_HINT, flasher_we2->rx_buffer.pos) != NULL)
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
}
|
||||
while ((esp_timer_get_time() - start) / 1000 < OTA_ENTER_TIMEOUT);
|
||||
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "enter ota mode failed");
|
||||
|
||||
// write offset config
|
||||
if (offset != 0)
|
||||
{
|
||||
char config[12] = { 0xC0, 0x5A, (offset >> 0) & 0xFF, (offset >> 8) & 0xFF, (offset >> 16) & 0xFF, (offset >> 24) & 0xFF, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xC0 };
|
||||
|
||||
ESP_GOTO_ON_ERROR(xmodem_start(flasher_we2), err, TAG, "write config failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(xmodem_write(flasher_we2, config, sizeof(config)), err, TAG, "write config failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(xmodem_finish(flasher_we2), err, TAG, "write config failed");
|
||||
|
||||
flasher_we2->tx_buffer.pos = 0;
|
||||
start = esp_timer_get_time();
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
do
|
||||
{
|
||||
if (sscma_client_io_available(flasher_we2->io, &rlen) == ESP_OK && rlen > 0)
|
||||
{
|
||||
if (rlen + flasher_we2->rx_buffer.pos > flasher_we2->rx_buffer.len)
|
||||
{
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < rlen; i++)
|
||||
{
|
||||
char c = '\0';
|
||||
sscma_client_io_read(flasher_we2->io, &c, 1);
|
||||
if (isprint(c))
|
||||
{
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos++] = c;
|
||||
}
|
||||
}
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos] = 0;
|
||||
if (strnstr(flasher_we2->rx_buffer.data, OTA_DONE_HINT, flasher_we2->rx_buffer.pos) != NULL)
|
||||
{
|
||||
sscma_client_io_write(flasher_we2->io, "n", 1);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
}
|
||||
while ((esp_timer_get_time() - start) / 1000 < OTA_DONE_TIMEOUT);
|
||||
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "enter ota mode failed");
|
||||
}
|
||||
|
||||
ESP_GOTO_ON_ERROR(xmodem_start(flasher_we2), err, TAG, "start xmodem failed");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_write(sscma_client_flasher_handle_t flasher, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_uart_t, base);
|
||||
|
||||
assert(len % XMODEM_BLOCK_SIZE == 0);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
ret = xmodem_write(flasher_we2, data, len);
|
||||
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_finish(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
int64_t start = 0;
|
||||
size_t rlen = 0;
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_uart_t, base);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
ret = xmodem_finish(flasher_we2);
|
||||
|
||||
flasher_we2->tx_buffer.pos = 0;
|
||||
start = esp_timer_get_time();
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
do
|
||||
{
|
||||
if (sscma_client_io_available(flasher_we2->io, &rlen) == ESP_OK && rlen > 0)
|
||||
{
|
||||
if (rlen + flasher_we2->rx_buffer.pos > flasher_we2->rx_buffer.len)
|
||||
{
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < rlen; i++)
|
||||
{
|
||||
char c = '\0';
|
||||
sscma_client_io_read(flasher_we2->io, &c, 1);
|
||||
if (isprint(c))
|
||||
{
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos++] = c;
|
||||
}
|
||||
}
|
||||
flasher_we2->rx_buffer.data[flasher_we2->rx_buffer.pos] = 0;
|
||||
if (strnstr(flasher_we2->rx_buffer.data, OTA_DONE_HINT, flasher_we2->rx_buffer.pos) != NULL)
|
||||
{
|
||||
sscma_client_io_write(flasher_we2->io, "y", 1);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(5 / portTICK_PERIOD_MS);
|
||||
}
|
||||
while ((esp_timer_get_time() - start) / 1000 < OTA_DONE_TIMEOUT);
|
||||
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "finish ota failed");
|
||||
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
err:
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static esp_err_t sscma_client_flasher_we2_abort(sscma_client_flasher_handle_t flasher)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_flasher_we2_uart_t *flasher_we2 = __containerof(flasher, sscma_client_flasher_we2_uart_t, base);
|
||||
|
||||
xSemaphoreTake(flasher_we2->lock, portMAX_DELAY);
|
||||
|
||||
ret = xmodem_abort(flasher_we2);
|
||||
|
||||
if (flasher_we2->reset_gpio_num >= 0)
|
||||
{
|
||||
if (flasher_we2->io_expander)
|
||||
{
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
esp_io_expander_set_level(flasher_we2->io_expander, flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_config_t io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1ULL << flasher_we2->reset_gpio_num,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, flasher_we2->reset_level);
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(flasher_we2->reset_gpio_num, !flasher_we2->reset_level);
|
||||
gpio_reset_pin(flasher_we2->reset_gpio_num);
|
||||
vTaskDelay(200);
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(flasher_we2->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "esp_check.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "sscma_client_io_interface.h"
|
||||
|
||||
static const char *TAG = "sscma_client.io";
|
||||
|
||||
esp_err_t sscma_client_io_del(sscma_client_io_t *io)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io->del, ESP_ERR_NOT_SUPPORTED, TAG, "del not supported");
|
||||
return io->del(io);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_io_write(sscma_client_io_t *io, const void *data, size_t len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io->write, ESP_ERR_NOT_SUPPORTED, TAG, "write not supported");
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
return io->write(io, data, len);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_io_read(sscma_client_io_t *io, void *data, size_t len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io->read, ESP_ERR_NOT_SUPPORTED, TAG, "read not supported");
|
||||
assert(data);
|
||||
return io->read(io, data, len);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_io_available(sscma_client_io_t *io, size_t *ret_avail)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io->available, ESP_ERR_NOT_SUPPORTED, TAG, "available not supported");
|
||||
return io->available(io, ret_avail);
|
||||
}
|
||||
|
||||
esp_err_t sscma_client_io_flush(sscma_client_io_t *io)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(io->flush, ESP_ERR_NOT_SUPPORTED, TAG, "flush not supported");
|
||||
return io->flush(io);
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "sscma_client_io_interface.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
static const char *TAG = "sscma_client.io.i2c";
|
||||
|
||||
#define I2C_ADDRESS (0x62)
|
||||
|
||||
#define HEADER_LEN (uint8_t)4
|
||||
#define MAX_PL_LEN (uint8_t)250
|
||||
#define CHECKSUM_LEN (uint8_t)2
|
||||
|
||||
#define PACKET_SIZE (uint16_t)(HEADER_LEN + MAX_PL_LEN + CHECKSUM_LEN)
|
||||
|
||||
#define FEATURE_TRANSPORT 0x10
|
||||
#define FEATURE_TRANSPORT_CMD_READ 0x01
|
||||
#define FEATURE_TRANSPORT_CMD_WRITE 0x02
|
||||
#define FEATURE_TRANSPORT_CMD_AVAILABLE 0x03
|
||||
#define FEATURE_TRANSPORT_CMD_START 0x04
|
||||
#define FEATURE_TRANSPORT_CMD_STOP 0x05
|
||||
#define FEATURE_TRANSPORT_CMD_RESET 0x06
|
||||
|
||||
static esp_err_t client_io_i2c_del(sscma_client_io_t *io);
|
||||
static esp_err_t client_io_i2c_write(sscma_client_io_t *io, const void *data, size_t len);
|
||||
static esp_err_t client_io_i2c_read(sscma_client_io_t *io, void *data, size_t len);
|
||||
static esp_err_t client_io_i2c_available(sscma_client_io_t *io, size_t *len);
|
||||
static esp_err_t client_io_i2c_flush(sscma_client_io_t *io);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sscma_client_io_t base;
|
||||
uint32_t i2c_bus_id; // I2C bus id, indicating which I2C port
|
||||
uint32_t dev_addr; // Device address
|
||||
int wait_delay; // I2C wait delay
|
||||
void *user_ctx; // User context
|
||||
SemaphoreHandle_t lock; // Lock
|
||||
uint8_t buffer[PACKET_SIZE]; // I2C packet buffer
|
||||
} sscma_client_io_i2c_t;
|
||||
|
||||
esp_err_t sscma_client_new_io_i2c_bus(sscma_client_i2c_bus_handle_t bus, const sscma_client_io_i2c_config_t *io_config, sscma_client_io_handle_t *ret_io)
|
||||
{
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_i2c_t *i2c_client_io = NULL;
|
||||
ESP_GOTO_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
i2c_client_io = (sscma_client_io_i2c_t *)calloc(1, sizeof(sscma_client_io_i2c_t));
|
||||
ESP_GOTO_ON_FALSE(i2c_client_io, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c client io");
|
||||
|
||||
i2c_client_io->i2c_bus_id = (uint32_t)bus;
|
||||
i2c_client_io->dev_addr = io_config->dev_addr;
|
||||
i2c_client_io->wait_delay = io_config->wait_delay;
|
||||
i2c_client_io->user_ctx = io_config->user_ctx;
|
||||
i2c_client_io->base.del = client_io_i2c_del;
|
||||
i2c_client_io->base.write = client_io_i2c_write;
|
||||
i2c_client_io->base.read = client_io_i2c_read;
|
||||
i2c_client_io->base.available = client_io_i2c_available;
|
||||
i2c_client_io->base.flush = client_io_i2c_flush;
|
||||
|
||||
i2c_client_io->lock = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(i2c_client_io->lock, ESP_ERR_NO_MEM, err, TAG, "no mem for lock");
|
||||
|
||||
*ret_io = &i2c_client_io->base;
|
||||
ESP_LOGD(TAG, "new i2c sscma client io @%p", i2c_client_io);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (i2c_client_io)
|
||||
{
|
||||
if (i2c_client_io->lock)
|
||||
{
|
||||
vSemaphoreDelete(i2c_client_io->lock);
|
||||
}
|
||||
free(i2c_client_io);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_i2c_del(sscma_client_io_t *io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_i2c_t *i2c_client_io = __containerof(io, sscma_client_io_i2c_t, base);
|
||||
|
||||
vSemaphoreDelete(i2c_client_io->lock);
|
||||
free(i2c_client_io);
|
||||
ESP_LOGD(TAG, "del i2c sscma client io @%p", i2c_client_io);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_i2c_write(sscma_client_io_t *io, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
sscma_client_io_i2c_t *i2c_client_io = __containerof(io, sscma_client_io_i2c_t, base);
|
||||
uint16_t packets = len / MAX_PL_LEN;
|
||||
uint16_t remain = len % MAX_PL_LEN;
|
||||
|
||||
xSemaphoreTake(i2c_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (data)
|
||||
{
|
||||
for (uint16_t i = 0; i < packets; i++)
|
||||
{
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_WRITE;
|
||||
i2c_client_io->buffer[2] = MAX_PL_LEN >> 8;
|
||||
i2c_client_io->buffer[3] = MAX_PL_LEN & 0xFF;
|
||||
memcpy(i2c_client_io->buffer + 4, data + i * MAX_PL_LEN, MAX_PL_LEN);
|
||||
// TODO CRC
|
||||
i2c_client_io->buffer[4 + MAX_PL_LEN] = 0xFF;
|
||||
i2c_client_io->buffer[5 + MAX_PL_LEN] = 0xFF;
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, MAX_PL_LEN + 6, portMAX_DELAY), err, TAG,
|
||||
"i2c master write failed");
|
||||
}
|
||||
|
||||
if (remain)
|
||||
{
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_WRITE;
|
||||
i2c_client_io->buffer[2] = remain >> 8;
|
||||
i2c_client_io->buffer[3] = remain & 0xFF;
|
||||
memcpy(i2c_client_io->buffer + 4, data + packets * MAX_PL_LEN, remain);
|
||||
// TODO CRC
|
||||
i2c_client_io->buffer[4 + remain] = 0xFF;
|
||||
i2c_client_io->buffer[5 + remain] = 0xFF;
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, remain + 6, portMAX_DELAY), err, TAG, "i2c master write failed");
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
xSemaphoreGive(i2c_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_i2c_read(sscma_client_io_t *io, void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
sscma_client_io_i2c_t *i2c_client_io = __containerof(io, sscma_client_io_i2c_t, base);
|
||||
uint16_t packets = len / MAX_PL_LEN;
|
||||
uint16_t remain = len % MAX_PL_LEN;
|
||||
|
||||
xSemaphoreTake(i2c_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (data)
|
||||
{
|
||||
for (uint16_t i = 0; i < packets; i++)
|
||||
{
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_READ;
|
||||
i2c_client_io->buffer[2] = MAX_PL_LEN >> 8;
|
||||
i2c_client_io->buffer[3] = MAX_PL_LEN & 0xFF;
|
||||
i2c_client_io->buffer[4] = 0xFF;
|
||||
i2c_client_io->buffer[5] = 0xFF;
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, 6, portMAX_DELAY), err, TAG, "i2c master write failed");
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_read_from_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, MAX_PL_LEN + 6, portMAX_DELAY), err, TAG,
|
||||
"i2c master read failed");
|
||||
memcpy(data + i * MAX_PL_LEN, i2c_client_io->buffer, MAX_PL_LEN);
|
||||
}
|
||||
|
||||
if (remain)
|
||||
{
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_READ;
|
||||
i2c_client_io->buffer[2] = remain >> 8;
|
||||
i2c_client_io->buffer[3] = remain & 0xFF;
|
||||
i2c_client_io->buffer[4] = 0xFF;
|
||||
i2c_client_io->buffer[5] = 0xFF;
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, 6, portMAX_DELAY), err, TAG, "i2c master write failed");
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_read_from_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, remain + 6, portMAX_DELAY), err, TAG, "i2c master read failed");
|
||||
memcpy(data + packets * MAX_PL_LEN, i2c_client_io->buffer, remain);
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
xSemaphoreGive(i2c_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_i2c_available(sscma_client_io_t *io, size_t *len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_i2c_t *i2c_client_io = __containerof(io, sscma_client_io_i2c_t, base);
|
||||
|
||||
xSemaphoreTake(i2c_client_io->lock, portMAX_DELAY);
|
||||
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_AVAILABLE;
|
||||
i2c_client_io->buffer[2] = 0x00;
|
||||
i2c_client_io->buffer[3] = 0x00;
|
||||
i2c_client_io->buffer[4] = 0xFF;
|
||||
i2c_client_io->buffer[5] = 0xFF;
|
||||
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, HEADER_LEN + CHECKSUM_LEN, portMAX_DELAY), err, TAG,
|
||||
"i2c master write failed");
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_read_from_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, 2, portMAX_DELAY), err, TAG, "i2c master read failed");
|
||||
|
||||
*len = (i2c_client_io->buffer[0] << 8) | i2c_client_io->buffer[1];
|
||||
|
||||
err:
|
||||
xSemaphoreGive(i2c_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_i2c_flush(sscma_client_io_t *io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_i2c_t *i2c_client_io = __containerof(io, sscma_client_io_i2c_t, base);
|
||||
|
||||
xSemaphoreTake(i2c_client_io->lock, portMAX_DELAY);
|
||||
|
||||
i2c_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
i2c_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_RESET;
|
||||
i2c_client_io->buffer[2] = 0x00;
|
||||
i2c_client_io->buffer[3] = 0x00;
|
||||
i2c_client_io->buffer[4] = 0xFF;
|
||||
i2c_client_io->buffer[5] = 0xFF;
|
||||
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(i2c_master_write_to_device(i2c_client_io->i2c_bus_id, i2c_client_io->dev_addr, i2c_client_io->buffer, HEADER_LEN + CHECKSUM_LEN, portMAX_DELAY), err, TAG,
|
||||
"i2c master write failed");
|
||||
if (i2c_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(i2c_client_io->wait_delay));
|
||||
}
|
||||
|
||||
err:
|
||||
xSemaphoreGive(i2c_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
// The local log level must be defined before including esp_log.h
|
||||
// Set the maximum log level for this source file
|
||||
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "cJSON.h"
|
||||
#include "sscma_client_io_interface.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
static const char *TAG = "sscma_client.io.spi";
|
||||
|
||||
#define HEADER_LEN (uint8_t)4
|
||||
#define MAX_PL_LEN (uint8_t)250
|
||||
#define CHECKSUM_LEN (uint8_t)2
|
||||
|
||||
#define PACKET_SIZE (uint16_t)(HEADER_LEN + MAX_PL_LEN + CHECKSUM_LEN)
|
||||
|
||||
#define MAX_RECIEVE_SIZE (uint16_t)4095
|
||||
|
||||
#define FEATURE_TRANSPORT 0x10
|
||||
#define FEATURE_TRANSPORT_CMD_READ 0x01
|
||||
#define FEATURE_TRANSPORT_CMD_WRITE 0x02
|
||||
#define FEATURE_TRANSPORT_CMD_AVAILABLE 0x03
|
||||
#define FEATURE_TRANSPORT_CMD_START 0x04
|
||||
#define FEATURE_TRANSPORT_CMD_STOP 0x05
|
||||
#define FEATURE_TRANSPORT_CMD_RESET 0x06
|
||||
|
||||
static esp_err_t client_io_spi_del(sscma_client_io_t *io);
|
||||
static esp_err_t client_io_spi_write(sscma_client_io_t *io, const void *data, size_t len);
|
||||
static esp_err_t client_io_spi_read(sscma_client_io_t *io, void *data, size_t len);
|
||||
static esp_err_t client_io_spi_available(sscma_client_io_t *io, size_t *len);
|
||||
static esp_err_t client_io_spi_flush(sscma_client_io_t *io);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sscma_client_io_t base;
|
||||
spi_device_handle_t spi_dev; // SPI bus id, indicating which SPI port
|
||||
int sync_gpio_num; // D/C line GPIO number
|
||||
size_t spi_trans_max_bytes; // SPI transaction max bytes
|
||||
int wait_delay; // SPI wait delay
|
||||
void *user_ctx; // User context
|
||||
esp_io_expander_handle_t io_expander; // IO expander
|
||||
SemaphoreHandle_t lock; // Lock
|
||||
uint8_t buffer[PACKET_SIZE];
|
||||
} sscma_client_io_spi_t;
|
||||
|
||||
esp_err_t sscma_client_new_io_spi_bus(sscma_client_spi_bus_handle_t bus, const sscma_client_io_spi_config_t *io_config, sscma_client_io_handle_t *ret_io)
|
||||
{
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_spi_t *spi_client_io = NULL;
|
||||
ESP_GOTO_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
spi_client_io = (sscma_client_io_spi_t *)calloc(1, sizeof(sscma_client_io_spi_t));
|
||||
ESP_GOTO_ON_FALSE(spi_client_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi client io");
|
||||
|
||||
spi_device_interface_config_t dev_config = {
|
||||
.flags = (io_config->flags.lsb_first ? SPI_DEVICE_TXBIT_LSBFIRST : 0) | (io_config->flags.sio_mode ? SPI_DEVICE_3WIRE : 0) | (io_config->flags.cs_high_active ? SPI_DEVICE_POSITIVE_CS : 0),
|
||||
.clock_speed_hz = io_config->pclk_hz,
|
||||
.mode = io_config->spi_mode,
|
||||
.spics_io_num = io_config->cs_gpio_num,
|
||||
.queue_size = 1,
|
||||
};
|
||||
|
||||
ret = spi_bus_add_device((spi_host_device_t)bus, &dev_config, &spi_client_io->spi_dev);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "adding spi device to bus failed");
|
||||
|
||||
if (io_config->sync_gpio_num >= 0)
|
||||
{
|
||||
if (io_config->flags.sync_use_expander)
|
||||
{
|
||||
if (io_config->io_expander == NULL)
|
||||
{
|
||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "io expander not set");
|
||||
}
|
||||
spi_client_io->io_expander = io_config->io_expander;
|
||||
ESP_GOTO_ON_ERROR(esp_io_expander_set_dir(io_config->io_expander, io_config->sync_gpio_num, IO_EXPANDER_INPUT), err, TAG, "setting sync GPIO for sync failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
// zero-initialize the config structure.
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.pin_bit_mask = (1 << io_config->sync_gpio_num);
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_down_en = 1;
|
||||
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configuring sync GPIO for sync failed");
|
||||
}
|
||||
}
|
||||
|
||||
spi_client_io->sync_gpio_num = io_config->sync_gpio_num;
|
||||
spi_client_io->wait_delay = io_config->wait_delay;
|
||||
spi_client_io->user_ctx = io_config->user_ctx;
|
||||
spi_client_io->base.del = client_io_spi_del;
|
||||
spi_client_io->base.write = client_io_spi_write;
|
||||
spi_client_io->base.read = client_io_spi_read;
|
||||
spi_client_io->base.available = client_io_spi_available;
|
||||
spi_client_io->base.flush = client_io_spi_flush;
|
||||
spi_client_io->base.handle = spi_client_io->spi_dev;
|
||||
|
||||
spi_client_io->lock = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(spi_client_io->lock, ESP_ERR_NO_MEM, err, TAG, "no mem for mutex");
|
||||
|
||||
size_t max_trans_bytes = 0;
|
||||
ESP_GOTO_ON_ERROR(spi_bus_get_max_transaction_len((spi_host_device_t)bus, &max_trans_bytes), err, TAG, "get spi max transaction len failed");
|
||||
spi_client_io->spi_trans_max_bytes = max_trans_bytes;
|
||||
ESP_LOGI(TAG, "spi max trans bytes: %d", spi_client_io->spi_trans_max_bytes);
|
||||
|
||||
*ret_io = &spi_client_io->base;
|
||||
ESP_LOGD(TAG, "new spi sscma client io @%p", spi_client_io);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (spi_client_io)
|
||||
{
|
||||
if (spi_client_io->spi_dev)
|
||||
{
|
||||
spi_bus_remove_device(spi_client_io->spi_dev);
|
||||
spi_bus_free((spi_host_device_t)bus);
|
||||
}
|
||||
if (spi_client_io->lock)
|
||||
{
|
||||
vSemaphoreDelete(spi_client_io->lock);
|
||||
}
|
||||
free(spi_client_io);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_spi_del(sscma_client_io_t *io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_spi_t *spi_client_io = __containerof(io, sscma_client_io_spi_t, base);
|
||||
if (spi_client_io->lock)
|
||||
{
|
||||
vSemaphoreDelete(spi_client_io->lock);
|
||||
}
|
||||
spi_bus_remove_device(spi_client_io->spi_dev);
|
||||
spi_bus_free((spi_host_device_t)spi_client_io->spi_dev);
|
||||
if (spi_client_io->sync_gpio_num >= 0)
|
||||
{
|
||||
gpio_reset_pin(spi_client_io->sync_gpio_num);
|
||||
}
|
||||
ESP_LOGD(TAG, "del spi sscma client io @%p", spi_client_io);
|
||||
|
||||
free(spi_client_io);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_spi_write(sscma_client_io_t *io, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_io_spi_t *spi_client_io = __containerof(io, sscma_client_io_spi_t, base);
|
||||
uint16_t packets = len / MAX_PL_LEN;
|
||||
uint16_t remain = len % MAX_PL_LEN;
|
||||
size_t trans_len = 0;
|
||||
|
||||
xSemaphoreTake(spi_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (spi_device_acquire_bus(spi_client_io->spi_dev, portMAX_DELAY) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
for (uint16_t i = 0; i < packets; i++)
|
||||
{
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_WRITE;
|
||||
spi_client_io->buffer[2] = MAX_PL_LEN >> 8;
|
||||
spi_client_io->buffer[3] = MAX_PL_LEN & 0xFF;
|
||||
spi_client_io->buffer[4 + MAX_PL_LEN] = 0xFF;
|
||||
spi_client_io->buffer[5 + MAX_PL_LEN] = 0xFF;
|
||||
memcpy(spi_client_io->buffer + 4, data + i * MAX_PL_LEN, MAX_PL_LEN);
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
}
|
||||
|
||||
if (remain)
|
||||
{
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_WRITE;
|
||||
spi_client_io->buffer[2] = remain >> 8;
|
||||
spi_client_io->buffer[3] = remain & 0xFF;
|
||||
spi_client_io->buffer[4 + remain] = 0xFF;
|
||||
spi_client_io->buffer[5 + remain] = 0xFF;
|
||||
memcpy(spi_client_io->buffer + 4, data + packets * MAX_PL_LEN, remain);
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
spi_device_release_bus(spi_client_io->spi_dev);
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_spi_read(sscma_client_io_t *io, void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_io_spi_t *spi_client_io = __containerof(io, sscma_client_io_spi_t, base);
|
||||
uint16_t packets = len / MAX_RECIEVE_SIZE;
|
||||
uint16_t remain = len % MAX_RECIEVE_SIZE;
|
||||
size_t trans_len = 0;
|
||||
|
||||
xSemaphoreTake(spi_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (spi_device_acquire_bus(spi_client_io->spi_dev, portMAX_DELAY) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
for (uint16_t i = 0; i < packets; i++)
|
||||
{
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_READ;
|
||||
spi_client_io->buffer[2] = MAX_RECIEVE_SIZE >> 8;
|
||||
spi_client_io->buffer[3] = MAX_RECIEVE_SIZE & 0xFF;
|
||||
spi_client_io->buffer[4] = 0xFF;
|
||||
spi_client_io->buffer[5] = 0xFF;
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
|
||||
trans_len = MAX_RECIEVE_SIZE;
|
||||
spi_trans.rx_buffer = data + i * MAX_RECIEVE_SIZE;
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.tx_buffer = NULL;
|
||||
spi_trans.rxlength = chunk_size * 8;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.rx_buffer = spi_trans.rx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
}
|
||||
if (remain)
|
||||
{
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_READ;
|
||||
spi_client_io->buffer[2] = remain >> 8;
|
||||
spi_client_io->buffer[3] = remain & 0xFF;
|
||||
spi_client_io->buffer[4] = 0xFF;
|
||||
spi_client_io->buffer[5] = 0xFF;
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
trans_len = remain;
|
||||
spi_trans.rx_buffer = data + packets * MAX_RECIEVE_SIZE;
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.tx_buffer = NULL;
|
||||
spi_trans.rxlength = chunk_size * 8;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.rx_buffer = spi_trans.rx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
spi_device_release_bus(spi_client_io->spi_dev);
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_spi_available(sscma_client_io_t *io, size_t *len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_io_spi_t *spi_client_io = __containerof(io, sscma_client_io_spi_t, base);
|
||||
size_t trans_len = 0;
|
||||
uint32_t sync_level = 0;
|
||||
|
||||
*len = 0;
|
||||
|
||||
xSemaphoreTake(spi_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (spi_client_io->sync_gpio_num >= 0)
|
||||
{
|
||||
if (spi_client_io->io_expander)
|
||||
{
|
||||
if (esp_io_expander_get_level(spi_client_io->io_expander, spi_client_io->sync_gpio_num, &sync_level) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sync_level = gpio_get_level(spi_client_io->sync_gpio_num);
|
||||
}
|
||||
if (sync_level == 0)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
|
||||
if (spi_device_acquire_bus(spi_client_io->spi_dev, portMAX_DELAY) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_AVAILABLE;
|
||||
spi_client_io->buffer[2] = 0x00;
|
||||
spi_client_io->buffer[3] = 0x00;
|
||||
spi_client_io->buffer[4] = 0xFF;
|
||||
spi_client_io->buffer[5] = 0xFF;
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
|
||||
spi_trans.length = 2 * 8;
|
||||
spi_trans.tx_buffer = NULL;
|
||||
spi_trans.rxlength = 2 * 8; // 8 bits per byte
|
||||
spi_trans.rx_buffer = spi_client_io->buffer;
|
||||
spi_trans.user = spi_client_io;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
*len = (spi_client_io->buffer[0] << 8) | spi_client_io->buffer[1];
|
||||
if (*len == 0xFFFF)
|
||||
{
|
||||
*len = 0;
|
||||
}
|
||||
err:
|
||||
spi_device_release_bus(spi_client_io->spi_dev);
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_spi_flush(sscma_client_io_t *io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
spi_transaction_t spi_trans = {};
|
||||
sscma_client_io_spi_t *spi_client_io = __containerof(io, sscma_client_io_spi_t, base);
|
||||
size_t trans_len = 0;
|
||||
uint32_t sync_level = 0;
|
||||
|
||||
xSemaphoreTake(spi_client_io->lock, portMAX_DELAY);
|
||||
|
||||
if (spi_client_io->sync_gpio_num >= 0)
|
||||
{
|
||||
if (spi_client_io->io_expander)
|
||||
{
|
||||
if (esp_io_expander_get_level(spi_client_io->io_expander, spi_client_io->sync_gpio_num, &sync_level) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sync_level = gpio_get_level(spi_client_io->sync_gpio_num);
|
||||
}
|
||||
if (sync_level == 0)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
|
||||
if (spi_device_acquire_bus(spi_client_io->spi_dev, portMAX_DELAY) != ESP_OK)
|
||||
{
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
trans_len = PACKET_SIZE;
|
||||
memset(spi_client_io->buffer, 0, sizeof(spi_client_io->buffer));
|
||||
spi_client_io->buffer[0] = FEATURE_TRANSPORT;
|
||||
spi_client_io->buffer[1] = FEATURE_TRANSPORT_CMD_RESET;
|
||||
spi_client_io->buffer[2] = 0x00;
|
||||
spi_client_io->buffer[3] = 0x00;
|
||||
spi_client_io->buffer[4] = 0xFF;
|
||||
spi_client_io->buffer[5] = 0xFF;
|
||||
spi_trans.tx_buffer = spi_client_io->buffer;
|
||||
if (spi_client_io->wait_delay > 0)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(spi_client_io->wait_delay));
|
||||
}
|
||||
do
|
||||
{
|
||||
uint16_t chunk_size = trans_len;
|
||||
if (chunk_size > spi_client_io->spi_trans_max_bytes)
|
||||
{
|
||||
chunk_size = spi_client_io->spi_trans_max_bytes;
|
||||
spi_trans.flags |= SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = trans_len;
|
||||
spi_trans.flags &= ~SPI_TRANS_CS_KEEP_ACTIVE;
|
||||
}
|
||||
spi_trans.length = chunk_size * 8;
|
||||
spi_trans.rxlength = 0;
|
||||
spi_trans.rx_buffer = NULL;
|
||||
spi_trans.user = spi_client_io;
|
||||
ret = spi_device_transmit(spi_client_io->spi_dev, &spi_trans);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (queue) failed");
|
||||
spi_trans.tx_buffer = spi_trans.tx_buffer + chunk_size;
|
||||
trans_len -= chunk_size;
|
||||
}
|
||||
while (trans_len > 0);
|
||||
|
||||
err:
|
||||
spi_device_release_bus(spi_client_io->spi_dev);
|
||||
xSemaphoreGive(spi_client_io->lock);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "sscma_client_io_interface.h"
|
||||
#include "sscma_client_io.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
static const char *TAG = "sscma_client.io.uart";
|
||||
|
||||
static esp_err_t client_io_uart_del(sscma_client_io_t *io);
|
||||
static esp_err_t client_io_uart_write(sscma_client_io_t *io, const void *data, size_t len);
|
||||
static esp_err_t client_io_uart_read(sscma_client_io_t *io, void *data, size_t len);
|
||||
static esp_err_t client_io_uart_available(sscma_client_io_t *io, size_t *len);
|
||||
static esp_err_t client_io_uart_flush(sscma_client_io_t *io);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sscma_client_io_t base;
|
||||
SemaphoreHandle_t lock; // Mutex lock
|
||||
uint32_t uart_port; // UART port
|
||||
void *user_ctx; // User context
|
||||
} sscma_client_io_uart_t;
|
||||
|
||||
esp_err_t sscma_client_new_io_uart_bus(sscma_client_uart_bus_handle_t bus, const sscma_client_io_uart_config_t *io_config, sscma_client_io_handle_t *ret_io)
|
||||
{
|
||||
#if CONFIG_SSCMA_ENABLE_DEBUG_LOG
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_uart_t *uart_client_io = NULL;
|
||||
ESP_GOTO_ON_FALSE(io_config && ret_io, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
uart_client_io = (sscma_client_io_uart_t *)calloc(1, sizeof(sscma_client_io_uart_t));
|
||||
ESP_GOTO_ON_FALSE(uart_client_io, ESP_ERR_NO_MEM, err, TAG, "no mem for uart client io");
|
||||
|
||||
uart_client_io->uart_port = (uint32_t)bus;
|
||||
uart_client_io->user_ctx = io_config->user_ctx;
|
||||
uart_client_io->base.del = client_io_uart_del;
|
||||
uart_client_io->base.write = client_io_uart_write;
|
||||
uart_client_io->base.read = client_io_uart_read;
|
||||
uart_client_io->base.available = client_io_uart_available;
|
||||
uart_client_io->base.flush = client_io_uart_flush;
|
||||
|
||||
uart_client_io->lock = xSemaphoreCreateMutex();
|
||||
ESP_GOTO_ON_FALSE(uart_client_io->lock, ESP_ERR_NO_MEM, err, TAG, "no mem for mutex");
|
||||
|
||||
*ret_io = &uart_client_io->base;
|
||||
ESP_LOGI(TAG, "new uart sscma client io @%p", uart_client_io);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (uart_client_io)
|
||||
{
|
||||
if (uart_client_io->lock)
|
||||
{
|
||||
vSemaphoreDelete(uart_client_io->lock);
|
||||
}
|
||||
free(uart_client_io);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t client_io_uart_del(sscma_client_io_t *io)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_uart_t *uart_client_io = __containerof(io, sscma_client_io_uart_t, base);
|
||||
|
||||
if (uart_client_io->lock)
|
||||
{
|
||||
vSemaphoreDelete(uart_client_io->lock);
|
||||
}
|
||||
|
||||
free(uart_client_io);
|
||||
|
||||
ESP_LOGD(TAG, "del uart sscma client io @%p", uart_client_io);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_uart_write(sscma_client_io_t *io, const void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_uart_t *uart_client_io = __containerof(io, sscma_client_io_uart_t, base);
|
||||
ESP_GOTO_ON_FALSE(uart_client_io, ESP_ERR_INVALID_STATE, err, TAG, "uart not initialized");
|
||||
|
||||
xSemaphoreTake(uart_client_io->lock, portMAX_DELAY);
|
||||
|
||||
ret = uart_write_bytes(uart_client_io->uart_port, data, len);
|
||||
|
||||
xSemaphoreGive(uart_client_io->lock);
|
||||
|
||||
err:
|
||||
return ret == len ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_uart_read(sscma_client_io_t *io, void *data, size_t len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_uart_t *uart_client_io = __containerof(io, sscma_client_io_uart_t, base);
|
||||
ESP_GOTO_ON_FALSE(uart_client_io, ESP_ERR_INVALID_STATE, err, TAG, "uart not initialized");
|
||||
|
||||
xSemaphoreTake(uart_client_io->lock, portMAX_DELAY);
|
||||
ret = uart_read_bytes(uart_client_io->uart_port, data, len, portMAX_DELAY);
|
||||
xSemaphoreGive(uart_client_io->lock);
|
||||
|
||||
err:
|
||||
return ret == len ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_uart_available(sscma_client_io_t *io, size_t *len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
sscma_client_io_uart_t *uart_client_io = __containerof(io, sscma_client_io_uart_t, base);
|
||||
ESP_GOTO_ON_FALSE(uart_client_io, ESP_ERR_INVALID_STATE, err, TAG, "uart not initialized");
|
||||
|
||||
xSemaphoreTake(uart_client_io->lock, portMAX_DELAY);
|
||||
ret = uart_get_buffered_data_len(uart_client_io->uart_port, len);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
xSemaphoreGive(uart_client_io->lock);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t client_io_uart_flush(sscma_client_io_t *io)
|
||||
{
|
||||
sscma_client_io_uart_t *uart_client_io = __containerof(io, sscma_client_io_uart_t, base);
|
||||
ESP_RETURN_ON_ERROR(uart_flush(uart_client_io->uart_port), TAG, "uart flush failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
1806
managed_components/wvirgil123__sscma_client/src/sscma_client_ops.c
Normal file
1806
managed_components/wvirgil123__sscma_client/src/sscma_client_ops.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user