337 lines
8.9 KiB
C
337 lines
8.9 KiB
C
|
|
|
|
#include "ymodem.h"
|
|
|
|
struct ymodem_info_t {
|
|
uint8_t isBusy; /* 正在处理主机数据的标志位 */
|
|
uint8_t enRecv; /* 使能接收主机的指令包 */
|
|
|
|
uint16_t rxLen; /* 接收到的数据长度 */
|
|
uint8_t *rxData; /* 协议解析的数据来源 */
|
|
|
|
uint8_t numPkt; /* 记录 YModem 协议的 packet number */
|
|
|
|
eYmFLOW flow; /* 记录协议的执行流程 */
|
|
|
|
union message_raw_t *msg; /* 接收主机数据包的缓存池,称为主机消息 */
|
|
|
|
struct tx_raw_t txPkt; /* 用于存放设备上发数据组包的部分参数 */
|
|
|
|
ymSend send; /* 数据发送接口 */
|
|
ymPrepareCallback prepare; /* 收到主机指令时的预备处理接口 */
|
|
ymReplyCallback reply; /* 正在执行指令时响应主机查询执行过程和结果的接口 */
|
|
};
|
|
|
|
static struct ymodem_info_t yM;
|
|
|
|
static struct drv_timer_t timerSendC; /* 用于定时向主机发送数据的定时器 */
|
|
|
|
|
|
|
|
static void timerSendCCallback (void *user_data)
|
|
{
|
|
static uint8_t c[1] = {YMODEM_C};
|
|
yM.send(c, 1, HAL_MAX_DELAY);
|
|
}
|
|
|
|
/**
|
|
* @brief 复位一些全局变量和标志信息
|
|
* @note
|
|
* @retval None
|
|
*/
|
|
static void ymodemReset (void)
|
|
{
|
|
yM.isBusy = 0;
|
|
yM.enRecv = 1;
|
|
yM.numPkt = 0;
|
|
yM.flow = YMODEM_FLOW_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief 对主机下发的心跳包的处理
|
|
* @note 对主机下发的数据帧进行回复, ymodem 没有心跳包,此处只是模拟
|
|
* @retval None
|
|
*/
|
|
static void ymodemHeartBeatProcess (void)
|
|
{
|
|
static eRESULT result;
|
|
|
|
yM.reply((eYmCMD)yM.msg->pkg.header, &result, NULL, NULL);
|
|
|
|
switch (result) {
|
|
case YM_RESULT_OK:
|
|
//yM.txPkt.response = YMODEM_ACK;
|
|
break;
|
|
|
|
case YM_RESULT_FAILED: {
|
|
/* 因前面已经加 1 ,此处是由于对数据处理有问题,非协议本身问题,因此需要减回 */
|
|
yM.numPkt--;
|
|
yM.txPkt.response = YMODEM_NAK;
|
|
break;
|
|
}
|
|
|
|
case YM_RESULT_CANCEL:
|
|
yM.txPkt.response = YMODEM_CAN;
|
|
break;
|
|
|
|
default:
|
|
/* 业务层还在处理数据,暂时不回复主机 */
|
|
return;
|
|
}
|
|
|
|
yM.isBusy = 0;
|
|
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
|
|
}
|
|
|
|
/**
|
|
* @brief 对主机下发指令的处理
|
|
* @note
|
|
* @retval None
|
|
*/
|
|
static void ymodemCommandProcess (void)
|
|
{
|
|
uint16_t data_len = 0;
|
|
|
|
yM.isBusy = 1;
|
|
|
|
|
|
if ((yM.msg->pkg.header == YMODEM_SOH)||(yM.msg->pkg.header == YMODEM_STX)) {
|
|
data_len = yM.rxLen - YMODEM_FRAME_FIXED_LEN;
|
|
}
|
|
|
|
yM.prepare((eYmCMD)yM.msg->pkg.header, yM.msg->pkg.data, data_len);
|
|
}
|
|
|
|
/**
|
|
* @brief 设置协议执行流程
|
|
* @note
|
|
* @param[in] cmd: 主机的指令
|
|
* @retval eErrCode
|
|
*/
|
|
static eErrCode ymodemExeFlow (eYmCMD cmd)
|
|
{
|
|
switch (cmd) {
|
|
case YM_CMD_SOH: {
|
|
if (yM.flow == YMODEM_FLOW_NONE) { /* 第一个 SOH 数据帧 */
|
|
yM.flow = YMODEM_FLOW_START;
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
} else if (yM.flow == YMODEM_FLOW_SECOND_EOT) { /* 最后一个空的 SOH 数据帧 */
|
|
drv_timer_pause(&timerSendC);
|
|
yM.flow = YMODEM_FLOW_SUCCESS;
|
|
yM.numPkt = 0;
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
} else { /* 正在传输数据的 SOH 数据帧 */
|
|
/* 暂停发送字符“ C ”的定时器 */
|
|
drv_timer_pause(&timerSendC);
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case YM_CMD_STX: {
|
|
/* 因为第一个有数据的数据帧可能是 STX ,因此此处有必要暂停发送字符“ C ”的定时器 */
|
|
drv_timer_pause(&timerSendC);
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
break;
|
|
}
|
|
|
|
case YM_CMD_EOT: {
|
|
if (yM.flow == YMODEM_FLOW_START) { /* 第一个 EOT */
|
|
yM.flow = YMODEM_FLOW_FIRST_EOT;
|
|
yM.txPkt.response = YMODEM_NAK;
|
|
} else if (yM.flow == YMODEM_FLOW_FIRST_EOT) { /* 第二个 EOT */
|
|
yM.flow = YMODEM_FLOW_SECOND_EOT;
|
|
yM.numPkt = 0;
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
/* 发完 ACK 需要继续发“ C ” */
|
|
drv_timer_restart(&timerSendC);
|
|
} else {
|
|
return ERR_EXE_FLOW;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case YM_CMD_CAN: {
|
|
yM.flow = YMODEM_FLOW_CANCEL;
|
|
yM.txPkt.response = YMODEM_ACK;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief 协议解析处理函数
|
|
* @note 需要循环调用
|
|
* @retval eErrCode
|
|
*/
|
|
eErrCode ymodemHandler (uint8_t *data, uint16_t len)
|
|
{
|
|
eErrCode err_code;
|
|
|
|
/* 有数据 */
|
|
if (data && len && yM.enRecv) {
|
|
/* 暂存和格式化 */
|
|
yM.rxData = data;
|
|
yM.rxLen = len;
|
|
yM.msg = (union message_raw_t *)yM.rxData;
|
|
//hex_printf(data, len);
|
|
/* 只有数据帧才做以下错误检查 */
|
|
if (yM.msg->pkg.header == YMODEM_SOH || yM.msg->pkg.header == YMODEM_STX) {
|
|
/* 判断序列号是否符合顺序 */
|
|
if (yM.msg->pkg.pkt_num != yM.numPkt) {
|
|
if (yM.msg->pkg.pkt_num == (yM.numPkt - 1)) {
|
|
printf("error: duplicate frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
|
|
err_code = ERR_DUPLICATE_FRAME;
|
|
} else {
|
|
printf("error: omission frame %d %d\r\n", yM.msg->pkg.pkt_num, yM.numPkt);
|
|
err_code = ERR_OMISSION_FRAME;
|
|
}
|
|
goto __error_exit;
|
|
}
|
|
|
|
/* 判断正反序列号是否正确 */
|
|
uint8_t pkt_num = ~(yM.msg->pkg.pkt_num);
|
|
if (yM.msg->pkg.not_pkt_num != pkt_num) {
|
|
printf("error: packet number: %.2X %.2X\r\n", yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
|
|
err_code = ERR_PKT_NUM_ERR;
|
|
goto __error_exit;
|
|
}
|
|
|
|
/* 获取协议帧中的数据字段长度 */
|
|
uint16_t data_len;
|
|
if (yM.msg->pkg.header == YMODEM_SOH) {
|
|
data_len = YMODEM_SOH_DATA_LEN;
|
|
} else if (yM.msg->pkg.header == YMODEM_STX) {
|
|
data_len = YMODEM_STX_DATA_LEN;
|
|
}
|
|
|
|
/* 帧长度判断 */
|
|
/* 奇怪的是, Xshell 在发送最后一个空 SOH 数据帧时会附加两个字节的 0x4F ,原因未知。
|
|
* 为了处理这个问题,避免误判为数据帧长度有误,此处嵌套了“ ymodem_pkt_num != 0 ”的判断 */
|
|
if (yM.numPkt != 0) {
|
|
if (yM.rxLen != (data_len + YMODEM_FRAME_FIXED_LEN)) {
|
|
//printf("error: _dev_rx_len: %d\r\n", yM.rxLen);
|
|
err_code = ERR_FRAME_LENGTH;
|
|
goto __error_exit;
|
|
}
|
|
}
|
|
|
|
/* 校验数据是否正确 */
|
|
uint16_t crc16 = crc16_xmodem(yM.msg->pkg.data, data_len);
|
|
printf("%04X", crc16);
|
|
uint16_t raw_crc16 = (yM.msg->pkg.data[data_len] << 8) | yM.msg->pkg.data[data_len + 1];
|
|
if (crc16 != raw_crc16) {
|
|
printf("error: crc16: %.4X\r\n", crc16);
|
|
printf("error: raw crc16: %.4X\r\n", raw_crc16);
|
|
err_code = ERR_FRAME_VERIFY_ERR;
|
|
goto __error_exit;
|
|
}
|
|
//printf("crc16 ok.\r\n");
|
|
|
|
/* 执行到此处记录序列号加1 */
|
|
yM.numPkt++;
|
|
}
|
|
//printf("Ymodem recv len: %d (%02X %02X %02X)\r\n",
|
|
// yM.rxLen, yM.msg->pkg.header, yM.msg->pkg.pkt_num, yM.msg->pkg.not_pkt_num);
|
|
|
|
/* 设置流程 */
|
|
if (ymodemExeFlow((eYmCMD)yM.msg->pkg.header)) {
|
|
//printf("error: flow illegal\r\n");
|
|
err_code = ERR_EXE_FLOW;
|
|
goto __error_exit;
|
|
}
|
|
|
|
/* 除了最后一个空的SOH数据帧,其他都会执行 ,包括 EOT CAN */
|
|
/* 需要注意是, 执行完后仍未回复主机,回复部分由 ymodemHeartBeatProcess 处理 */
|
|
ymodemCommandProcess();
|
|
return ERR_OK;
|
|
|
|
__error_exit:
|
|
|
|
yM.txPkt.response = YMODEM_NAK;
|
|
yM.send(&yM.txPkt.response, 1, HAL_MAX_DELAY);
|
|
|
|
return err_code;
|
|
}
|
|
|
|
if (yM.isBusy) {
|
|
/* 模拟主机的心跳处理,查询是否对协议包处理完毕,以向主机回复 */
|
|
ymodemHeartBeatProcess();
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief 配置协议析构层的参数
|
|
* @note
|
|
* @param[in] para: 需要进行配置的选型或参数
|
|
* @param[in] value: 对应的数据或值
|
|
* @retval None
|
|
*/
|
|
void ymodemConfig (eYmMODE mode, void *value)
|
|
{
|
|
switch (mode) {
|
|
case YM_MODE_RESET:
|
|
ymodemReset();
|
|
drv_timer_restart(&timerSendC);
|
|
break;
|
|
|
|
case YM_MODE_RECV: {
|
|
uint8_t *enable = (uint8_t *)value;
|
|
yM.enRecv = *enable;
|
|
if (yM.enRecv == 0) {
|
|
drv_timer_pause(&timerSendC);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief 协议析构层的初始化
|
|
* @note
|
|
* @param[in] Send: 底层数据发送接口
|
|
* @param[in] HeartbeatCallback: 心跳包的响应接口
|
|
* @param[in] PrepareCallback: 指令包的处理接口
|
|
* @param[in] Set_ReplyInfo: 查询指令执行结果的处理接口
|
|
* @retval None
|
|
*/
|
|
void ymodem_init (ymSend Send,
|
|
ymPrepareCallback PrepareCallback,
|
|
ymReplyCallback Set_ReplyInfo)
|
|
{
|
|
yM.send = Send;
|
|
yM.prepare = PrepareCallback;
|
|
yM.reply = Set_ReplyInfo;
|
|
|
|
ymodemReset();
|
|
|
|
drv_timer_init(&timerSendC,
|
|
timerSendCCallback,
|
|
1000,
|
|
TIMER_RUN_FOREVER);
|
|
drv_timer_start(&timerSendC);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|