/** * @file lwip_ftp_upgrade.c * @brief 基于 LwIP socket 的 FTP 固件升级状态机实现(网线远程升级) */ #include "publicdata/public_define.h" #include "plat_comm/lwip_module/lwip_ftp_upgrade.h" #include "plat_comm/lwip_module/lwip_module.h" #include "plat_comm/impl/bs_public_impl.h" /* T_FTP_UPDATE_INFO / t_ftp_update_info */ #include "plat_comm/plat_comm_task.h" /* Plat_Comm_LOG */ #include "bsp_include.h" #include "main.h" #include #include #include #include #include "FreeRTOS.h" #include "task.h" #include "lwip/sockets.h" /* fd_set, lwip_select, SOL_SOCKET, SO_ERROR */ #ifndef EINPROGRESS #define EINPROGRESS 115 #endif #if LWIP_FTP_UPGRADE_ENABLE /* FTP 协议常量 */ #define FTP_CTRL_DEFAULT_PORT 21 #define FTP_CTRL_RX_BUF_SIZE 512 #define FTP_LINE_MAX 256 #define FTP_DATA_RX_BUF_SIZE 1460 /* 一帧以太网接近 MTU */ #define FTP_STEP_FAIL_MAX 3 /* 每个状态连续失败此次数后才进入 FAIL */ /* ====================== FTP 升级状态机定义 ====================== */ typedef enum { LWIP_FTP_STEP_IDLE = 0, /* 空闲,未开始 */ LWIP_FTP_STEP_CONNECT, /* 发起控制连接(非阻塞) */ LWIP_FTP_STEP_WAIT_CTRL_CONNECT, /* 等待控制连接完成(select,不阻塞任务) */ LWIP_FTP_STEP_WAIT_WELCOME, /* 等待 220 欢迎语 */ LWIP_FTP_STEP_SEND_USER, /* 发送 USER */ LWIP_FTP_STEP_WAIT_USER, /* 等待 USER 响应 */ LWIP_FTP_STEP_SEND_PASS, /* 发送 PASS */ LWIP_FTP_STEP_WAIT_PASS, /* 等待 PASS 响应 */ LWIP_FTP_STEP_SEND_TYPE, /* 发送 TYPE I */ LWIP_FTP_STEP_WAIT_TYPE, /* 等待 TYPE 响应 */ LWIP_FTP_STEP_SEND_CWD, /* 发送 CWD(可选) */ LWIP_FTP_STEP_WAIT_CWD, /* 等待 CWD 响应 */ LWIP_FTP_STEP_SEND_SIZE, /* 发送 SIZE */ LWIP_FTP_STEP_WAIT_SIZE, /* 等待 SIZE 响应,获取文件大小 */ LWIP_FTP_STEP_PREPARE_FLASH, /* 擦除升级区 + 写 IDLE 头 */ LWIP_FTP_STEP_SEND_PASV, /* 发送 PASV */ LWIP_FTP_STEP_WAIT_PASV, /* 等待 227,解析数据地址 */ LWIP_FTP_STEP_CONNECT_DATA, /* 发起数据连接(非阻塞) */ LWIP_FTP_STEP_WAIT_DATA_CONNECT, /* 等待数据连接完成(select) */ LWIP_FTP_STEP_SEND_RETR, /* 发送 RETR */ LWIP_FTP_STEP_WAIT_RETR, /* 等待 150/125 等开始传输响应 */ LWIP_FTP_STEP_RECV_DATA, /* 分包接收数据 + 每包 1024 写 Flash */ LWIP_FTP_STEP_SEND_QUIT, /* 发送 QUIT,关闭连接 */ LWIP_FTP_STEP_DONE, /* 升级完成 */ LWIP_FTP_STEP_FAIL /* 升级失败 */ } E_LWIP_FTP_STEP; typedef struct { E_LWIP_FTP_STEP step; /* 当前步骤 */ int ctrl_sock; /* 控制通道 socket */ int data_sock; /* 数据通道 socket */ /* PASV 解析得到的数据通道地址 */ struct in_addr data_ip; uint16_t data_port; /* 文件信息 */ uint32_t file_size; /* 升级文件总大小 */ uint32_t downloaded; /* 已下载字节数 */ /* PACKET_SIZE=1024 的分包写入状态 */ uint32_t packet_index; /* 当前包索引(从 0 开始) */ uint8_t *packet_buf; /* 动态申请的 1024 字节缓存 */ uint32_t packet_used; /* 当前包已写入的字节数 */ /* 当前控制命令等待截止时间(用于 1 秒超时控制) */ uint32_t step_deadline_ms; uint8_t step_fail_cnt; /* 当前步骤连续失败次数,满 FTP_STEP_FAIL_MAX 次才进入 FAIL */ int last_err; /* 最近一次失败时的错误码(errno 或 SO_ERROR),便于日志 */ uint8_t fail_stage; /* 控制连接失败阶段:1=resolve 2=socket 3=nonblock 4=connect */ } LWIP_FTP_UPGRADE_CTX; static LWIP_FTP_UPGRADE_CTX s_ctx; /* ====================== 工具函数 ====================== */ static uint32_t ftp_now_ms(void) { return (uint32_t)HAL_GetTick(); } static void ftp_close_sock(int *psock) { if (psock && *psock >= 0) { lwip_tcp_close(*psock); *psock = -1; } } /* 发送完整命令字符串(一次性) */ static int ftp_send_all(int sock, const char *s) { size_t left = strlen(s); const char *p = s; while (left > 0) { int n = lwip_tcp_send(sock, p, left); if (n <= 0) return -1; p += (size_t)n; left -= (size_t)n; } return 0; } /* 发送命令(不等待响应) */ static int ftp_send_cmd(int sock, const char *fmt, const char *arg) { char buf[256]; if (arg) { snprintf(buf, sizeof(buf), fmt, arg); } else { snprintf(buf, sizeof(buf), "%s", fmt); } return ftp_send_all(sock, buf); } /* 解析 PASV 响应,得到 data_ip/data_port */ static int ftp_parse_pasv(const char *msg, struct in_addr *out_ip, uint16_t *out_port) { const char *p = strchr(msg, '('); const char *q = strchr(msg, ')'); if (!p || !q || q <= p) return -1; int h1,h2,h3,h4,p1,p2; if (sscanf(p + 1, "%d,%d,%d,%d,%d,%d", &h1,&h2,&h3,&h4,&p1,&p2) != 6) return -1; if (h1<0||h1>255||h2<0||h2>255||h3<0||h3>255||h4<0||h4>255) return -1; if (p1<0||p1>255||p2<0||p2>255) return -1; char ip_str[32]; snprintf(ip_str, sizeof(ip_str), "%d.%d.%d.%d", h1,h2,h3,h4); unsigned long addr = inet_addr(ip_str); if (addr == INADDR_NONE) return -1; out_ip->s_addr = addr; *out_port = (uint16_t)((p1 << 8) | p2); return 0; } /* 单次尝试接收并解析一条 FTP 响应(非阻塞,只 recv 一次) */ static int ftp_recv_reply_once(int sock, int *out_code, char *out_msg, size_t out_msg_sz) { char buf[FTP_CTRL_RX_BUF_SIZE]; char line[FTP_LINE_MAX]; int code = -1; if (out_code) *out_code = -1; if (out_msg && out_msg_sz) out_msg[0] = '\0'; int n = lwip_tcp_recv(sock, buf, sizeof(buf)); if (n > 0) { /* 先截成一行(找到第一个 \r 或 \n) */ int i; for (i = 0; i < n; i++) { if (buf[i] == '\r' || buf[i] == '\n') { break; } } if (i >= (int)sizeof(line)) i = (int)sizeof(line) - 1; memcpy(line, buf, (size_t)i); line[i] = '\0'; /* 解析前三位数字为响应码 */ size_t ln = strlen(line); if (ln >= 3 && line[0] >= '0' && line[0] <= '9' && line[1] >= '0' && line[1] <= '9' && line[2] >= '0' && line[2] <= '9') { code = (line[0]-'0')*100 + (line[1]-'0')*10 + (line[2]-'0'); if (out_msg && out_msg_sz) { strncpy(out_msg, line, out_msg_sz - 1); out_msg[out_msg_sz - 1] = '\0'; } if (out_code) *out_code = code; return 1; /* 成功解析到一条响应 */ } /* 收到但未解析出合法 code,当作暂时无效数据 */ return 0; } if (n == 0) { return -1; /* 对端关闭 */ } if (errno != 0 && errno != EWOULDBLOCK && errno != EAGAIN) { return -1; /* 真正错误 */ } return 0; /* 暂无数据 */ } /* 建立控制连接(非阻塞 connect:0=成功,1=进行中需等 WAIT_CTRL_CONNECT,-1=失败) */ static int ftp_connect_ctrl(LWIP_FTP_UPGRADE_CTX *ctx, const char *host, uint16_t port) { struct in_addr ip; ctx->last_err = 0; ctx->fail_stage = 0; if (lwip_resolve_host(host, &ip) != 0) { ctx->last_err = errno; ctx->fail_stage = 1; /* resolve */ return -1; } int sock = lwip_socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { ctx->last_err = errno; ctx->fail_stage = 2; /* socket */ return -1; } if (lwip_set_nonblock(sock) != 0) { ctx->last_err = errno; ctx->fail_stage = 3; /* nonblock */ lwip_close(sock); return -1; } struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = ip.s_addr; sa.sin_port = htons(port); if (lwip_connect(sock, (struct sockaddr*)&sa, sizeof(sa)) == 0) { ctx->ctrl_sock = sock; return 0; /* 立即连接成功(本机或极快网络) */ } if (errno == EINPROGRESS) { ctx->ctrl_sock = sock; return 1; /* 进行中,需在 WAIT_CTRL_CONNECT 中 select 等待 */ } /* connect 同步失败:优先用 errno;若为 0 则用 SO_ERROR(部分 LwIP 不设 errno) */ ctx->fail_stage = 4; /* connect */ { int err = errno; if (err == 0) { int so_err = 0; socklen_t len = (socklen_t)sizeof(so_err); if (lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_err, &len) == 0) err = so_err; } ctx->last_err = err; lwip_close(sock); } return -1; } /* 轮询非阻塞 connect 是否完成(不阻塞:select 超时 0 或很短),0=未就绪,1=已连接,-1=失败/超时 */ static int ftp_poll_connect_done(int sock, uint32_t deadline_ms) { fd_set wfds; struct timeval tv; int err = 0; socklen_t len = (socklen_t)sizeof(err); FD_ZERO(&wfds); FD_SET(sock, &wfds); tv.tv_sec = 0; tv.tv_usec = 50000; /* 最多等 50ms,避免卡死任务 */ int n = lwip_select(sock + 1, NULL, &wfds, NULL, &tv); if (n > 0 && FD_ISSET(sock, &wfds)) { if (lwip_getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) != 0 || err != 0) { return -1; } return 1; /* 已连接 */ } if (ftp_now_ms() >= deadline_ms) { return -1; /* 超时 */ } return 0; /* 未就绪,下次周期再试 */ } /* 建立数据连接 socket(非阻塞:0=成功,1=进行中,-1=失败) */ static int ftp_connect_data(LWIP_FTP_UPGRADE_CTX *ctx) { int sock = lwip_socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) return -1; if (lwip_set_nonblock(sock) != 0) { lwip_close(sock); return -1; } struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = ctx->data_ip.s_addr; sa.sin_port = htons(ctx->data_port); if (lwip_connect(sock, (struct sockaddr*)&sa, sizeof(sa)) == 0) { ctx->data_sock = sock; return 0; } if (errno == EINPROGRESS) { ctx->data_sock = sock; return 1; } lwip_close(sock); return -1; } /* 擦除升级区 + 写 IDLE 头 */ static int ftp_prepare_flash(LWIP_FTP_UPGRADE_CTX *ctx) { if (ctx->file_size == 0) return -1; if (upgrade_erase_program_area() != 0) { return -1; } upgrade_header_t header; memset(&header, 0, sizeof(header)); header.upgrade_flag = UPGRADE_FLAG_IDLE; header.file_size = ctx->file_size; header.packet_count = (ctx->file_size + PACKET_SIZE - 1) / PACKET_SIZE; header.crc32 = 0; header.timestamp = 0; if (upgrade_success_update_header(&header) != 0) { return -1; } ctx->packet_index = 0; ctx->packet_used = 0; ctx->downloaded = 0; return 0; } /* 写一包(<=1024 字节)到升级区 */ static int ftp_flash_write_one_packet(uint32_t index, const uint8_t *data, uint32_t len) { return upgrade_write_packet(index, data, len); } /* 接收数据通道数据,按 1024 分包写 Flash;本函数只调一次,读少量数据即可 */ static int ftp_recv_data_step(LWIP_FTP_UPGRADE_CTX *ctx) { if (ctx->data_sock < 0) return -1; uint8_t buf[FTP_DATA_RX_BUF_SIZE]; int n = lwip_tcp_recv(ctx->data_sock, buf, sizeof(buf)); if (n > 0) { size_t pos = 0; while (pos < (size_t)n) { uint32_t space = PACKET_SIZE - ctx->packet_used; uint32_t take = (uint32_t)(((size_t)n - pos) > space ? space : ((size_t)n - pos)); memcpy(&ctx->packet_buf[ctx->packet_used], &buf[pos], take); ctx->packet_used += take; pos += take; ctx->downloaded += take; /* 接收进度打印 */ if (ctx->file_size > 0) { uint32_t percent = (ctx->downloaded * 100U) / ctx->file_size; Plat_Comm_LOG("lwip ftp: recv %lu/%lu (%u%%)\r\n", (unsigned long)ctx->downloaded, (unsigned long)ctx->file_size, (unsigned)percent); } if (ctx->packet_used == PACKET_SIZE) { if (ftp_flash_write_one_packet(ctx->packet_index, ctx->packet_buf, ctx->packet_used) != 0) { return -1; } ctx->packet_index++; ctx->packet_used = 0; } } return 0; /* 还有后续数据,下一次 step 再收 */ } if (n == 0) { /* 数据通道关闭:刷写最后一包(不足 1024) */ if (ctx->packet_used > 0) { if (ftp_flash_write_one_packet(ctx->packet_index, ctx->packet_buf, ctx->packet_used) != 0) { return -1; } ctx->packet_index++; ctx->packet_used = 0; } ftp_close_sock(&ctx->data_sock); return 1; /* 表示数据接收完毕 */ } if (errno != 0 && errno != EWOULDBLOCK && errno != EAGAIN) { return -1; } return 0; /* 暂无数据 */ } /* 写完成标志:NEED_UP */ static int ftp_flash_finish(LWIP_FTP_UPGRADE_CTX *ctx) { if (ctx->file_size == 0) return -1; upgrade_header_t header; memset(&header, 0, sizeof(header)); header.upgrade_flag = UPGRADE_FLAG_NEED_UP; header.file_size = ctx->file_size; header.packet_count = (ctx->file_size + PACKET_SIZE - 1) / PACKET_SIZE; header.crc32 = 0; header.timestamp = 0; if (upgrade_success_update_header(&header) != 0) { return -1; } return 0; } /* ====================== 对外接口实现 ====================== */ void lwip_ftp_upgrade_init_from_tftp(void) { uint8_t *old_buf = s_ctx.packet_buf; /* 保留之前的动态缓存指针(如有) */ memset(&s_ctx, 0, sizeof(s_ctx)); s_ctx.ctrl_sock = -1; s_ctx.data_sock = -1; s_ctx.packet_buf = old_buf; s_ctx.step = LWIP_FTP_STEP_CONNECT; s_ctx.step_fail_cnt = 0; s_ctx.file_size = 0; s_ctx.downloaded = 0; s_ctx.packet_index = 0; s_ctx.packet_used = 0; s_ctx.step_deadline_ms = 0; /* 使用 FreeRTOS 堆动态申请 1024 字节缓存(仅申请一次,后续重复使用) */ if (s_ctx.packet_buf == NULL) { s_ctx.packet_buf = (uint8_t *)pvPortMalloc(PACKET_SIZE); if (s_ctx.packet_buf == NULL) { Plat_Comm_LOG("lwip ftp: pvPortMalloc packet_buf fail\r\n"); s_ctx.step = LWIP_FTP_STEP_FAIL; return; } } Plat_Comm_LOG("lwip ftp: init from t_ftp_update_info, host=%s, file=%s\r\n", (char *)t_ftp_update_info.u8_update_server_addr, (char *)t_ftp_update_info.u8_file_name); } int lwip_ftp_upgrade_step(void) { switch (s_ctx.step) { case LWIP_FTP_STEP_IDLE: return 0; case LWIP_FTP_STEP_CONNECT: { /* 发起控制连接(非阻塞,避免 connect 卡死任务) */ const char *host = (const char *)t_ftp_update_info.u8_update_server_addr; uint16_t port = (t_ftp_update_info.u16_update_server_port == 0) ? FTP_CTRL_DEFAULT_PORT : t_ftp_update_info.u16_update_server_port; int cr = ftp_connect_ctrl(&s_ctx, host, port); if (cr < 0) { Plat_Comm_LOG("lwip ftp: connect ctrl fail stage=%u err=%d (retry %u/%u)\r\n", (unsigned)s_ctx.fail_stage, s_ctx.last_err, (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; if (cr == 0) { s_ctx.step = LWIP_FTP_STEP_WAIT_WELCOME; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } else { s_ctx.step = LWIP_FTP_STEP_WAIT_CTRL_CONNECT; s_ctx.step_deadline_ms = ftp_now_ms() + 15000; /* 控制连接最多等 15 秒 */ } break; } case LWIP_FTP_STEP_WAIT_CTRL_CONNECT: { int r = ftp_poll_connect_done(s_ctx.ctrl_sock, s_ctx.step_deadline_ms); if (r == 1) { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_WELCOME; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } else if (r < 0) { Plat_Comm_LOG("lwip ftp: ctrl connect timeout or error (retry %u/%u)\r\n", (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 15000; } } break; } case LWIP_FTP_STEP_WAIT_WELCOME: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { Plat_Comm_LOG("lwip ftp: welcome error r=%d (retry %u/%u)\r\n", r, (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code < 200 || code >= 400) { Plat_Comm_LOG("lwip ftp: welcome fail code=%d msg=%s (retry %u/%u)\r\n", code, msg, (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_USER; } } else { /* r == 0, 暂无完整响应 */ if (ftp_now_ms() >= s_ctx.step_deadline_ms) { Plat_Comm_LOG("lwip ftp: welcome timeout (retry %u/%u)\r\n", (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_SEND_USER: { const char *user = (const char *)t_ftp_update_info.u8_user; if (user == NULL || user[0] == 0) user = "anonymous"; if (ftp_send_cmd(s_ctx.ctrl_sock, "USER %s\r\n", user) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_USER; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* USER 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_USER: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code >= 400) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (code == 331) { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_PASS; } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_TYPE; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_SEND_PASS: { const char *pass = (const char *)t_ftp_update_info.u8_pwd; const char *user = (const char *)t_ftp_update_info.u8_user; if (user == NULL || user[0] == 0) { pass = "anonymous@"; } else if (pass == NULL) { pass = ""; } if (ftp_send_cmd(s_ctx.ctrl_sock, "PASS %s\r\n", pass) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_PASS; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* PASS 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_PASS: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code >= 400) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_TYPE; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_SEND_TYPE: { if (ftp_send_cmd(s_ctx.ctrl_sock, "TYPE I\r\n", NULL) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_TYPE; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* TYPE 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_TYPE: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code >= 400) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (t_ftp_update_info.u8_path[0] != 0) { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_CWD; } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_SIZE; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_SEND_CWD: { const char *path = (const char *)t_ftp_update_info.u8_path; if (ftp_send_cmd(s_ctx.ctrl_sock, "CWD %s\r\n", path) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_CWD; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* CWD 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_CWD: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code >= 400) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_SIZE; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_SEND_SIZE: { const char *file = (const char *)t_ftp_update_info.u8_file_name; if (ftp_send_cmd(s_ctx.ctrl_sock, "SIZE %s\r\n", file) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_SIZE; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* SIZE 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_SIZE: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { Plat_Comm_LOG("lwip ftp: SIZE error r=%d (retry %u/%u)\r\n", r, (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code != 213) { Plat_Comm_LOG("lwip ftp: SIZE fail code=%d msg=%s (retry %u/%u)\r\n", code, msg, (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { unsigned long sz = 0; if (sscanf(msg, "213 %lu", &sz) != 1 || sz == 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.file_size = (uint32_t)sz; t_ftp_update_info.u32_file_size = s_ctx.file_size; s_ctx.step = LWIP_FTP_STEP_PREPARE_FLASH; } } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { Plat_Comm_LOG("lwip ftp: SIZE timeout (retry %u/%u)\r\n", (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_PREPARE_FLASH: { if (ftp_prepare_flash(&s_ctx) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_PASV; break; } case LWIP_FTP_STEP_SEND_PASV: { if (ftp_send_cmd(s_ctx.ctrl_sock, "PASV\r\n", NULL) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_PASV; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* PASV 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_PASV: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code != 227) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (ftp_parse_pasv(msg, &s_ctx.data_ip, &s_ctx.data_port) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_CONNECT_DATA; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_CONNECT_DATA: { int dr = ftp_connect_data(&s_ctx); if (dr < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; if (dr == 0) { s_ctx.step = LWIP_FTP_STEP_SEND_RETR; } else { s_ctx.step = LWIP_FTP_STEP_WAIT_DATA_CONNECT; s_ctx.step_deadline_ms = ftp_now_ms() + 10000; /* 数据连接最多等 10 秒 */ } break; } case LWIP_FTP_STEP_WAIT_DATA_CONNECT: { int r = ftp_poll_connect_done(s_ctx.data_sock, s_ctx.step_deadline_ms); if (r == 1) { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_SEND_RETR; } else if (r < 0) { Plat_Comm_LOG("lwip ftp: data connect timeout or error (retry %u/%u)\r\n", (unsigned)(s_ctx.step_fail_cnt + 1), (unsigned)FTP_STEP_FAIL_MAX); s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 10000; } } break; } case LWIP_FTP_STEP_SEND_RETR: { const char *file = (const char *)t_ftp_update_info.u8_file_name; if (ftp_send_cmd(s_ctx.ctrl_sock, "RETR %s\r\n", file) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } break; } s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_WAIT_RETR; s_ctx.step_deadline_ms = ftp_now_ms() + 1000; /* RETR 响应最长等待 1 秒 */ break; } case LWIP_FTP_STEP_WAIT_RETR: { int code; char msg[FTP_LINE_MAX]; int r = ftp_recv_reply_once(s_ctx.ctrl_sock, &code, msg, sizeof(msg)); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else if (r == 1) { if (code >= 400) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_RECV_DATA; } } else { if (ftp_now_ms() >= s_ctx.step_deadline_ms) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } else { s_ctx.step_deadline_ms = ftp_now_ms() + 1000; } } } break; } case LWIP_FTP_STEP_RECV_DATA: { int r = ftp_recv_data_step(&s_ctx); if (r < 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } } else if (r == 1) { s_ctx.step_fail_cnt = 0; /* 数据接收完成,等待 226 */ s_ctx.step = LWIP_FTP_STEP_SEND_QUIT; /* 简化:直接发送 QUIT,认为传输成功 */ } break; } case LWIP_FTP_STEP_SEND_QUIT: { /* 发送 QUIT,忽略响应 */ (void)ftp_send_cmd(s_ctx.ctrl_sock, "QUIT\r\n", NULL); ftp_close_sock(&s_ctx.ctrl_sock); if (ftp_flash_finish(&s_ctx) != 0) { s_ctx.step_fail_cnt++; if (s_ctx.step_fail_cnt >= FTP_STEP_FAIL_MAX) { s_ctx.step = LWIP_FTP_STEP_FAIL; } /* 否则保持 SEND_QUIT,下一周期会再次 QUIT 并再次 flash_finish(一般不重试) */ } else { s_ctx.step_fail_cnt = 0; s_ctx.step = LWIP_FTP_STEP_DONE; } v_sys_reboot(); //系统重启 默认此时未充电 break; } case LWIP_FTP_STEP_DONE: return 1; case LWIP_FTP_STEP_FAIL: ftp_close_sock(&s_ctx.ctrl_sock); ftp_close_sock(&s_ctx.data_sock); if (s_ctx.packet_buf != NULL) { vPortFree(s_ctx.packet_buf); s_ctx.packet_buf = NULL; } return -1; default: break; } return 0; } #else /* !LWIP_FTP_UPGRADE_ENABLE */ void lwip_ftp_upgrade_init_from_tftp(void) {} int lwip_ftp_upgrade_step(void) { return 0; } #endif /* LWIP_FTP_UPGRADE_ENABLE */