#include "rtthread.h" #include "ipc/completion.h" #include "ymodem.h" #include #include #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 #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