Files
test_rtt_413/iap/iap.c
2024-07-26 09:03:23 +08:00

625 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "rtthread.h"
#include "ipc/completion.h"
#include "ymodem.h"
#include <stdint.h>
#include <stddef.h>
#include "fal.h"
#define IAP_FLASH_APP_SEC "app"
#define IAP_FLASH_BOOT_SEC "boot"
#define IFLASH_PAGE_SIZE (128 * 1024)
// #define IAP_DBG
#include "rthw.h"
#ifdef IAP_DBG
char iap_dbg_log[4096] = "";
int iap_dbg_index = 0;
#endif
void iap_dbg(const char *fmt, ...)
{
#ifdef IAP_DBG
va_list args;
rt_base_t level = rt_hw_interrupt_disable();
va_start(args, fmt);
iap_dbg_index += rt_vsnprintf(&iap_dbg_log[iap_dbg_index], sizeof(iap_dbg_log) - iap_dbg_index, fmt, args);
va_end(args);
rt_hw_interrupt_enable(level);
#endif
}
static struct rt_completion _wait;
rt_err_t port_rx_ind(rt_device_t dev, rt_size_t size)
{
rt_completion_done(&_wait);
return RT_EOK;
}
#define IS_AF(c) ((c >= 'A') && (c <= 'F'))
#define IS_af(c) ((c >= 'a') && (c <= 'f'))
#define IS_09(c) ((c >= '0') && (c <= '9'))
#define ISVALIDHEX(c) IS_AF(c) || IS_af(c) || IS_09(c)
#define ISVALIDDEC(c) IS_09(c)
#define CONVERTDEC(c) (c - '0')
#define CONVERTHEX_alpha(c) (IS_AF(c) ? (c - 'A'+10) : (c - 'a'+10))
#define CONVERTHEX(c) (IS_09(c) ? (c - '0') : CONVERTHEX_alpha(c))
/**
* @brief Convert a string to an integer
* @param inputstr: The string to be converted
* @param intnum: The intger value
* @retval 1: Correct
* 0: Error
*/
rt_inline rt_uint32_t Str2Int(uint8_t *inputstr, int32_t *intnum)
{
rt_uint32_t i = 0, res = 0;
rt_uint32_t val = 0;
if (inputstr[0] == '0' && (inputstr[1] == 'x' || inputstr[1] == 'X'))
{
if (inputstr[2] == '\0')
{
return 0;
}
for (i = 2; i < 11; i++)
{
if (inputstr[i] == '\0')
{
*intnum = val;
/* return 1; */
res = 1;
break;
}
if (ISVALIDHEX(inputstr[i]))
{
val = (val << 4) + CONVERTHEX(inputstr[i]);
}
else
{
/* return 0, Invalid input */
res = 0;
break;
}
}
/* over 8 digit hex --invalid */
if (i >= 11)
{
res = 0;
}
}
else /* max 10-digit decimal input */
{
for (i = 0;i < 11;i++)
{
if (inputstr[i] == '\0')
{
*intnum = val;
/* return 1 */
res = 1;
break;
}
else if ((inputstr[i] == 'k' || inputstr[i] == 'K') && (i > 0))
{
val = val << 10;
*intnum = val;
res = 1;
break;
}
else if ((inputstr[i] == 'm' || inputstr[i] == 'M') && (i > 0))
{
val = val << 20;
*intnum = val;
res = 1;
break;
}
else if (ISVALIDDEC(inputstr[i]))
{
val = val * 10 + CONVERTDEC(inputstr[i]);
}
else
{
/* return 0, Invalid input */
res = 0;
break;
}
}
/* Over 10 digit decimal --invalid */
if (i >= 11)
{
res = 0;
}
}
return res;
}
static const char *port_list[] = {
"uart1",
"uart2",
};
#define PORTS_NUM (sizeof(port_list)/sizeof(port_list[0]))
void rt_hw_shell_relink(const char *name, rt_uint32_t baud);
static rt_device_t _read_ports(rt_device_t ports[], rt_uint8_t want)
{
rt_uint8_t tmp;
for (int i = 0; i < PORTS_NUM; i++)
{
if (ports[i] == RT_NULL)
continue;
if (rt_device_read(ports[i], 0, &tmp, 1) != 1)
continue;
if (tmp == want)
return ports[i];
}
return RT_NULL;
}
static void _init_all_ports(rt_device_t ports[])
{
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
config.baud_rate = BAUD_RATE_460800;
for (int i = 0; i < PORTS_NUM; i++)
{
ports[i] = rt_device_find(port_list[i]);
if (ports[i] == RT_NULL)
continue;
rt_enter_critical();
ports[i]->rx_indicate = port_rx_ind;
rt_exit_critical();
rt_device_control(ports[i], RT_DEVICE_CTRL_CONFIG, &config);
rt_device_open(ports[i], RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
}
}
static void _close_other_ports(rt_device_t ports[], rt_device_t console_dev)
{
for (int i = 0; i < PORTS_NUM; i++)
{
if (ports[i] == RT_NULL)
continue;
if (ports[i] == console_dev)
continue;
rt_device_close(ports[i]);
ports[i] = RT_NULL;
}
}
static void _print_ports(rt_device_t ports[], const char *fmt, ...)
{
va_list args;
char line_buf[128] = { 0 };
rt_size_t real;
va_start(args, fmt);
real = rt_vsnprintf(line_buf, sizeof(line_buf), fmt, args);
va_end(args);
for (int i = 0; i < PORTS_NUM; i++)
{
if (ports[i] == RT_NULL)
continue;
rt_device_write(ports[i], 0, line_buf, real);
}
}
static const char version_header[] = {0xB5,0x5B,0xff,'\0'};
static const char Firmware_Version[] = "\r\nXY_F405_IAP_460800_FV2.0_R20240724";
static const char version_end[] = {0xBB,'\0'};
static void iap_show_version_string(rt_device_t ports[])
{
char iap_version[100]= {'\0'};
strcat(iap_version,version_header);
strcat(iap_version,Firmware_Version);
strcat(iap_version,version_end);
iap_version[2] = strlen(iap_version);
_print_ports(ports, "%s\n", iap_version);
}
#define RT_BUF_SIZE (1024)
struct custom_ctx {
struct rym_ctx parent;
int32_t file_size;
int32_t w_index;
int32_t recevied_head;
const struct fal_partition *flash;
rt_uint8_t ck_buffer[RT_BUF_SIZE];
};
static enum rym_code _rym_recv_begin(
struct rym_ctx *ctx,
rt_uint8_t *buf,
rt_size_t len)
{
struct custom_ctx *cctx = (struct custom_ctx *) ctx;
RT_ASSERT(cctx);
rt_uint8_t tmp_size[16] = {0};
while (*buf != 0)
{
buf ++;
}
buf ++;
rt_bool_t end = RT_FALSE;
for (rt_uint8_t i = 0; i < 16; i++)
{
if (*buf == 0)
{
tmp_size[i] = 0;
end = RT_TRUE;
break;
}
tmp_size[i] = *buf++;
}
if (!end)
{
return RYM_CODE_CAN;
}
if (!Str2Int(tmp_size, &cctx->file_size))
{
return RYM_CODE_CAN;
}
if (cctx->file_size >= cctx->flash->len)
{
return RYM_CODE_CAN;
}
cctx->w_index = 0;
cctx->recevied_head = 0;
return RYM_CODE_ACK;
}
static enum rym_code _rym_recv_data(
struct rym_ctx *ctx,
rt_uint8_t *buf,
rt_size_t len)
{
struct custom_ctx *cctx = (struct custom_ctx *) ctx;
RT_ASSERT(cctx);
RT_ASSERT(len <= RT_BUF_SIZE);
if (cctx->recevied_head == 0)
{
if (fal_partition_erase(cctx->flash, 0, cctx->file_size) < 0)
{
return RYM_CODE_CAN;
}
cctx->recevied_head = 1;
}
if (cctx->w_index >= cctx->file_size)
{
return RYM_CODE_ACK;
}
if (cctx->w_index + len > cctx->file_size)
{
len = cctx->file_size - cctx->w_index;
}
if (fal_partition_write(cctx->flash, cctx->w_index, buf, len) <= 0)
{
return RYM_CODE_CAN;
}
if (fal_partition_read(cctx->flash, cctx->w_index, cctx->ck_buffer, len) <= 0)
{
return RYM_CODE_CAN;
}
for (int i = 0; i < len; i++)
{
if (cctx->ck_buffer[i] != buf[i])
return RYM_CODE_CAN;
}
cctx->w_index += len;
return RYM_CODE_ACK;
}
static enum rym_code _rym_recv_end(
struct rym_ctx *ctx,
rt_uint8_t *buf,
rt_size_t len)
{
return RYM_CODE_ACK;
}
enum iap_v5_cmd
{
IAP_V5_CMD_ONE_REPLY = 1,
IAP_V5_CMD_C_REPLY,
IAP_V5_CMD_UPDATE_SUCCEED,
IAP_V5_CMD_UPDATE_FAILED,
IAP_V5_CMD_BIN_OVERSIZE,
IAP_V5_CMD_CHECK_FAILED,
IAP_V5_CMD_USER_CANCEL,
IAP_V5_CMD_RECEIVE_FAILED,
IAP_V5_CMD_POWER_ON_AGAIN,
};
/**
* @brief 响应协议
*
* 0xB5,0x5B,{cmd},0xBB, 0x00
*/
#define IAP_V5_CMD_SIZE (5)
static const rt_uint8_t *_pack_cmd(enum iap_v5_cmd cmd)
{
static rt_uint8_t _cmd_line[IAP_V5_CMD_SIZE]
= { 0xB5, 0x5B, 0x00, 0xBB, 0x00 };
_cmd_line[2] = cmd;
return _cmd_line;
}
#define _response_cmd(cmd) \
rt_device_write(console_dev, 0, _pack_cmd(cmd), IAP_V5_CMD_SIZE)
static rt_device_t iap_v5_req_char(rt_device_t ports[],
struct rt_completion *_wait,
rt_uint8_t want,
rt_tick_t wait)
{
rt_tick_t start = rt_tick_get();
rt_device_t get = RT_NULL;
while (rt_tick_get() - start < wait)
{
get = _read_ports(ports, want);
if (get != RT_NULL)
{
break;
}
if (rt_completion_wait(_wait, wait - (rt_tick_get() - start))
!= RT_EOK)
{
break;
}
}
return get;
}
static void jump_to_app(void);
extern const uint32_t IapAppAddr;
static int _iap_entry(rt_bool_t is_boot);
int iap_main_entry(void)
{
return _iap_entry(RT_TRUE);
}
// INIT_APP_EXPORT(iap_main_entry);
#include "shell.h"
static void _jump_to_app_entry(void *p)
{
rt_thread_delay(500);
rt_kprintf("jump to app ...\n");
jump_to_app();
}
void _on_iap_end(rt_device_t console_dev)
{
rt_device_close(console_dev);
rt_hw_shell_relink(console_dev->parent.name, BAUD_RATE_460800);
}
static rt_bool_t _handle_connect(rt_device_t console_dev, rt_tick_t timeout)
{
rt_int32_t cnt_c = 0;
rt_uint8_t tmp;
rt_tick_t start = rt_tick_get();
while (rt_tick_get() - start < timeout)
{
if (rt_device_read(console_dev, 0, &tmp, 1) != 1)
{
if (rt_completion_wait(&_wait, timeout - (rt_tick_get() - start)) != RT_EOK)
break;
continue;
}
if (tmp == '1')
{
_response_cmd(IAP_V5_CMD_ONE_REPLY);
continue;
}
if (tmp == 'c')
{
cnt_c ++;
_response_cmd(IAP_V5_CMD_C_REPLY);
break;
}
}
return cnt_c > 0;
}
static int _iap_entry(rt_bool_t is_boot)
{
if (IapAppAddr && is_boot)
{
return 0;
}
rt_device_t _ports[PORTS_NUM] = {0};
rt_err_t err = RT_EOK;
struct custom_ctx *ctx = RT_NULL;
rt_device_t console_dev = rt_console_get_device();
RT_ASSERT(console_dev);
rt_completion_init(&_wait);
// 使用特殊设备暂时替代 console 口, 避免逻辑出错
rt_hw_shell_relink("block_r", BAUD_RATE_460800);
rt_thread_delay(200);
_init_all_ports(_ports);
iap_show_version_string(_ports);
rt_device_t get_port = iap_v5_req_char(_ports, &_wait, '1', is_boot? 1000: 100000);
if (get_port == RT_NULL)
{
_close_other_ports(_ports, console_dev);
goto _exit;
}
if (get_port != console_dev)
{
console_dev = get_port;
}
_close_other_ports(_ports, console_dev);
_response_cmd(IAP_V5_CMD_ONE_REPLY);
if (!_handle_connect(console_dev, 1000))
{
err = RT_ERROR;
iap_dbg("_handle_connect failed\n");
goto _exit;
}
ctx = rt_calloc(1, sizeof(*ctx));
if (!ctx)
{
err = RT_ENOMEM;
iap_dbg("rt_malloc failed\n");
goto _exit;
}
ctx->parent.v5_iap_adapt = RT_TRUE;
ctx->flash = fal_partition_find(IapAppAddr? IAP_FLASH_BOOT_SEC: IAP_FLASH_APP_SEC);
if (ctx->flash == RT_NULL)
{
err = RT_ENOSYS;
iap_dbg("fal_partition_find %s failed: %d\n", IapAppAddr? IAP_FLASH_BOOT_SEC: IAP_FLASH_APP_SEC, rt_get_errno());
rt_free(ctx);
goto _exit;
}
err = rym_recv_on_device(&ctx->parent, console_dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
_rym_recv_begin, _rym_recv_data, _rym_recv_end, 1000);
rt_free(ctx);
rt_thread_mdelay(200);
if (err == RT_EOK)
{
_response_cmd(IAP_V5_CMD_UPDATE_SUCCEED);
_on_iap_end(console_dev);
return 0;
}
_exit:
if (err == RT_EOK && is_boot)
{
rt_thread_startup(rt_thread_create("iap_boot", _jump_to_app_entry, RT_NULL, 2048, 10, 10));
_on_iap_end(console_dev);
return 0;
}
_response_cmd(IAP_V5_CMD_UPDATE_FAILED);
_response_cmd(IAP_V5_CMD_POWER_ON_AGAIN);
_on_iap_end(console_dev);
return 0;
}
#include <stdint.h>
#define FLASH_RUN_BASEADDR 0x08020000
//定义函数指针用于跳转到APP
typedef void (*pFunction)(void);
static pFunction JumpToApplication;
static uint32_t JumpAddress;
void jump_to_app(void)
{
SysTick->CTRL = 0; //关键代码
HAL_DeInit();
HAL_NVIC_DisableIRQ(SysTick_IRQn);
HAL_NVIC_ClearPendingIRQ(SysTick_IRQn);
/* Jump to user application */
__disable_irq();
JumpAddress = *(__IO uint32_t*) (FLASH_RUN_BASEADDR + 4);
JumpToApplication = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) FLASH_RUN_BASEADDR);
__set_CONTROL(0);
__enable_irq();
JumpToApplication();
}
MSH_CMD_EXPORT(jump_to_app, jump_to_app);
static void _cmd_iap(void)
{
_iap_entry(RT_FALSE);
}
MSH_CMD_EXPORT_ALIAS(_cmd_iap, iap, iap bootloader / app start);
#ifdef IAP_DBG
void iap_log()
{
int index = 0;
for (int i = 0; i < iap_dbg_index; i++)
{
if (iap_dbg_log[i] == '\n' || iap_dbg_log[i] == '\0')
{
iap_dbg_log[i] = '\0';
rt_kprintf("log: %s\n", &iap_dbg_log[index]);
index = i+1;
}
}
}
MSH_CMD_EXPORT(iap_log, iap log);
#endif