2013-07-13 00:28:35 +02:00
|
|
|
/****************************************************************************
|
2021-06-10 18:40:35 +02:00
|
|
|
* apps/system/zmodem/zm_proto.c
|
2013-07-13 00:28:35 +02:00
|
|
|
*
|
2021-06-10 13:17:16 +02:00
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
|
|
* this work for additional information regarding copyright ownership. The
|
|
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at
|
2013-07-13 00:28:35 +02:00
|
|
|
*
|
2021-06-10 13:17:16 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2013-07-13 00:28:35 +02:00
|
|
|
*
|
2021-06-10 13:17:16 +02:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
2013-07-13 00:28:35 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2021-06-10 13:17:16 +02:00
|
|
|
/* References:
|
|
|
|
* "The ZMODEM Inter Application File Transfer Protocol", Chuck Forsberg,
|
|
|
|
* Omen Technology Inc., October 14, 1988
|
|
|
|
*/
|
|
|
|
|
2013-07-13 00:28:35 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <crc16.h>
|
|
|
|
#include <crc32.h>
|
|
|
|
|
|
|
|
#include "zm.h"
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Data
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* Paragraph 8.4. Session Abort Sequence
|
|
|
|
*
|
|
|
|
* "If the receiver is receiving data in streaming mode, the Attn sequence
|
|
|
|
* is executed to interrupt data transmission before the Cancel sequence is
|
|
|
|
* sent. The Cancel sequence consists of eight CAN characters and ten
|
|
|
|
* backspace characters. ZMODEM only requires five Cancel characters, the
|
|
|
|
* other three are "insurance".
|
|
|
|
*
|
|
|
|
* "The trailing backspace characters attempt to erase the effects of the
|
|
|
|
* CAN characters if they are received by a command interpreter.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const uint8_t g_canistr[CANISTR_SIZE] =
|
|
|
|
{
|
|
|
|
/* Eight CAN characters */
|
|
|
|
|
|
|
|
ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN,
|
|
|
|
ASCII_CAN, ASCII_CAN,
|
|
|
|
|
|
|
|
/* Ten backspace characters */
|
|
|
|
|
|
|
|
ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS,
|
|
|
|
ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS
|
|
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************
|
2020-06-02 15:02:47 +02:00
|
|
|
* Public Functions
|
2013-07-13 00:28:35 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_putzdle
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Transfer a value to a buffer performing ZDLE escaping if necessary.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* pzm - Zmodem session state
|
|
|
|
* buffer - Buffer in which to add the possibly escaped character
|
|
|
|
* ch - The raw, unescaped character to be added
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
FAR uint8_t *zm_putzdle(FAR struct zm_state_s *pzm, FAR uint8_t *buffer,
|
|
|
|
uint8_t ch)
|
|
|
|
{
|
|
|
|
uint8_t ch7 = ch & 0x7f;
|
|
|
|
|
|
|
|
/* Check if this character requires ZDLE escaping.
|
|
|
|
*
|
|
|
|
* The Zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and a CR
|
|
|
|
* following '@' be escaped.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ch == ZDLE ||
|
|
|
|
ch7 == ASCII_DLE ||
|
|
|
|
ch7 == ASCII_DC1 ||
|
|
|
|
ch7 == ASCII_DC3 ||
|
|
|
|
ch7 == ASCII_GS ||
|
|
|
|
(ch7 == '\r' && (pzm->flags & ZM_FLAG_ATSIGN) != 0) ||
|
|
|
|
(ch7 < ' ' && (pzm->flags & ZM_FLAG_ESCCTRL) != 0) ||
|
|
|
|
ch7 == ASCII_DEL ||
|
|
|
|
ch == 0xff
|
|
|
|
)
|
|
|
|
{
|
|
|
|
/* Yes... save the data link escape the character */
|
|
|
|
|
|
|
|
*buffer++ = ZDLE;
|
|
|
|
|
|
|
|
/* And modify the character itself as appropriate */
|
|
|
|
|
|
|
|
if (ch == ASCII_DEL)
|
|
|
|
{
|
|
|
|
ch = ZRUB0;
|
|
|
|
}
|
|
|
|
else if (ch == 0xff)
|
|
|
|
{
|
|
|
|
ch = ZRUB1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ch ^= 0x40;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Save the possibly escaped character */
|
|
|
|
|
|
|
|
*buffer++ = ch;
|
|
|
|
|
|
|
|
/* Check if the character is the AT sign */
|
|
|
|
|
|
|
|
if (ch7 == '@')
|
|
|
|
{
|
|
|
|
pzm->flags |= ZM_FLAG_ATSIGN;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pzm->flags &= ~ZM_FLAG_ATSIGN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_senddata
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Send data to the remote peer performing CRC operations as required
|
|
|
|
* (ZBIN or ZBIN32 format assumed, ZCRCW terminator is always used)
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
2013-07-13 03:06:00 +02:00
|
|
|
* pzm - Zmodem session state
|
|
|
|
* buffer - Buffer of data to be sent
|
2013-07-13 00:28:35 +02:00
|
|
|
* buflen - The number of bytes in buffer to be sent
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int zm_senddata(FAR struct zm_state_s *pzm, FAR const uint8_t *buffer,
|
|
|
|
size_t buflen)
|
|
|
|
{
|
2013-07-13 03:06:00 +02:00
|
|
|
uint8_t *ptr = pzm->scratch;
|
2013-07-13 00:28:35 +02:00
|
|
|
ssize_t nwritten;
|
|
|
|
uint32_t crc;
|
|
|
|
uint8_t zbin;
|
|
|
|
uint8_t term;
|
2013-07-13 03:06:00 +02:00
|
|
|
int i;
|
2013-07-13 00:28:35 +02:00
|
|
|
|
|
|
|
/* Make select ZBIN or ZBIN32 format and the ZCRCW terminator */
|
|
|
|
|
|
|
|
if ((pzm->flags & ZM_FLAG_CRC32) != 0)
|
|
|
|
{
|
|
|
|
zbin = ZBIN32;
|
2013-07-13 03:06:00 +02:00
|
|
|
crc = 0xffffffff;
|
2013-07-13 00:28:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
zbin = ZBIN;
|
2013-07-13 03:06:00 +02:00
|
|
|
crc = 0;
|
2013-07-13 00:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
term = ZCRCW;
|
2020-06-02 15:02:47 +02:00
|
|
|
zmdbg("zbin=%c, buflen=%zu, term=%c flags=%04x\n",
|
2013-07-13 21:19:15 +02:00
|
|
|
zbin, buflen, term, pzm->flags);
|
2013-07-13 00:28:35 +02:00
|
|
|
|
|
|
|
/* Transfer the data to the I/O buffer, accumulating the CRC */
|
|
|
|
|
|
|
|
while (buflen-- > 0)
|
|
|
|
{
|
|
|
|
if (zbin == ZBIN)
|
|
|
|
{
|
|
|
|
crc = (uint32_t)crc16part(buffer, 1, (uint16_t)crc);
|
|
|
|
}
|
|
|
|
else /* zbin = ZBIN32 */
|
|
|
|
{
|
|
|
|
crc = crc32part(buffer, 1, crc);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = zm_putzdle(pzm, ptr, *buffer++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trasnfer the data link escape character (without updating the CRC) */
|
|
|
|
|
|
|
|
*ptr++ = ZDLE;
|
|
|
|
|
|
|
|
/* Transfer the terminating character, updating the CRC */
|
|
|
|
|
|
|
|
if (zbin == ZBIN)
|
|
|
|
{
|
2020-06-02 15:02:47 +02:00
|
|
|
crc = crc16part((FAR const uint8_t *)&term, 1, crc);
|
2013-07-13 00:28:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
crc = crc32part((FAR const uint8_t *)&term, 1, crc);
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptr++ = term;
|
|
|
|
|
2018-05-27 16:43:22 +02:00
|
|
|
/* Calculate and transfer the final CRC value */
|
2013-07-13 00:28:35 +02:00
|
|
|
|
|
|
|
if (zbin == ZBIN)
|
|
|
|
{
|
|
|
|
ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff);
|
|
|
|
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
crc = ~crc;
|
2013-07-13 03:06:00 +02:00
|
|
|
for (i = 0; i < 4; i++, crc >>= 8)
|
2013-07-13 00:28:35 +02:00
|
|
|
{
|
|
|
|
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the header */
|
|
|
|
|
2013-07-13 03:06:00 +02:00
|
|
|
nwritten = zm_remwrite(pzm->remfd, pzm->scratch, ptr - pzm->scratch);
|
2013-07-13 00:28:35 +02:00
|
|
|
return nwritten < 0 ? (int)nwritten : OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_sendhexhdr
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Send a ZHEX header to the remote peer performing CRC operations as
|
|
|
|
* necessary.
|
|
|
|
*
|
|
|
|
* Hex header:
|
2020-06-02 15:02:47 +02:00
|
|
|
* ZPAD ZPAD ZDLE ZHEX type f3/p0 f2/p1 f1/p2 f0/p3 crc1 crc2 CR LF [XON]
|
2013-07-13 00:28:35 +02:00
|
|
|
* Payload length: 16 (14 hex digits, cr, lf, ignoring optional XON)
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* pzm - Zmodem session state
|
|
|
|
* type - Header type {ZRINIT, ZRQINIT, ZDATA, ZACK, ZNAK, ZCRC, ZRPOS,
|
|
|
|
* ZCOMPL, ZEOF, ZFIN}
|
|
|
|
* buffer - 4-byte buffer of data to be sent
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* The allocated I/O buffer is available to buffer file data.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int zm_sendhexhdr(FAR struct zm_state_s *pzm, int type,
|
|
|
|
FAR const uint8_t *buffer)
|
|
|
|
{
|
|
|
|
FAR uint8_t *ptr;
|
|
|
|
ssize_t nwritten;
|
|
|
|
uint16_t crc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
zmdbg("Sending type %d: %02x %02x %02x %02x\n",
|
|
|
|
type, buffer[0], buffer[1], buffer[2], buffer[3]);
|
|
|
|
|
|
|
|
/* ZPAD ZPAD ZDLE ZHEX */
|
|
|
|
|
|
|
|
ptr = pzm->scratch;
|
|
|
|
*ptr++ = ZPAD;
|
|
|
|
*ptr++ = ZPAD;
|
|
|
|
*ptr++ = ZDLE;
|
|
|
|
*ptr++ = ZHEX;
|
|
|
|
|
|
|
|
/* type */
|
|
|
|
|
|
|
|
crc = crc16part((FAR const uint8_t *)&type, 1, 0);
|
|
|
|
ptr = zm_puthex8(ptr, type);
|
|
|
|
|
|
|
|
/* f3/p0 f2/p1 f1/p2 f0/p3 */
|
|
|
|
|
|
|
|
crc = crc16part(buffer, 4, crc);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
ptr = zm_puthex8(ptr, *buffer++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* crc-1 crc-2 */
|
2014-04-14 00:24:28 +02:00
|
|
|
|
2013-07-13 00:28:35 +02:00
|
|
|
ptr = zm_puthex8(ptr, (crc >> 8) & 0xff);
|
|
|
|
ptr = zm_puthex8(ptr, crc & 0xff);
|
|
|
|
|
|
|
|
/* CR LF */
|
|
|
|
|
|
|
|
*ptr++ = '\r';
|
|
|
|
*ptr++ = '\n';
|
|
|
|
|
|
|
|
/* [XON] */
|
|
|
|
|
|
|
|
if (type != ZACK && type != ZFIN)
|
|
|
|
{
|
|
|
|
*ptr++ = ASCII_XON;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the header */
|
|
|
|
|
|
|
|
nwritten = zm_remwrite(pzm->remfd, pzm->scratch, ptr - pzm->scratch);
|
|
|
|
return nwritten < 0 ? (int)nwritten : OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_sendbin16hdr
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Send a ZBIN header to the remote peer performing CRC operations as
|
|
|
|
* necessary. Normally called indirectly through zm_sendbinhdr().
|
|
|
|
*
|
|
|
|
* 16-bit binary header:
|
|
|
|
* ZPAD ZDLE ZBIN type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2
|
|
|
|
* Payload length: 7 (type, 4 bytes data, 2 byte CRC)
|
2014-04-14 00:24:28 +02:00
|
|
|
*
|
2013-07-13 00:28:35 +02:00
|
|
|
* Input Parameters:
|
|
|
|
* pzm - Zmodem session state
|
|
|
|
* type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA}
|
|
|
|
* buffer - 4-byte buffer of data to be sent
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* The allocated I/O buffer is available to buffer file data.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int zm_sendbin16hdr(FAR struct zm_state_s *pzm, int type,
|
|
|
|
FAR const uint8_t *buffer)
|
|
|
|
{
|
|
|
|
FAR uint8_t *ptr;
|
|
|
|
ssize_t nwritten;
|
|
|
|
uint16_t crc;
|
|
|
|
int buflen;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
zmdbg("Sending type %d: %02x %02x %02x %02x\n",
|
|
|
|
type, buffer[0], buffer[1], buffer[2], buffer[3]);
|
|
|
|
|
|
|
|
/* XPAD ZDLE ZBIN */
|
|
|
|
|
|
|
|
ptr = pzm->scratch;
|
|
|
|
*ptr++ = ZPAD;
|
|
|
|
*ptr++ = ZDLE;
|
|
|
|
*ptr++ = ZBIN;
|
|
|
|
|
|
|
|
/* type */
|
|
|
|
|
|
|
|
crc = crc16part((FAR const uint8_t *)&type, 1, 0);
|
|
|
|
ptr = zm_putzdle(pzm, ptr, type);
|
|
|
|
|
|
|
|
/* f3/p0 f2/p1 f1/p2 f0/p3 */
|
|
|
|
|
|
|
|
crc = crc16part(buffer, 4, crc);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
ptr = zm_putzdle(pzm, ptr, *buffer++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* crc-1 crc-2 */
|
|
|
|
|
|
|
|
ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff);
|
|
|
|
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
|
|
|
|
|
|
|
|
/* Send the header */
|
|
|
|
|
|
|
|
buflen = ptr - pzm->scratch;
|
|
|
|
nwritten = zm_remwrite(pzm->remfd, pzm->scratch, buflen);
|
|
|
|
return nwritten < 0 ? (int)nwritten : OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_sendbin32hdr
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Send a ZBIN32 header to the remote peer performing CRC operations as
|
|
|
|
* necessary. Normally called indirectly through zm_sendbinhdr().
|
|
|
|
*
|
|
|
|
* 32-bit inary header:
|
|
|
|
* ZPAD ZDLE ZBIN32 type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2 crc-3 crc-4
|
|
|
|
* Payload length: 9 (type, 4 bytes data, 4 byte CRC)
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* pzm - Zmodem session state
|
|
|
|
* type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA}
|
|
|
|
* buffer - 4-byte buffer of data to be sent
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* The allocated I/O buffer is available to buffer file data.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int zm_sendbin32hdr(FAR struct zm_state_s *pzm, int type,
|
|
|
|
FAR const uint8_t *buffer)
|
|
|
|
{
|
|
|
|
FAR uint8_t *ptr;
|
|
|
|
ssize_t nwritten;
|
|
|
|
uint32_t crc;
|
|
|
|
int buflen;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
zmdbg("Sending type %d: %02x %02x %02x %02x\n",
|
|
|
|
type, buffer[0], buffer[1], buffer[2], buffer[3]);
|
|
|
|
|
|
|
|
/* XPAD ZDLE ZBIN32 */
|
|
|
|
|
|
|
|
ptr = pzm->scratch;
|
|
|
|
*ptr++ = ZPAD;
|
|
|
|
*ptr++ = ZDLE;
|
|
|
|
*ptr++ = ZBIN32;
|
|
|
|
|
|
|
|
/* type */
|
|
|
|
|
|
|
|
ptr = zm_putzdle(pzm, ptr, type);
|
2020-06-02 15:02:47 +02:00
|
|
|
crc = crc32part((FAR const uint8_t *)&type, 1, 0xffffffffl);
|
2013-07-13 00:28:35 +02:00
|
|
|
|
|
|
|
/* f3/p0 f2/p1 f1/p2 f0/p3 */
|
|
|
|
|
|
|
|
crc = crc32part(buffer, 4, crc);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
ptr = zm_putzdle(pzm, ptr, *buffer++);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* crc-1 crc-2 crc-3 crc-4 */
|
|
|
|
|
|
|
|
crc = ~crc;
|
|
|
|
for (i = 0; i < 4; i++, crc >>= 8)
|
|
|
|
{
|
|
|
|
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the header */
|
|
|
|
|
|
|
|
buflen = ptr - pzm->scratch;
|
|
|
|
nwritten = zm_remwrite(pzm->remfd, pzm->scratch, buflen);
|
|
|
|
return nwritten < 0 ? (int)nwritten : OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: zm_sendbinhdr
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Send a binary header to the remote peer. This is a simple wrapper
|
|
|
|
* function for zm_sendbin16hdr() and zm_sendbin32hdr(). It decides on
|
|
|
|
* the correct CRC format and re-directs the call appropriately.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* pzm - Zmodem session state
|
|
|
|
* type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA}
|
|
|
|
* buffer - 4-byte buffer of data to be sent
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
* The allocated I/O buffer is available to buffer file data.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int zm_sendbinhdr(FAR struct zm_state_s *pzm, int type,
|
|
|
|
FAR const uint8_t *buffer)
|
|
|
|
{
|
|
|
|
if ((pzm->flags & ZM_FLAG_CRC32) == 0)
|
|
|
|
{
|
|
|
|
return zm_sendbin16hdr(pzm, type, buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-07-13 21:19:15 +02:00
|
|
|
return zm_sendbin32hdr(pzm, type, buffer);
|
2013-07-13 00:28:35 +02:00
|
|
|
}
|
|
|
|
}
|