Files
test_rtt_413/iap/iap.c
2023-08-01 10:22:00 +08:00

486 lines
10 KiB
C
Raw 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 "fal.h"
#define IAP_FLASH_APP_SEC "app"
#define IAP_FLASH_BOOT_SEC "boot"
static rt_device_t console_dev;
struct rt_completion _wait;
#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;
}
rt_err_t port_rx_ind(rt_device_t dev, rt_size_t size)
{
rt_completion_done(&_wait);
return RT_EOK;
}
#define RT_BUF_SIZE (1024)
/*
* 固件格式:
*
* 作用 & 说明
* - 记录固件对应硬件 & 版本信息
* - 简便起见,不包含校验信息
* - 允许 iap 升级不含 header 的普通固件
*
* index len desc
* 0 4 'XYFC'
* 4 4 固件信息长度
* 8 N 固件信息 ('类型'-'版本')
* 8+N M 固件数据
*/
static const char FIXED_HEAD[] = {'X', 'Y', 'F', 'C'};
extern const char IAP_BIN_PREFIX[];
static int _check_head(const rt_uint8_t *buf, rt_size_t len)
{
if (len < 8)
{
return 0;
}
for (int i = 0; i < sizeof(FIXED_HEAD); i++)
{
if (FIXED_HEAD[i] != buf[i])
{
return 0;
}
}
rt_uint32_t real = *((const rt_uint32_t *)&buf[4]);
rt_uint32_t want = rt_strlen(IAP_BIN_PREFIX);
if (real <= want || len - 8 < real)
{
return -3;
}
for(int i = 0; i < want; i++)
{
if (IAP_BIN_PREFIX[i] != buf[8 + i])
{
return -4;
}
}
return real + 8;
}
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)
{
// return RYM_ERR_ACK;
// return RYM_CODE_CAN;
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);
int offset = 0;
if (cctx->recevied_head == 0)
{
offset = _check_head(buf, len);
if (offset < 0)
{
return RYM_ERR_FILE;
}
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[offset], len - offset) <= 0)
{
return RYM_CODE_CAN;
}
if (fal_partition_read(cctx->flash, cctx->w_index, cctx->ck_buffer, len - offset) <= 0)
{
return RYM_CODE_CAN;
}
for (int i = 0; i < len - offset; i++)
{
if (cctx->ck_buffer[i] != buf[i + offset])
return RYM_CODE_CAN;
}
cctx->w_index += len - offset;
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_bool_t iap_v5_req_char(rt_device_t console_dev,
struct rt_completion *_wait,
rt_uint8_t want,
rt_tick_t wait)
{
rt_uint8_t ch;
rt_tick_t start = rt_tick_get();
rt_bool_t rt = RT_FALSE;
while (rt_tick_get() - start < wait)
{
if (rt_device_read(console_dev, 0, &ch, 1) == 1)
{
if (ch == want)
{
rt = RT_TRUE;
break;
}
continue;
}
if (rt_completion_wait(_wait, wait - (rt_tick_get() - start))
!= RT_EOK)
{
break;
}
}
return rt;
}
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);
}
#include "shell.h"
static int _iap_entry(rt_bool_t is_boot)
{
if (IapAppAddr && is_boot)
{
return 0;
}
if ((!IapAppAddr) && is_boot)
{
finsh_set_prompt("msh (IAP)");
}
rt_err_t (*odev_rx_ind)(rt_device_t dev, rt_size_t size);
rt_err_t err = RT_EOK;
struct custom_ctx *ctx = RT_NULL;
console_dev = rt_console_get_device();
RT_ASSERT(console_dev);
rt_completion_init(&_wait);
odev_rx_ind = console_dev->rx_indicate;
rt_device_set_rx_indicate(console_dev, port_rx_ind);
if (!iap_v5_req_char(console_dev, &_wait, '1', is_boot? 1000: 100000))
{
goto _exit;
}
_response_cmd(IAP_V5_CMD_ONE_REPLY);
rt_thread_mdelay(200);
if (!iap_v5_req_char(console_dev, &_wait, 'c', 1000))
{
err = RT_ENOSYS;
goto _exit;
}
_response_cmd(IAP_V5_CMD_C_REPLY);
ctx = rt_calloc(1, sizeof(*ctx));
if (!ctx)
{
err = RT_ENOMEM;
rt_kprintf("rt_malloc failed\n");
goto _exit;
}
ctx->flash = fal_partition_find(IapAppAddr? IAP_FLASH_BOOT_SEC: IAP_FLASH_APP_SEC);
if (ctx->flash == RT_NULL)
{
err = RT_ENOSYS;
rt_kprintf("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);
return 0;
}
_exit:
if (err == RT_EOK && is_boot)
{
rt_kprintf("jump to app ...\n");
jump_to_app();
return 0;
}
_response_cmd(IAP_V5_CMD_UPDATE_FAILED);
_response_cmd(IAP_V5_CMD_POWER_ON_AGAIN);
rt_device_set_rx_indicate(console_dev, odev_rx_ind);
return 0;
}
#include <stm32f4xx.h>
#include <stdint.h>
#define FLASH_RUN_BASEADDR 0x08020000
//定义函数指针用于跳转到APP
typedef void (*pFunction)(void);
static pFunction JumpToApplication;
static uint32_t JumpAddress;
static 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);
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);