Files
CCU621M/app/plat_comm/lwip_module/lwip_ftp_upgrade.c
T

1030 lines
36 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.
/**
* @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 <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#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 */