| /**************************************************************************** |
| * apps/system/zmodem/zm_proto.c |
| * |
| * 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 |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /* References: |
| * "The ZMODEM Inter Application File Transfer Protocol", Chuck Forsberg, |
| * Omen Technology Inc., October 14, 1988 |
| */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdio.h> |
| |
| #include <nuttx/crc16.h> |
| #include <nuttx/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 |
| }; |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * 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=%zu, term=%c flags=%04x\n", |
| zbin, buflen, term, pzm->flags); |
| |
| /* 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 = crc16part((FAR const uint8_t *)&term, 1, crc); |
| } |
| else |
| { |
| crc = crc32part((FAR const uint8_t *)&term, 1, crc); |
| } |
| |
| *ptr++ = term; |
| |
| /* Calculate and transfer the final CRC value */ |
| |
| if (zbin == ZBIN) |
| { |
| 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 crc1 crc2 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 */ |
| |
| 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 */ |
| |
| 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_sendbin32hdr(pzm, type, buffer); |
| } |
| } |