diff --git a/apps/chrg/applications/thread/chrg_comm.c b/apps/chrg/applications/thread/chrg_comm.c index 5b3a06d..9c29767 100644 --- a/apps/chrg/applications/thread/chrg_comm.c +++ b/apps/chrg/applications/thread/chrg_comm.c @@ -22,11 +22,12 @@ #include #include "chrg_north_pkg.h" #include "chrg_lcd.h" +#include "chrg_ymodem_proxy.h" #include "stm32f4xx_hal.h" #define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */ -#define CHRG_FIRMWARE_VERSION 0x0139U /* 当前协议板版本:V1.1.41 */ +#define CHRG_FIRMWARE_VERSION 0x013AU /* 当前协议板版本:V1.1.42 */ rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR]; @@ -37,6 +38,7 @@ rt_uint16_t usRegProBuf[TOTAL_HOLD_ALL_REGS]; //北向protcal??? //rt_uint16_t usRegTestBuf[TOTAL_TEST_REGS]; rt_uint8_t g_proto_pData[80] = {0};//上位机协议指令,控制四个通道;二维 rt_uint16_t g_proto_reqLen = 0; +#if 0 /* 旧YMODEM重发状态机已由chrg_ymodem_proxy替代。 */ rt_uint8_t mobude_ymodem_buf[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升级缓冲区 rt_uint8_t mobude_ymodem_buf_2[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升级缓冲区 @@ -65,6 +67,7 @@ static const rt_uint8_t g_ymodem_update_frame[8] = static const rt_uint8_t g_ymodem_jump_frame[8] = {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0xF8, 0x2F}; static void chrg_ymodem_session_stop(void); +#endif /* Modbus 影子寄存器: 0x06/0x10 写操作暂存到此, PARAM_EFFECT 生效后复制到实际 pNOR->sw */ static struct chrg_switch_t g_shadow_sw[TOTAL_NOR_CHS]; static rt_uint8_t g_shadow_dirty[TOTAL_NOR_CHS] = {0}; /* 0=已生效 1=待生效,防止重复覆盖 */ @@ -1414,6 +1417,7 @@ eMBException funcTrimELoad (rt_uint8_t *reqFrame, rt_uint16_t reqLen, 其它说明: - *****************************************************************/ +#if 0 /* 旧YMODEM时序只作参考,禁止编译和运行。 */ Ymodem_state ystate = Ymodem_No; rt_uint8_t s_first_soh_rcvd = 0; @@ -1633,6 +1637,7 @@ static void chrg_ymodem_session_stop(void) chrg_ymodem_protocol_reset(); ystate = Ymodem_No; } +#endif eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen, rt_uint8_t *resFrame, rt_uint16_t *resLen) @@ -1651,16 +1656,13 @@ eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen, ch = (eIDX_SOU_CH)ch_num; if (reqFrame[5] == 0xFF) { - if (chrg_ymodem_session_start(ch) != 0) { + if (chrg_ymodem_proxy_enter((rt_uint8_t)ch) != 0) { return MB_EX_SLAVE_BUSY; } } else if (reqFrame[5] == 0x00) { - chrg_eload_clear_ymodem_resp(); - if (chrg_ymodem_send_control_frame(g_ymodem_jump_frame) != 0) { - chrg_ymodem_session_stop(); + if (chrg_ymodem_proxy_exit((rt_uint8_t)ch) != 0) { return MB_EX_SLAVE_DEVICE_FAILURE; } - chrg_ymodem_session_stop(); } else { return MB_EX_ILLEGAL_DATA_VALUE; } @@ -1757,6 +1759,7 @@ static int chrg_comm_parse_msg (struct chrg_comm_t *pCOMM) return ret; } +#if 0 /* 旧版协议板自动重发实现,禁止编译。 */ /* * 旧版Ymodem处理代码保留在下方注释中作为历史参考。当前实现使用完整拼帧、 * CRC校验和停止等待重发机制,不再依赖一次串口接收刚好得到一整帧。 @@ -2199,6 +2202,7 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM) // return eStatus; //} +#endif /***************************************************************** 函数名称: chrg_comm_updata_north 函数描述: 解析南向程序数据包 @@ -2226,17 +2230,15 @@ static int chrg_comm_updata_north (struct chrg_comm_t *pCOMM) } if (pCOMM->rx_len > 0) { - process_ret = chrg_ymodem_process(pCOMM->rx_buf, pCOMM->rx_len); - if ((process_ret == -3) || (process_ret == -4)) { - /* 本地校验或南向提交失败时要求上位机重发当前帧。 */ - pCOMM->tx_buf[0] = NAK; - pCOMM->tx_len = 1; - return chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len); - } + process_ret = chrg_ymodem_proxy_feed_north(pCOMM->rx_buf, + pCOMM->rx_len); + (void)process_ret; } - need_tx = ymodem_send(pCOMM); - if ((need_tx != 0) && (pCOMM->tx_len > 0)) { + pCOMM->tx_len = chrg_ymodem_proxy_poll(pCOMM->tx_buf, + sizeof(pCOMM->tx_buf)); + need_tx = (pCOMM->tx_len > 0U) ? 1U : 0U; + if (need_tx != 0U) { ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len); } // uint8_t need_tx = 0; diff --git a/apps/chrg/applications/thread/chrg_roll_sou.c b/apps/chrg/applications/thread/chrg_roll_sou.c index 29b99b1..c4ad23b 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.c +++ b/apps/chrg/applications/thread/chrg_roll_sou.c @@ -41,6 +41,13 @@ static rt_uint8_t g_sou_measure_once_active_mask = 0U; * round, which also covers the south board's slower power-up time. */ static rt_uint8_t g_sou_version_ready[TOTAL_SOU_CHS] = {0U}; +void chrg_roll_sou_version_invalidate(rt_uint8_t ch) +{ + if (ch < TOTAL_SOU_CHS) { + g_sou_version_ready[ch] = 0U; + } +} + /* COM批量命令按通道轮询发送。一次写命令收到匹配回码后才移动游标; * 回码错误或超时时游标保持不变,使同一条命令优先完成重发,避免破坏 * 本通道批次内的寄存器执行顺序。 */ diff --git a/apps/chrg/applications/thread/chrg_roll_sou.h b/apps/chrg/applications/thread/chrg_roll_sou.h index 41d383b..0c64570 100644 --- a/apps/chrg/applications/thread/chrg_roll_sou.h +++ b/apps/chrg/applications/thread/chrg_roll_sou.h @@ -95,6 +95,8 @@ extern void chrg_roll_sou_thread_entry (void *data); 其它说明: 数据在函数内完成复制,调用者返回后可以立即复用原缓冲区 *****************************************************************/ extern int chrg_send_sou_upadata_data(const rt_uint8_t *buf, rt_uint16_t leng); +/* 升级跳转App后强制目标通道重新读取功率板版本。 */ +extern void chrg_roll_sou_version_invalidate(rt_uint8_t ch); extern int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val); extern int chrg_sou_com_batch_submit(eIDX_SOU_CH ch, const rt_uint16_t *regs, const rt_uint16_t *vals, rt_uint8_t count); diff --git a/apps/chrg/applications/utils/chrg_def.h b/apps/chrg/applications/utils/chrg_def.h index 50a88bb..b0b736e 100644 --- a/apps/chrg/applications/utils/chrg_def.h +++ b/apps/chrg/applications/utils/chrg_def.h @@ -14,8 +14,8 @@ /* CHRG application version. Keep this value synchronized with the changelog. */ #define CHRG_VERSION_MAJOR 1U #define CHRG_VERSION_MINOR 1U -#define CHRG_VERSION_PATCH 41U -#define CHRG_VERSION_STRING "V1.1.41" +#define CHRG_VERSION_PATCH 42U +#define CHRG_VERSION_STRING "V1.1.42" /* * Channel polling mode: diff --git a/apps/chrg/applications/utils/chrg_eload.c b/apps/chrg/applications/utils/chrg_eload.c index e53aaef..16bec7e 100644 --- a/apps/chrg/applications/utils/chrg_eload.c +++ b/apps/chrg/applications/utils/chrg_eload.c @@ -156,7 +156,7 @@ static rt_uint8_t g_eload_ymodem_ready = 0; // 应答就绪标志(1=有新应 函数描述: 存储南向板返回的ymodem应答字节(由chrg_roll_sou在RUN_UPDATA调用) 输入参数: resp: 南向ymodem应答 (ACK=0x06 / NAK=0x15 / 'C'=0x43) *****************************************************************/ -#define ELOAD_YMODEM_RESP_DEPTH 8 +#define ELOAD_YMODEM_RESP_DEPTH 16 static rt_uint8_t g_ymodem_resp_queue[ELOAD_YMODEM_RESP_DEPTH]; static rt_uint8_t g_ymodem_resp_read = 0; static rt_uint8_t g_ymodem_resp_write = 0; @@ -172,6 +172,8 @@ void chrg_eload_set_ymodem_resp(rt_uint8_t resp) rt_hw_interrupt_enable(level); return; } + /* 保留新到达的ACK/NAK/CAN;队列满仅会汰汰最旧字节, + * 正常停止等待流程下队列不应到达此深度。 */ g_ymodem_resp_read = (g_ymodem_resp_read + 1) % ELOAD_YMODEM_RESP_DEPTH; g_ymodem_resp_count--; } diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.c b/apps/chrg/applications/utils/chrg_ymodem_proxy.c new file mode 100644 index 0000000..8a23cf7 --- /dev/null +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.c @@ -0,0 +1,540 @@ +#include +#include + +#include "chrg_def.h" +#include "chrg_thread.h" +#include "chrg_south.h" +#include "chrg_roll_sou.h" +#include "chrg_eload.h" +#include "chrg_ymodem_proxy.h" + +#define LOG_TAG "chrg.upgrade" +#include + +#define YMODEM_RX_FRAME_TIMEOUT_MS 1000U +#define YMODEM_SESSION_IDLE_MS 15000U +#define YMODEM_ENTER_TIMEOUT_MS 15000U +#define YMODEM_RESPONSE_TIMEOUT_MS 10000U + +typedef struct { + chrg_ymodem_proxy_state_t state; + rt_uint8_t ch; + rt_uint8_t expected_packet; + rt_uint8_t current_packet; + rt_uint8_t meta_ack_seen; + rt_uint8_t eot2_ack_seen; + rt_uint8_t deferred_c; + rt_uint16_t rx_len; + rt_uint16_t expected_len; + rt_tick_t rx_tick; + rt_tick_t response_tick; + rt_tick_t activity_tick; + rt_uint8_t rx_buf[YMODEM_STX_SIZE]; + rt_uint8_t north_pending[2]; + rt_uint8_t north_pending_len; +} chrg_ymodem_proxy_t; + +static chrg_ymodem_proxy_t g_proxy = { + .state = YMODEM_PROXY_IDLE, + .ch = 0xFFU +}; + +static const rt_uint8_t g_enter_frame[8] = + {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xF8, 0x6B}; +static const rt_uint8_t g_exit_frame[8] = + {ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0xF8, 0x2F}; +static const rt_uint8_t g_cancel_frame[2] = {YM_CAN, YM_CAN}; + +static const char *chrg_ymodem_proxy_state_name(chrg_ymodem_proxy_state_t state) +{ + switch (state) { + case YMODEM_PROXY_IDLE: return "IDLE"; + case YMODEM_PROXY_ENTERING: return "ENTERING"; + case YMODEM_PROXY_WAIT_C: return "WAIT_C"; + case YMODEM_PROXY_WAIT_META: return "WAIT_META"; + case YMODEM_PROXY_WAIT_META_RESP: return "META_RESP"; + case YMODEM_PROXY_WAIT_DATA: return "WAIT_DATA"; + case YMODEM_PROXY_WAIT_DATA_RESP: return "DATA_RESP"; + case YMODEM_PROXY_WAIT_EOT1_RESP: return "EOT1_RESP"; + case YMODEM_PROXY_WAIT_EOT2: return "EOT2"; + case YMODEM_PROXY_WAIT_EOT2_RESP: return "EOT2_RESP"; + case YMODEM_PROXY_WAIT_END_SOH: return "END_SOH"; + case YMODEM_PROXY_WAIT_END_ACK: return "END_ACK"; + case YMODEM_PROXY_TRANSFER_DONE: return "DONE"; + case YMODEM_PROXY_EXITING: return "EXITING"; + case YMODEM_PROXY_VERIFY: return "VERIFY"; + default: return "ABORT"; + } +} + +static void chrg_ymodem_proxy_reset(void) +{ + rt_memset(&g_proxy, 0, sizeof(g_proxy)); + g_proxy.state = YMODEM_PROXY_IDLE; + g_proxy.ch = 0xFFU; +} + +static void chrg_ymodem_proxy_session_stop(void) +{ + struct chrg_thread_t *thread = &chrgthr[IDX_THR_ROLL_SOU]; + struct chrg_rollsou_t *roll = &chrgrollsou; + + if (thread->mutex != RT_NULL) { + rt_mutex_take(thread->mutex, RT_WAITING_FOREVER); + roll->mode = 0U; + roll->send_flag = 0U; + roll->leng = 0U; + roll->updata_result = 0; + roll->run = RUN_AUTO; + rt_mutex_release(thread->mutex); + } + chrg_eload_clear_ymodem_resp(); + if (chrgsouth.rb != RT_NULL) { + rt_ringbuffer_reset(chrgsouth.rb); + } + chrg_ymodem_proxy_reset(); +} + +static void chrg_ymodem_proxy_queue_north(rt_uint8_t first, + rt_uint8_t second, rt_uint8_t length) +{ + if ((length == 0U) || (g_proxy.north_pending_len != 0U)) { + return; + } + g_proxy.north_pending[0] = first; + g_proxy.north_pending[1] = second; + g_proxy.north_pending_len = length; +} + +static void chrg_ymodem_proxy_abort(const char *reason) +{ + if (g_proxy.state == YMODEM_PROXY_ABORT) { + return; + } + LOG_E("abort ch=%u state=%s reason=%s", g_proxy.ch + 1U, + chrg_ymodem_proxy_state_name(g_proxy.state), reason); + (void)chrg_send_sou_upadata_data(g_cancel_frame, sizeof(g_cancel_frame)); + /* 取消必须覆盖尚未发送的NAK,确保PC必然收到CAN CAN。 */ + g_proxy.north_pending[0] = YM_CAN; + g_proxy.north_pending[1] = YM_CAN; + g_proxy.north_pending_len = 2U; + g_proxy.state = YMODEM_PROXY_ABORT; + g_proxy.activity_tick = rt_tick_get(); +} + +static rt_uint16_t chrg_ymodem_frame_size(rt_uint8_t header) +{ + if (header == SOH) return YMODEM_SOH_SIZE; + if (header == STX) return YMODEM_STX_SIZE; + if ((header == EOT) || (header == YM_CAN)) return 1U; + return 0U; +} + +static rt_uint16_t chrg_ymodem_crc16(const rt_uint8_t *data, + rt_uint16_t length) +{ + rt_uint16_t crc = 0U; + rt_uint16_t i; + rt_uint8_t bit; + + for (i = 0U; i < length; i++) { + crc ^= (rt_uint16_t)data[i] << 8; + for (bit = 0U; bit < 8U; bit++) { + crc = (crc & 0x8000U) ? + (rt_uint16_t)((crc << 1) ^ 0x1021U) : + (rt_uint16_t)(crc << 1); + } + } + return crc; +} + +static rt_bool_t chrg_ymodem_frame_valid(const rt_uint8_t *frame, + rt_uint16_t length) +{ + rt_uint16_t data_len; + rt_uint16_t frame_crc; + + if ((frame == RT_NULL) || + (length != chrg_ymodem_frame_size(frame[0]))) { + return RT_FALSE; + } + if ((frame[0] == EOT) || (frame[0] == YM_CAN)) { + return RT_TRUE; + } + if ((rt_uint8_t)(frame[1] + frame[2]) != 0xFFU) { + return RT_FALSE; + } + data_len = (frame[0] == SOH) ? 128U : 1024U; + frame_crc = ((rt_uint16_t)frame[length - 2U] << 8) | + frame[length - 1U]; + return (chrg_ymodem_crc16(&frame[YMODEM_DATA_OFFSET], data_len) == + frame_crc) ? RT_TRUE : RT_FALSE; +} + +static rt_bool_t chrg_ymodem_metadata_valid(const rt_uint8_t *frame) +{ + const rt_uint8_t *data = &frame[YMODEM_DATA_OFFSET]; + rt_uint16_t name_len = 0U; + rt_uint16_t pos; + rt_bool_t digit_seen = RT_FALSE; + + if ((frame[0] != SOH) || (frame[1] != 0U)) return RT_FALSE; + while ((name_len < 128U) && (data[name_len] != 0U)) name_len++; + if ((name_len < 4U) || (name_len >= 127U) || + (data[name_len - 4U] != '.') || + ((data[name_len - 3U] | 0x20U) != 'u') || + ((data[name_len - 2U] | 0x20U) != 'p') || + ((data[name_len - 1U] | 0x20U) != 'k')) return RT_FALSE; + for (pos = name_len + 1U; pos < 128U && data[pos] != 0U; pos++) { + if ((data[pos] < '0') || (data[pos] > '9')) return RT_FALSE; + digit_seen = RT_TRUE; + } + return digit_seen; +} + +static rt_bool_t chrg_ymodem_end_soh_valid(const rt_uint8_t *frame) +{ + rt_uint16_t i; + if ((frame[0] != SOH) || (frame[1] != 0U)) return RT_FALSE; + for (i = 0U; i < 128U; i++) { + if (frame[YMODEM_DATA_OFFSET + i] != 0U) return RT_FALSE; + } + return RT_TRUE; +} + +static int chrg_ymodem_proxy_submit_frame(const rt_uint8_t *frame, + rt_uint16_t length, chrg_ymodem_proxy_state_t next_state) +{ + int ret = chrg_send_sou_upadata_data(frame, length); + if (ret != 0) return ret; + g_proxy.current_packet = (length > 1U) ? frame[1] : 0U; + g_proxy.state = next_state; + g_proxy.response_tick = rt_tick_get(); + g_proxy.activity_tick = g_proxy.response_tick; + LOG_I("south tx ch=%u state=%s type=%02x pkt=%u len=%u", + g_proxy.ch + 1U, chrg_ymodem_proxy_state_name(next_state), + frame[0], g_proxy.current_packet, length); + return 0; +} + +static int chrg_ymodem_proxy_prepare_channel(rt_uint8_t ch) +{ + struct chrg_thread_t *thread = &chrgthr[IDX_THR_ROLL_SOU]; + struct chrg_rollsou_t *roll = &chrgrollsou; + + if ((ch >= TOTAL_SOU_CHS) || (thread->mutex == RT_NULL)) return -1; + if (rt_mutex_take(thread->mutex, + rt_tick_from_millisecond(100U)) != RT_EOK) return -2; + roll->run = RUN_UPDATA; + roll->ch = ch; + roll->mode = 1U; + roll->send_flag = 0U; + roll->leng = 0U; + roll->updata_result = 0; + rt_mutex_release(thread->mutex); + rt_thread_mdelay(25U); + chrg_eload_clear_ymodem_resp(); + if (chrgsouth.rb != RT_NULL) rt_ringbuffer_reset(chrgsouth.rb); + return 0; +} + +int chrg_ymodem_proxy_enter(rt_uint8_t ch) +{ + rt_tick_t start; + rt_uint8_t response; + + if (g_proxy.state != YMODEM_PROXY_IDLE) return -1; + if (chrg_ymodem_proxy_prepare_channel(ch) != 0) return -2; + chrg_ymodem_proxy_reset(); + g_proxy.ch = ch; + g_proxy.state = YMODEM_PROXY_ENTERING; + g_proxy.activity_tick = rt_tick_get(); + if (chrg_send_sou_upadata_data(g_enter_frame, sizeof(g_enter_frame)) != 0) { + chrg_ymodem_proxy_session_stop(); + return -3; + } + start = rt_tick_get(); + while ((rt_tick_t)(rt_tick_get() - start) < + rt_tick_from_millisecond(YMODEM_ENTER_TIMEOUT_MS)) { + response = chrg_eload_get_ymodem_resp(); + if (response == ACK) { + g_proxy.state = YMODEM_PROXY_WAIT_C; + g_proxy.activity_tick = rt_tick_get(); + return 0; + } + if (response == ACK_C) g_proxy.deferred_c = 1U; + if ((response == NAK) || (response == YM_CAN)) break; + rt_thread_mdelay(1U); + } + chrg_ymodem_proxy_session_stop(); + return -4; +} + +int chrg_ymodem_proxy_exit(rt_uint8_t ch) +{ + rt_tick_t start; + rt_uint8_t response; + + if ((g_proxy.state != YMODEM_PROXY_TRANSFER_DONE) || + (ch != g_proxy.ch)) return -1; + g_proxy.state = YMODEM_PROXY_EXITING; + chrg_eload_clear_ymodem_resp(); + if (chrg_send_sou_upadata_data(g_exit_frame, sizeof(g_exit_frame)) != 0) + return -2; + start = rt_tick_get(); + while ((rt_tick_t)(rt_tick_get() - start) < + rt_tick_from_millisecond(YMODEM_RESPONSE_TIMEOUT_MS)) { + response = chrg_eload_get_ymodem_resp(); + if (response == ACK) { + chrg_roll_sou_version_invalidate(ch); + chrg_ymodem_proxy_session_stop(); + return 0; + } + if ((response == NAK) || (response == YM_CAN)) break; + rt_thread_mdelay(1U); + } + chrg_ymodem_proxy_session_stop(); + return -3; +} + +static int chrg_ymodem_proxy_handle_frame(const rt_uint8_t *frame, + rt_uint16_t length) +{ + if (!chrg_ymodem_frame_valid(frame, length)) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + return -1; + } + if (frame[0] == YM_CAN) { + chrg_ymodem_proxy_abort("north CAN"); + return 0; + } + switch (g_proxy.state) { + case YMODEM_PROXY_WAIT_META: + if (!chrg_ymodem_metadata_valid(frame)) break; + g_proxy.meta_ack_seen = 0U; + return chrg_ymodem_proxy_submit_frame(frame, length, + YMODEM_PROXY_WAIT_META_RESP); + case YMODEM_PROXY_WAIT_DATA: + if (frame[0] == EOT) { + return chrg_ymodem_proxy_submit_frame(frame, length, + YMODEM_PROXY_WAIT_EOT1_RESP); + } + if ((frame[1] != g_proxy.expected_packet) || + ((g_proxy.expected_packet == 1U) && + ((frame[0] != STX) || (length != YMODEM_STX_SIZE)))) break; + return chrg_ymodem_proxy_submit_frame(frame, length, + YMODEM_PROXY_WAIT_DATA_RESP); + case YMODEM_PROXY_WAIT_EOT2: + if (frame[0] != EOT) break; + g_proxy.eot2_ack_seen = 0U; + return chrg_ymodem_proxy_submit_frame(frame, length, + YMODEM_PROXY_WAIT_EOT2_RESP); + case YMODEM_PROXY_WAIT_END_SOH: + if (!chrg_ymodem_end_soh_valid(frame)) break; + return chrg_ymodem_proxy_submit_frame(frame, length, + YMODEM_PROXY_WAIT_END_ACK); + default: + break; + } + LOG_W("north frame rejected ch=%u state=%s type=%02x pkt=%u len=%u expected=%u", + g_proxy.ch + 1U, chrg_ymodem_proxy_state_name(g_proxy.state), + frame[0], (length > 1U) ? frame[1] : 0U, length, + g_proxy.expected_packet); + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + return -2; +} + +int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, + rt_uint16_t length) +{ + rt_uint16_t offset = 0U; + rt_uint16_t copy_len; + + if ((data == RT_NULL) || (length == 0U) || + (g_proxy.state == YMODEM_PROXY_IDLE)) return -1; + while (offset < length) { + /* CAN在任意会话阶段都必须抢占普通停止等待限制。 */ + if ((g_proxy.expected_len == 0U) && + (data[offset] == YM_CAN)) { + chrg_ymodem_proxy_abort("north CAN"); + return 0; + } + if ((g_proxy.state == YMODEM_PROXY_WAIT_META_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_DATA_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_EOT1_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_EOT2_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_END_ACK)) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + return -2; + } + if (g_proxy.expected_len == 0U) { + g_proxy.expected_len = chrg_ymodem_frame_size(data[offset]); + if (g_proxy.expected_len == 0U) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + return -3; + } + g_proxy.rx_len = 0U; + g_proxy.rx_tick = rt_tick_get(); + } + copy_len = g_proxy.expected_len - g_proxy.rx_len; + if (copy_len > (length - offset)) copy_len = length - offset; + rt_memcpy(&g_proxy.rx_buf[g_proxy.rx_len], &data[offset], copy_len); + g_proxy.rx_len += copy_len; + offset += copy_len; + g_proxy.activity_tick = rt_tick_get(); + if (g_proxy.rx_len == g_proxy.expected_len) { + int ret = chrg_ymodem_proxy_handle_frame(g_proxy.rx_buf, + g_proxy.rx_len); + g_proxy.rx_len = 0U; + g_proxy.expected_len = 0U; + g_proxy.rx_tick = 0U; + if (ret != 0) return ret; + } + } + return 0; +} + +static void chrg_ymodem_proxy_handle_south(rt_uint8_t response) +{ + g_proxy.activity_tick = rt_tick_get(); + LOG_I("south rx ch=%u state=%s resp=%02x pkt=%u", + g_proxy.ch + 1U, chrg_ymodem_proxy_state_name(g_proxy.state), + response, g_proxy.current_packet); + if (response == YM_CAN) { + chrg_ymodem_proxy_abort("south CAN"); + return; + } + switch (g_proxy.state) { + case YMODEM_PROXY_WAIT_C: + if (response == ACK_C) { + chrg_ymodem_proxy_queue_north(ACK_C, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_META; + } + break; + case YMODEM_PROXY_WAIT_META_RESP: + if (response == ACK) { + g_proxy.meta_ack_seen = 1U; + chrg_ymodem_proxy_queue_north(ACK, 0U, 1U); + } else if ((response == ACK_C) && g_proxy.meta_ack_seen) { + chrg_ymodem_proxy_queue_north(ACK_C, 0U, 1U); + g_proxy.expected_packet = 1U; + g_proxy.state = YMODEM_PROXY_WAIT_DATA; + } else if (response == NAK) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_META; + } + break; + case YMODEM_PROXY_WAIT_DATA_RESP: + if (response == ACK) { + chrg_ymodem_proxy_queue_north(ACK, 0U, 1U); + g_proxy.expected_packet++; + g_proxy.state = YMODEM_PROXY_WAIT_DATA; + } else if (response == NAK) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_DATA; + } + break; + case YMODEM_PROXY_WAIT_EOT1_RESP: + if (response == NAK) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_EOT2; + } else if (response == ACK) { + chrg_ymodem_proxy_abort("first EOT returned ACK"); + } + break; + case YMODEM_PROXY_WAIT_EOT2_RESP: + if (response == ACK) { + g_proxy.eot2_ack_seen = 1U; + chrg_ymodem_proxy_queue_north(ACK, 0U, 1U); + } else if ((response == ACK_C) && g_proxy.eot2_ack_seen) { + chrg_ymodem_proxy_queue_north(ACK_C, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_END_SOH; + } else if (response == NAK) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_EOT2; + } + break; + case YMODEM_PROXY_WAIT_END_ACK: + if (response == ACK) { + chrg_ymodem_proxy_queue_north(ACK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_TRANSFER_DONE; + } else if (response == NAK) { + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + g_proxy.state = YMODEM_PROXY_WAIT_END_SOH; + } + break; + default: + break; + } +} + +rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, + rt_uint16_t capacity) +{ + rt_tick_t now; + rt_uint8_t response; + rt_uint16_t length; + + if ((output == RT_NULL) || (capacity == 0U) || + (g_proxy.state == YMODEM_PROXY_IDLE)) return 0U; + now = rt_tick_get(); + /* CAN已交给南向发送线程且北向CAN已发出后再恢复普通轮询, + * 禁止session_stop提前清掉尚未真正发送的南向CAN。 */ + if ((g_proxy.state == YMODEM_PROXY_ABORT) && + (g_proxy.north_pending_len == 0U) && + (chrgrollsou.send_flag == 0U)) { + chrg_ymodem_proxy_session_stop(); + return 0U; + } + if (g_proxy.deferred_c && (g_proxy.state == YMODEM_PROXY_WAIT_C)) { + g_proxy.deferred_c = 0U; + chrg_ymodem_proxy_handle_south(ACK_C); + } + response = chrg_eload_get_ymodem_resp(); + if (response != 0U) chrg_ymodem_proxy_handle_south(response); + if ((g_proxy.expected_len != 0U) && (g_proxy.rx_tick != 0U) && + ((rt_tick_t)(now - g_proxy.rx_tick) >= + rt_tick_from_millisecond(YMODEM_RX_FRAME_TIMEOUT_MS))) { + g_proxy.rx_len = 0U; + g_proxy.expected_len = 0U; + g_proxy.rx_tick = 0U; + chrg_ymodem_proxy_queue_north(NAK, 0U, 1U); + } + if ((g_proxy.response_tick != 0U) && + ((g_proxy.state == YMODEM_PROXY_WAIT_META_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_DATA_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_EOT1_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_EOT2_RESP) || + (g_proxy.state == YMODEM_PROXY_WAIT_END_ACK)) && + ((rt_tick_t)(now - g_proxy.response_tick) >= + rt_tick_from_millisecond(YMODEM_RESPONSE_TIMEOUT_MS))) { + chrg_ymodem_proxy_abort("south response timeout"); + } + if ((g_proxy.state != YMODEM_PROXY_ABORT) && + (g_proxy.activity_tick != 0U) && + ((rt_tick_t)(now - g_proxy.activity_tick) >= + rt_tick_from_millisecond(YMODEM_SESSION_IDLE_MS))) { + chrg_ymodem_proxy_abort("session idle timeout"); + } + if (g_proxy.north_pending_len == 0U) return 0U; + length = g_proxy.north_pending_len; + if (length > capacity) return 0U; + rt_memcpy(output, g_proxy.north_pending, length); + g_proxy.north_pending_len = 0U; + return length; +} + +rt_bool_t chrg_ymodem_proxy_active(void) +{ + return (g_proxy.state != YMODEM_PROXY_IDLE) ? RT_TRUE : RT_FALSE; +} + +rt_uint8_t chrg_ymodem_proxy_channel(void) +{ + return g_proxy.ch; +} + +chrg_ymodem_proxy_state_t chrg_ymodem_proxy_state(void) +{ + return g_proxy.state; +} diff --git a/apps/chrg/applications/utils/chrg_ymodem_proxy.h b/apps/chrg/applications/utils/chrg_ymodem_proxy.h new file mode 100644 index 0000000..8002b8a --- /dev/null +++ b/apps/chrg/applications/utils/chrg_ymodem_proxy.h @@ -0,0 +1,35 @@ +#ifndef __CHRG_YMODEM_PROXY_H__ +#define __CHRG_YMODEM_PROXY_H__ + +#include + +typedef enum { + YMODEM_PROXY_IDLE = 0, + YMODEM_PROXY_ENTERING, + YMODEM_PROXY_WAIT_C, + YMODEM_PROXY_WAIT_META, + YMODEM_PROXY_WAIT_META_RESP, + YMODEM_PROXY_WAIT_DATA, + YMODEM_PROXY_WAIT_DATA_RESP, + YMODEM_PROXY_WAIT_EOT1_RESP, + YMODEM_PROXY_WAIT_EOT2, + YMODEM_PROXY_WAIT_EOT2_RESP, + YMODEM_PROXY_WAIT_END_SOH, + YMODEM_PROXY_WAIT_END_ACK, + YMODEM_PROXY_TRANSFER_DONE, + YMODEM_PROXY_EXITING, + YMODEM_PROXY_VERIFY, + YMODEM_PROXY_ABORT +} chrg_ymodem_proxy_state_t; + +extern int chrg_ymodem_proxy_enter(rt_uint8_t ch); +extern int chrg_ymodem_proxy_exit(rt_uint8_t ch); +extern int chrg_ymodem_proxy_feed_north(const rt_uint8_t *data, + rt_uint16_t length); +extern rt_uint16_t chrg_ymodem_proxy_poll(rt_uint8_t *output, + rt_uint16_t capacity); +extern rt_bool_t chrg_ymodem_proxy_active(void); +extern rt_uint8_t chrg_ymodem_proxy_channel(void); +extern chrg_ymodem_proxy_state_t chrg_ymodem_proxy_state(void); + +#endif diff --git a/apps/chrg/chrg.uvprojx b/apps/chrg/chrg.uvprojx index 6090132..03eee58 100644 --- a/apps/chrg/chrg.uvprojx +++ b/apps/chrg/chrg.uvprojx @@ -503,6 +503,11 @@ 1 applications\utils\chrg_eload.c + + chrg_ymodem_proxy.c + 1 + applications\utils\chrg_ymodem_proxy.c + chrg_utils.c 1