📄 xymodem.c
字号:
/*
* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
* All rights reserved.
*
* This software is copyrighted by and is the sole property of
* VIA Networking Technologies, Inc. This software may only be used
* in accordance with the corresponding license agreement. Any unauthorized
* use, duplication, transmission, distribution, or disclosure of this
* software is expressly forbidden.
*
* This software is provided by VIA Networking Technologies, Inc. "as is"
* and any express or implied warranties, including, but not limited to, the
* implied warranties of merchantability and fitness for a particular purpose
* are disclaimed. In no event shall VIA Networking Technologies, Inc.
* be liable for any direct, indirect, incidental, special, exemplary, or
* consequential damages.
*
*
* File: xymodem.c
*
* Purpose: XY-Modem protocol
*
* Author: Tevin Chen
*
* Date: Jan 08, 2002
*
* Functions:
*
* Revision History:
*
*/
#if !defined(__KERNEL_H__)
#include "kernel.h"
#endif
#if !defined(__ASCII_H__)
#include "ascii.h"
#endif
#if !defined(__STR_H__)
#include "str.h"
#endif
#if !defined(__CRC_H__)
#include "crc.h"
#endif
#if !defined(__DEVICE_H__)
#include "device.h"
#endif
#if !defined(__UART_H__)
#include "uart.h"
#endif
/*--------------------- Static Definitions ------------------------*/
#define MAX_RETRY 10
/*--------------------- Static Types ------------------------------*/
/*--------------------- Static Macros -----------------------------*/
/*--------------------- Static Classes ----------------------------*/
/*--------------------- Static Variables --------------------------*/
UINT8 g_au8ModemBuf[1032]; // 1024 for XModem 1k + 3 head chars
// + 2 crc + 1 nul + 2 padding(dword alignment)
/*--------------------- Static Functions --------------------------*/
static void s_vFlushInput(void);
static BOOL s_bCheck(BOOL bCrc, const PUINT8 pu8PktBuf, INT16 i16PktLen);
static INT32 s_i32Transmit(PUINT8 pu8TxBuf, UINT32 u32TxBufLen, BOOL bCrc);
/*--------------------- Export Variables --------------------------*/
static void s_vFlushInput(void)
{
while (UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC * 3 / 2) >= 0)
;
}
static BOOL s_bCheck(BOOL bCrc, const PUINT8 pu8PktBuf, INT16 i16PktLen)
{
if (bCrc) {
UINT16 u16Crc = CRC_u16Crc16(pu8PktBuf, i16PktLen);
UINT16 tcrc = (pu8PktBuf[i16PktLen]<<8) + pu8PktBuf[i16PktLen+1];
if (u16Crc == tcrc)
return TRUE;
}
else {
INT16 ii;
UINT8 cks = 0;
for (ii = 0; ii < i16PktLen; ii++) {
cks += pu8PktBuf[ii];
}
if (cks == pu8PktBuf[i16PktLen])
return TRUE;
}
return FALSE;
}
INT32 XYMDM_i32Receive(PUINT8 pu8RxBuf, UINT32 u32RxBufLen)
{
INT16 i16Char;
UINT16 u16Retry;
INT16 ii;
UINT8 u8PacketNo = 1;
INT16 i16PerPktLen = 128;
UINT32 u32AlreadyRecv = 0;
UINT8 u8TryChar = 'C'; // try CRC first
BOOL bCrc = FALSE;
PUINT8 pu8XmodemBuf;
INT16 i16Retrans = MAX_RETRY;
BOOL bInYmodemMode = FALSE;
UINT8 u8YmodemEot = 0;
UINT32 u32YmodemLenParam = 0;
for (;;) {
// wait for 32 sec to make connection
for (u16Retry = 0; u16Retry < 16; ++u16Retry) {
// tell sender to send
if (u8TryChar != 0)
UART_vPutCharRaw(u8TryChar);
// get response from sender
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC * 2)) >= 0) {
switch (i16Char) {
case MC_SOH:
i16PerPktLen = 128;
goto start_recv;
case MC_STX:
i16PerPktLen = 1024;
goto start_recv;
case MC_EOT:
// OK: normal end
if (bInYmodemMode)
goto ymodem_end;
else {
s_vFlushInput();
UART_vPutCharRaw(MC_ACK);
return u32AlreadyRecv;
}
case MC_CAN:
// ERR: canceled by remote
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC)) == MC_CAN) {
s_vFlushInput();
UART_vPutCharRaw(MC_ACK);
return -1;
}
break;
default:
// other char, ignore
break;
}
}
}
// if no response for CRC, try NAK
if (u8TryChar == 'C') {
u8TryChar = MC_NAK;
continue;
}
// ERR: not sync, abort rx, flush input, send CAN
s_vFlushInput();
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
return -2;
ymodem_end:
if (u8YmodemEot == 0) {
// the first eot, send NAK
s_vFlushInput();
UART_vPutCharRaw(MC_NAK);
u8YmodemEot++;
continue;
}
else {
// the second eot, send ACK
s_vFlushInput();
UART_vPutCharRaw(MC_ACK);
u8TryChar = 'C';
continue;
}
start_recv:
if (u8TryChar == 'C')
bCrc = TRUE;
// clear try char for next packet
u8TryChar = 0;
pu8XmodemBuf = g_au8ModemBuf;
*pu8XmodemBuf++ = (UINT8)i16Char;
// recv a packet
for (ii = 0; ii < (i16PerPktLen + 3 + (bCrc ? 1 : 0)); ii++) {
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC)) < 0) {
s_vFlushInput();
UART_vPutCharRaw(MC_NAK);
continue;
}
*pu8XmodemBuf++ = (UINT8)i16Char;
}
if (g_au8ModemBuf[1] == 0) {
// check if ymodem protocol
UINT8 u8Pos = STR_iStrlen((char *)&g_au8ModemBuf[3]) + 4;
if (!bInYmodemMode) {
// start of ymodem
bInYmodemMode = TRUE;
u32YmodemLenParam = STR_u32StrDecToU32((char *)&g_au8ModemBuf[u8Pos]);
UART_vPutCharRaw(MC_ACK);
u8TryChar = 'C';
continue;
}
else {
BOOL bEnd = TRUE;
UINT16 uu;
for (uu = 0; uu < i16PerPktLen; uu++) {
if (g_au8ModemBuf[3+uu] != 0)
bEnd = FALSE;
}
if (bEnd) {
// end of ymodem
UART_vPutCharRaw(MC_ACK);
return u32YmodemLenParam;
}
}
}
// good packet, send ACK
if ((g_au8ModemBuf[1] == (UINT8)(~g_au8ModemBuf[2]))
&& ((g_au8ModemBuf[1] == u8PacketNo) || (g_au8ModemBuf[1] == (UINT8)(u8PacketNo - 1)))
&& s_bCheck(bCrc, &g_au8ModemBuf[3], i16PerPktLen) ) {
// normal good packet
if (g_au8ModemBuf[1] == u8PacketNo) {
register int count = u32RxBufLen - u32AlreadyRecv;
if (count > i16PerPktLen)
count = i16PerPktLen;
if (count > 0) {
STR_pvMemcpy(&pu8RxBuf[u32AlreadyRecv], &g_au8ModemBuf[3], count);
u32AlreadyRecv += count;
}
++u8PacketNo;
i16Retrans = MAX_RETRY;
}
// this is a retry good packet, it's ok to receive a repeated packet, just drop it
else {
--i16Retrans;
if (i16Retrans <= 0) {
// ERR: too many retry error, abort rx, flush input, send CAN
s_vFlushInput();
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
return -3;
}
}
UART_vPutCharRaw(MC_ACK);
}
// bad packet, send NAK
else {
s_vFlushInput();
UART_vPutCharRaw(MC_NAK);
}
}
}
/*
* Each packet looks like this:
* +-----+-------+-------+------+-----+
* | SOH | Seq1. | Seq2. | data | SUM |
* +-----+-------+-------+------+-----+
* SOH = 1 byte, 0x01(SOH) / 0x02(STX)
* Seq1 = 1 byte, The sequence number.
* Seq2 = 1 byte, The complement of the sequence number.
* Data = A 128/1024 bytes of data.
* SUM = 2 bytes, CRC: ccitt 16-bit CRC
* 1 byte, CheckSum: Add the contents of the 128 bytes and use the low-order
* 8 bits of the result.
*
*/
static INT32 s_i32Transmit(PUINT8 pu8TxBuf, UINT32 u32TxBufLen, BOOL bCrc)
{
INT16 i16Char;
UINT16 u16Retry;
INT16 ii;
UINT8 u8PacketNo = 1;
INT16 i16PerPktLen;
UINT32 u32AlreadySend = 0;
INT32 i32WantToSend;
for (;;) {
i32WantToSend = u32TxBufLen - u32AlreadySend;
if (i32WantToSend <= 128) {
i16PerPktLen = 128;
g_au8ModemBuf[0] = MC_SOH;
}
else {
i16PerPktLen = 1024;
g_au8ModemBuf[0] = MC_STX;
}
g_au8ModemBuf[1] = u8PacketNo;
g_au8ModemBuf[2] = ~u8PacketNo;
// adjust i32WantToSend not big than i16PerPktLen
if (i32WantToSend > i16PerPktLen)
i32WantToSend = i16PerPktLen;
// send a packet
if (i32WantToSend >= 0) {
// clear buffer, for security
STR_pvMemset(&g_au8ModemBuf[3], 0, i16PerPktLen);
// append EOF
if (i32WantToSend == 0) {
// nothing left to send, put EOF
g_au8ModemBuf[3] = MC_EOF;
}
else {
STR_pvMemcpy(&g_au8ModemBuf[3], &pu8TxBuf[u32AlreadySend], i32WantToSend);
// append EOF at the tail
if (i32WantToSend < i16PerPktLen)
g_au8ModemBuf[3+i32WantToSend] = MC_EOF;
}
// calculate CRC or CheckSum
if (bCrc) {
UINT16 u16Crc = CRC_u16Crc16(&g_au8ModemBuf[3], i16PerPktLen);
g_au8ModemBuf[i16PerPktLen+3] = (u16Crc>>8) & 0xFF;
g_au8ModemBuf[i16PerPktLen+4] = u16Crc & 0xFF;
}
else {
UINT8 u8CkSum = 0;
for (ii = 3; ii < i16PerPktLen+3; ii++)
u8CkSum += g_au8ModemBuf[ii];
g_au8ModemBuf[i16PerPktLen+3] = u8CkSum;
}
for (u16Retry = 0; u16Retry < MAX_RETRY; ++u16Retry) {
// xmit a packet
for (ii = 0; ii < (i16PerPktLen+4+(bCrc?1:0)); ii++) {
UART_vPutCharRaw(g_au8ModemBuf[ii]);
}
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC)) >= 0 ) {
if (i16Char == MC_ACK) {
// OK: send next packet
++u8PacketNo;
u32AlreadySend += i16PerPktLen;
break;
}
else if (i16Char == MC_CAN) {
// ERR: canceled by remote
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC)) == MC_CAN) {
UART_vPutCharRaw(MC_ACK);
s_vFlushInput();
return -1;
}
}
else {
// if NAK or other char, then retry
}
}
}
if (u16Retry == MAX_RETRY) {
// ERR: too many xmit error, abort tx, send CAN, flush input
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
s_vFlushInput();
return -4;
}
}
// send completed
else {
// OK: wait 20 sec for last ACK to end
for (u16Retry = 0; u16Retry < 10; ++u16Retry) {
UART_vPutCharRaw(MC_EOT);
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC * 2)) == MC_ACK)
break;
}
if (u16Retry == 10) {
// ERR: end of xmit, but no ACK
s_vFlushInput();
return -5;
}
return u32AlreadySend;
}
}
}
INT32 XYMDM_i32Transmit(PUINT8 pu8TxBuf, UINT32 u32TxBufLen)
{
INT16 i16Char;
UINT16 u16Retry;
// wait for 32 sec to make connection
for (u16Retry = 0; u16Retry < 16; ++u16Retry) {
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC * 2)) >= 0) {
switch (i16Char) {
case 'C':
return s_i32Transmit(pu8TxBuf, u32TxBufLen, TRUE);
case MC_NAK:
return s_i32Transmit(pu8TxBuf, u32TxBufLen, FALSE);
case MC_CAN:
// ERR: canceled by remote
if ((i16Char = UART_i16GetCharRawTimed(TMR_TICKS_PER_SEC)) == MC_CAN) {
UART_vPutCharRaw(MC_ACK);
s_vFlushInput();
return -1;
}
break;
default:
// other char, ignore
break;
}
}
}
// ERR: not sync, abort tx, send CAN, flush input
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
UART_vPutCharRaw(MC_CAN);
s_vFlushInput();
return -2;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -