/****************************************************************************
* system/zmodem/zm_proto.c
*
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* References:
* "The ZMODEM Inter Application File Transfer Protocol", Chuck Forsberg,
* Omen Technology Inc., October 14, 1988
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <crc16.h>
#include <crc32.h>
#include "zm.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* 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.
*/
#define CANISTR_SIZE (8+10)
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
};
/****************************************************************************
* Public Function Protypes
****************************************************************************/
/****************************************************************************
* 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:
* pzm - Zmodem session state
* buffer - Buffer of data to be sent
* 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)
{
uint8_t *ptr = pzm->scratch;
ssize_t nwritten;
uint32_t crc;
uint8_t zbin;
uint8_t term;
int i;
/* Make select ZBIN or ZBIN32 format and the ZCRCW terminator */
if ((pzm->flags & ZM_FLAG_CRC32) != 0)
{
zbin = ZBIN32;
crc = 0xffffffff;
}
else
{
zbin = ZBIN;
crc = 0;
}
term = ZCRCW;
zmdbg("zbin=%c, buflen=%d, term=%c\n", zbin, buflen, term);
/* 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)
{
crc = (uint32_t)crc16part((FAR const uint8_t *)&term, 1, (uint16_t)crc);
}
else
{
crc = crc32part((FAR const uint8_t *)&term, 1, crc);
}
*ptr++ = term;
/* Calcualate and transfer the final CRC value */
if (zbin == ZBIN)
{
crc = (uint32_t)crc16part(g_zeroes, 2, (uint16_t)crc);
ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff);
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
}
else
{
crc = ~crc;
for (i = 0; i < 4; i++, crc >>= 8)
{
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
}
}
/* Send the header */
nwritten = zm_remwrite(pzm->remfd, pzm->scratch, ptr - pzm->scratch);
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:
* ZPAD ZPAD ZDLE ZHEX type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2 CR LF [XON]
* 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 */
crc = crc16part(g_zeroes, 2, crc);
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)
*
* 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 */
crc = crc16part(g_zeroes, 2, crc);
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);
crc = crc32part((FAR const uint8_t *)&type, 1, 0xffffffffL);
/* 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
{
return zm_sendbin16hdr(pzm, type, buffer);
}
}