summaryrefslogtreecommitdiff
path: root/apps/system/zmodem/zm_state.c
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-07-13 13:19:15 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-07-13 13:19:15 -0600
commit8c2f7661b762fb378415e2df4b2634910acd3eb4 (patch)
tree92510b249f88031efd0bf40bfaf06a76e45adab7 /apps/system/zmodem/zm_state.c
parent92c70c86d8d7c023e16c87a1bd39cc0d7981d017 (diff)
downloadnuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.tar.gz
nuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.tar.bz2
nuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.zip
More Zmodem bugfixes and new files
Diffstat (limited to 'apps/system/zmodem/zm_state.c')
-rw-r--r--apps/system/zmodem/zm_state.c960
1 files changed, 960 insertions, 0 deletions
diff --git a/apps/system/zmodem/zm_state.c b/apps/system/zmodem/zm_state.c
new file mode 100644
index 000000000..014e26ad5
--- /dev/null
+++ b/apps/system/zmodem/zm_state.c
@@ -0,0 +1,960 @@
+/****************************************************************************
+ * system/zmodem/zm_state.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
+ *
+ * This is an original work, but I want to make sure that credit is given
+ * where due: Parts of the state machine design were inspired by the
+ * Zmodem library of Edward A. Falk, dated January, 1995. License
+ * unspecified.
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <assert.h>
+#include <errno.h>
+#include <crc16.h>
+#include <crc32.h>
+
+#include <nuttx/ascii.h>
+
+#include "zm.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* State-specific data receipt handlers */
+
+static int zm_idle(FAR struct zm_state_s *pzm, uint8_t ch);
+static int zm_header(FAR struct zm_state_s *pzm, uint8_t ch);
+static int zm_data(FAR struct zm_state_s *pzm, uint8_t ch);
+static int zm_finish(FAR struct zm_state_s *pzm, uint8_t ch);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: zm_event
+ *
+ * Description:
+ * This is the heart of the Zmodem state machine. Logic initiated by
+ * zm_parse() will detect events and, eventually call this function.
+ * This function will make the state transition, performing any action
+ * associated with the event.
+ *
+ ****************************************************************************/
+
+static int zm_event(FAR struct zm_state_s *pzm, int event)
+{
+ FAR const struct zm_transition_s *ptr;
+
+ zmdbg("ZM[R|S]_state: %d event: %d\n", pzm->state, event);
+
+ /* Look up the entry associated with the event in the current state
+ * transition table. NOTE that each state table must be termined with a
+ * ZME_ERROR entry that provides indicates that the event was not
+ * expected. Thus, the following search will always be successful.
+ */
+
+ ptr = pzm->evtable[pzm->state];
+ while (ptr->type != ZME_ERROR && ptr->type != event)
+ {
+ /* Skip to the next entry */
+
+ ptr++;
+ }
+
+ zmdbg("Transition ZM[R|S]_state %d->%d discard: %d action: %p\n",
+ pzm->state, ptr->next, ptr->bdiscard, ptr->action);
+
+ /* Perform the state transition */
+
+ pzm->state = ptr->next;
+
+ /* Discard buffered data if so requrested */
+
+ if (ptr->bdiscard)
+ {
+ pzm->rcvlen = 0;
+ pzm->rcvndx = 0;
+ }
+
+ /* And, finally, perform the associated action */
+
+ return ptr->action(pzm);
+}
+
+/****************************************************************************
+ * Name: zm_nakhdr
+ *
+ * Description:
+ * Send a NAK in response to a malformed or unsupported header.
+ *
+ ****************************************************************************/
+
+static int zm_nakhdr(FAR struct zm_state_s *pzm)
+{
+ zmdbg("PSTATE %d:%d->%d.%d: NAKing\n",
+ pzm->pstate, pzm->psubstate, PSTATE_IDLE, PIDLE_ZPAD);
+
+ /* Revert to the IDLE state */
+
+ pzm->pstate = PSTATE_IDLE;
+ pzm->psubstate = PIDLE_ZPAD;
+
+ /* And NAK the header */
+
+ return zm_sendhexhdr(pzm, ZNAK, g_zeroes);
+}
+
+/****************************************************************************
+ * Name: zm_hdrevent
+ *
+ * Description:
+ * Process an event associated with a header.
+ *
+ ****************************************************************************/
+
+static int zm_hdrevent(FAR struct zm_state_s *pzm)
+{
+ zmdbg("Received type: %d data: %02x %02x %02x %02x\n",
+ pzm->hdrdata[0],
+ pzm->hdrdata[1], pzm->hdrdata[2], pzm->hdrdata[3], pzm->hdrdata[4]);
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, PSTATE_IDLE, PIDLE_ZPAD);
+
+ /* Revert to the IDLE state */
+
+ pzm->pstate = PSTATE_IDLE;
+ pzm->psubstate = PIDLE_ZPAD;
+
+ /* Verify the checksum. 16- or 32-bit? */
+
+ if (pzm->hdrfmt == ZBIN32)
+ {
+ uint32_t crc;
+
+ /* Checksum is over 9 bytes: The header type, 4 data bytes, plus 4 CRC bytes */
+
+ crc = crc32part(pzm->hdrdata, 9, 0xffffffff);
+ if (crc != 0xdebb20e3)
+ {
+ zmdbg("ERROR: ZBIN32 CRC32 failure: %08x vs debb20e3\n", crc);
+ return zm_nakhdr(pzm);
+ }
+ }
+ else
+ {
+ uint16_t crc;
+
+ /* Checksum is over 7 bytes: The header type, 4 data bytes, plus 2 CRC bytes */
+
+ crc = crc16part(pzm->hdrdata, 7, 0);
+ if (crc != 0)
+ {
+ zmdbg("ERROR: ZBIN/ZHEX CRC16 failure: %04x vs 0000\n", crc);
+ return zm_nakhdr(pzm);
+ }
+ }
+
+ return zm_event(pzm, pzm->hdrdata[0]);
+}
+
+/****************************************************************************
+ * Name: zm_dataevent
+ *
+ * Description:
+ * Process an event associated with a header.
+ *
+ ****************************************************************************/
+
+static int zm_dataevent(FAR struct zm_state_s *pzm)
+{
+ zmdbg("Received type: %d length: %d\n", pzm->pkttype, pzm->pktlen);
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, PSTATE_IDLE, PIDLE_ZPAD);
+
+ /* Revert to the IDLE state */
+
+ pzm->pstate = PSTATE_IDLE;
+ pzm->psubstate = PIDLE_ZPAD;
+
+ /* Verify the checksum. 16- or 32-bit? */
+
+ if (pzm->hdrfmt == ZBIN32)
+ {
+ uint32_t crc;
+
+ crc = crc32part(pzm->pktbuf, pzm->pktlen, 0xffffffff);
+ if (crc != 0xdebb20e3)
+ {
+ zmdbg("ERROR: ZBIN32 CRC32 failure: %08x vs debb20e3\n", crc);
+ pzm->flags &= ~ZM_FLAG_CRKOK;
+ }
+ else
+ {
+ pzm->flags |= ZM_FLAG_CRKOK;
+ }
+ }
+ else
+ {
+ uint16_t crc;
+
+ crc = crc16part(pzm->pktbuf, pzm->pktlen, 0);
+ if (crc != 0)
+ {
+ zmdbg("ERROR: ZBIN/ZHEX CRC16 failure: %04x vs 0000\n", crc);
+ pzm->flags &= ~ZM_FLAG_CRKOK;
+ }
+ else
+ {
+ pzm->flags |= ZM_FLAG_CRKOK;
+ }
+ }
+
+ return zm_event(pzm, ZME_DATARCVD);
+}
+
+/****************************************************************************
+ * Name: zm_idle
+ *
+ * Description:
+ * Data has been received in state PSTATE_IDLE. In this state we are
+ * looking for the beginning of a header indicated by the receipt of
+ * ZDLE. We skip over ZPAD characters and flush the received buffer in
+ * the case where anything else is received.
+ *
+ ****************************************************************************/
+
+static int zm_idle(FAR struct zm_state_s *pzm, uint8_t ch)
+{
+ switch (ch)
+ {
+ /* One or more ZPAD characters must precede the ZDLE */
+
+ case ZPAD:
+ {
+ /* The ZDLE character is expected next */
+
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, pzm->pstate, PIDLE_ZDLE);
+
+ pzm->psubstate = PIDLE_ZDLE;
+ }
+ break;
+
+ /* ZDLE indicates the beginning of a header. */
+
+ case ZDLE:
+
+ /* Was the ZDLE preceded by ZPAD[s]? If not, fall through and treat
+ * as the default case.
+ */
+
+ if (pzm->psubstate == PIDLE_ZDLE)
+ {
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, PSTATE_HEADER, PHEADER_FORMAT);
+
+ pzm->pstate = PSTATE_HEADER;
+ pzm->psubstate = PHEADER_FORMAT;
+ break;
+ }
+
+ /* Unexpected character. Wait for the next ZPAD to get us */
+
+ default:
+ if (pzm->psubstate != PIDLE_ZPAD)
+ {
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, pzm->pstate, PIDLE_ZPAD);
+
+ pzm->psubstate = PIDLE_ZPAD;
+ }
+ break;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: zm_header
+ *
+ * Description:
+ * Data has been received in state PSTATE_HEADER (i.e., ZDLE was received
+ * in PSTAT_IDLE).
+ *
+ * The following headers are supported:
+ *
+ * 16-bit Binary:
+ * 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)
+ * 32-bit Binary:
+ * 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)
+ * Hex:
+ * 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)
+ *
+ ****************************************************************************/
+
+static int zm_header(FAR struct zm_state_s *pzm, uint8_t ch)
+{
+ /* ZDLE encountered in this state means that the following character is
+ * escaped.
+ */
+
+ if (ch == ZDLE && (pzm->flags & ZM_FLAG_ESC) == 0)
+ {
+ /* Indicate that we are beginning the escape sequence and return */
+
+ pzm->flags |= ZM_FLAG_ESC;
+ return OK;
+ }
+
+ /* Handle the escaped character in an escape sequence */
+
+ if ((pzm->flags & ZM_FLAG_ESC) != 0)
+ {
+ switch (ch)
+ {
+ /* Two special cases */
+
+ case ZRUB0:
+ ch = ASCII_DEL;
+ break;
+
+ case ZRUB1:
+ ch = 0xff;
+ break;
+
+ /* The typical case: Toggle bit 6 */
+
+ default:
+ ch ^= 0x40;
+ break;
+ }
+
+ /* We are no longer in an escape sequence */
+
+ pzm->flags &= ~ZM_FLAG_ESC;
+ }
+
+ /* Now handle the next character, escaped or not, according to the current
+ * PSTATE_HEADER substate.
+ */
+
+ switch (pzm->psubstate)
+ {
+ /* Waiting for the header format {ZBIN, ZBIN32, ZHEX} */
+
+ case PHEADER_FORMAT:
+ {
+ switch (ch)
+ {
+ /* Supported header formats */
+
+ case ZHEX:
+ case ZBIN:
+ case ZBIN32:
+ {
+ /* Save the header format character. Next we expect the header
+ * data payload beginning with the header type.
+ */
+
+ pzm->hdrfmt = ch;
+ pzm->psubstate = PHEADER_PAYLOAD;
+ pzm->hdrndx = 0;
+ }
+ break;
+
+ default:
+ {
+ /* Unrecognized header format. */
+
+ return zm_nakhdr(pzm);
+ }
+ }
+ }
+ break;
+
+ /* Waiting for header payload */
+
+ case PHEADER_PAYLOAD:
+ {
+ int ndx = pzm->hdrndx;
+
+ switch (pzm->hdrfmt)
+ {
+ /* Supported header formats */
+
+ case ZHEX:
+ {
+ if (!isxdigit(ch))
+ {
+ return zm_nakhdr(pzm);
+ }
+
+ /* Save the MS nibble; setup to receive the LS nibble. Index
+ * is not incremented.
+ */
+
+ pzm->hdrdata[ndx] = zm_decnibble(ch) << 4;
+ pzm->psubstate = PHEADER_LSPAYLOAD;
+ }
+ break;
+
+ case ZBIN:
+ case ZBIN32:
+ {
+ /* Save the payload byte and increment the index. */
+
+ pzm->hdrdata[ndx] = ch;
+ ndx++;
+
+ /* Check if the full header payload has bee buffered.
+ *
+ * The ZBIN format uses 16-bit CRC so the binary length of the
+ * full payload is 1+4+2 = 7 bytes; the ZBIN32 uses a 32-bit CRC
+ * so the binary length of the payload is 1+4+4 = 9 bytes;
+ */
+
+ if (ndx >= 9 || (pzm->hdrfmt == ZBIN && ndx >= 7))
+ {
+ return zm_hdrevent(pzm);
+ }
+ else
+ {
+ /* Setup to receive the next byte */
+
+ pzm->psubstate = PHEADER_PAYLOAD;
+ pzm->hdrndx = ndx;
+ }
+ }
+ break;
+
+ default: /* Should not happen */
+ break;
+ }
+ }
+ break;
+
+ /* Waiting for LS nibble header type (ZHEX only) */
+
+ case PHEADER_LSPAYLOAD:
+ {
+ int ndx = pzm->hdrndx;
+
+ if (pzm->hdrfmt == ZHEX && isxdigit(ch))
+ {
+ /* Save the LS nibble and increment the index. */
+
+ pzm->hdrdata[ndx] |= zm_decnibble(ch);
+ ndx++;
+
+ /* The ZHEX format uses 16-bit CRC. So the binary length
+ * of the sequence is 1+4+2 = 7 bytes.
+ */
+
+ if (ndx >= 7)
+ {
+ return zm_hdrevent(pzm);
+ }
+ else
+ {
+ /* Setup to receive the next MS nibble */
+
+ pzm->psubstate = PHEADER_PAYLOAD;
+ pzm->hdrndx = ndx;
+ }
+ }
+ else
+ {
+ return zm_nakhdr(pzm);
+ }
+ }
+ break;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: zm_data
+ *
+ * Description:
+ * Data has been received in state PSTATE_DATA. PSTATE_DATA is set by
+ * Zmodem transfer logic when it exepects to received data from the
+ * remote peer.
+ *
+ * FORMAT:
+ * xx xx xx xx ... xx ZDLE <type> crc-1 crc-2 [crc-3 crc-4]
+ *
+ * Where xx is binary data (that may be escaped). The 16- or 32-bit CRC
+ * is selected based on a preceding header. ZHEX data packets are not
+ * supported.
+ *
+ * When setting pstate to PSTATE_DATA, it is also expected that the
+ * following initialization is performed:
+ *
+ * - The crc value is initialized appropriately
+ * - ncrc is set to zero.
+ * - pktlen is set to zero
+ *
+ ****************************************************************************/
+
+static int zm_data(FAR struct zm_state_s *pzm, uint8_t ch)
+{
+ /* ZDLE encountered in this state means that the following character is
+ * escaped. Escaped characters may appear anywhere within the data packet.
+ */
+
+ if (ch == ZDLE && (pzm->flags & ZM_FLAG_ESC) == 0)
+ {
+ /* Indicate that we are beginning the escape sequence and return */
+
+ pzm->flags |= ZM_FLAG_ESC;
+ return OK;
+ }
+
+ /* Make sure that there is space for another byte in the packet buffer */
+
+ if (pzm->pktlen >= CONFIG_SYSTEM_ZMODEM_PKTBUFSIZE)
+ {
+ zmdbg("ERROR: The packet buffer is full\n");
+ return -ENOSPC;
+ }
+
+ /* Handle the escaped character in an escape sequence */
+
+ if ((pzm->flags & ZM_FLAG_ESC) != 0)
+ {
+ switch (ch)
+ {
+ /* The data packet type may immediately follow the ZDLE in PDATA_READ
+ * substate.
+ */
+
+ case ZCRCW: /* Data packet (Non-streaming, ZACK response expected) */
+ case ZCRCE: /* Data packet (End-of-file, no response unless an error occurs) */
+ case ZCRCG: /* Data packet (Full streaming, no response) */
+ case ZCRCQ: /* Data packet (ZACK response expected) */
+ {
+ /* Save the packet type, change substates, and set of count that
+ * indicates the nubmer of bytes still to be added to the packet
+ * buffer:
+ *
+ * ZBIN: 1+2 = 3
+ * ZBIN32: 1+4 = 5
+ */
+
+ pzm->pkttype = ch;
+ pzm->psubstate = PDATA_CRC;
+ pzm->ncrc = (pzm->hdrfmt == ZBIN32) ? 5 : 3;
+ }
+ break;
+
+ /* Some special cases */
+
+ case ZRUB0:
+ ch = ASCII_DEL;
+ break;
+
+ case ZRUB1:
+ ch = 0xff;
+ break;
+
+ /* The typical case: Toggle bit 6 */
+
+ default:
+ ch ^= 0x40;
+ break;
+ }
+
+ /* We are no longer in an escape sequence */
+
+ pzm->flags &= ~ZM_FLAG_ESC;
+ }
+
+ /* Transfer received data from the I/O buffer to the packet buffer.
+ * Accumulate the CRC for the received data. This includes the data
+ * payload plus the packet type code plus the CRC itself.
+ */
+
+ pzm->pktbuf[pzm->pktlen++] = ch;
+ if (pzm->ncrc == 1)
+ {
+ /* We are at the end of the packet. Check the CRC and post the event */
+
+ zm_dataevent(pzm);
+ }
+ else if (pzm->ncrc > 1)
+ {
+ /* We are still parsing the CRC. Decrement the count of CRC bytes
+ * remaining.
+ */
+
+ pzm->ncrc--;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: zm_finish
+ *
+ * Description:
+ * Data has been received in state PSTATE_FINISH. Juse wait for "OO"
+ *
+ ****************************************************************************/
+
+static int zm_finish(FAR struct zm_state_s *pzm, uint8_t ch)
+{
+ /* Wait for "OO" */
+
+ if (ch == 'O')
+ {
+ /* Have we seen the second '0'? */
+
+ if (pzm->psubstate == PFINISH_2NDO)
+ {
+ /* Yes.. then we are finished */
+
+ return ZM_XFRDONE;
+ }
+ else
+ {
+ /* No.. this was the first */
+
+ pzm->psubstate = PFINISH_2NDO;
+ }
+ }
+ else
+ {
+ /* Reset. We have not seen the first 'O' of the pair */
+
+ pzm->psubstate = PFINISH_1STO;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: zm_parse
+ *
+ * Description:
+ * New data from the remote peer is available in pzm->rcvbuf. The number
+ * number of bytes of new data is given by rcvlen.
+ *
+ * This function will parse the data in the buffer and, based on the
+ * current state and the contents of the buffer, will drive the Zmodem
+ * state machine.
+ *
+ ****************************************************************************/
+
+static int zm_parse(FAR struct zm_state_s *pzm, size_t rcvlen)
+{
+ uint8_t ch;
+ int ret;
+
+ DEBUGASSERT(pzm && rcvlen < CONFIG_SYSTEM_ZMODEM_RCVBUFSIZE);
+ zm_dumpbuffer("Received", pzm->rcvbuf, rcvlen);
+
+ /* We keep a copy of the length and buffer index in the state structure.
+ * This is only so that deeply nested logic can use these values.
+ */
+
+ pzm->rcvlen = rcvlen;
+ pzm->rcvndx = 0;
+
+ /* Process each byte until we reach the end of the buffer (or until the
+ * data is discarded.
+ */
+
+ while (pzm->rcvndx < pzm->rcvlen)
+ {
+ /* Get the next byte from the buffer */
+
+ ch = pzm->rcvbuf[pzm->rcvndx];
+ pzm->rcvndx++;
+
+ /* Handle sequences of CAN characters. When we encounter 5 in a row,
+ * then we consider this a request to cancel the file transfer.
+ */
+
+ if (ch == ASCII_CAN)
+ {
+ if (++pzm->ncan >= 5)
+ {
+ zmdbg("Remote end has cancelled");
+ pzm->rcvlen = 0;
+ pzm->rcvndx = 0;
+ return -EAGAIN;
+ }
+ }
+ else
+ {
+ /* Not CAN... reset the sequence count */
+
+ pzm->ncan = 0;
+ }
+
+ /* Skip over XON and XOFF */
+
+ if (ch != ASCII_XON && ch != ASCII_XOFF)
+ {
+ /* And process what follows based on the current parsing state */
+
+ switch (pzm->pstate)
+ {
+ case PSTATE_IDLE:
+ ret = zm_idle(pzm, ch);
+ break;
+
+ case PSTATE_HEADER:
+ ret = zm_header(pzm, ch);
+ break;
+
+ case PSTATE_DATA:
+ ret = zm_data(pzm, ch);
+ break;
+
+ case PSTATE_FINISH:
+ ret = zm_finish(pzm, ch);
+ break;
+
+ /* This should not happen */
+
+ default:
+ zmdbg("ERROR: Invalid state: %d\n", pzm->pstate);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* Handle end-of-transfer and irrecoverable errors by breaking out
+ * of the loop and return a non-zero return value to indicate that
+ * transfer is complete.
+ */
+
+ if (ret != OK)
+ {
+ zmdbg("Aborting: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ /* If we made it through the entire buffer with no errors detected, then
+ * return OK == 0 meaning that everything is okay, but we are not finished
+ * with the transfer.
+ */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: zm_datapump
+ *
+ * Description:
+ * Drive the Zmodem state machine by reading data from the remote peer and
+ * providing that data to the parser. This loop runs until a fatal error
+ * is detected or until the state machine reports that the transfer has
+ * completed successfully.
+ *
+ ****************************************************************************/
+
+int zm_datapump(FAR struct zm_state_s *pzm)
+{
+ int ret = OK;
+ ssize_t nread;
+
+ /* Loop until either a read error occurs or until a non-zero value is
+ * returned by the parser.
+ */
+
+ do
+ {
+ /* Start/restart the timer. Whenever we read data from the peer we
+ * must anticipate a timeout because we can never be sure that the peer
+ * is still responding.
+ */
+
+ sched_lock();
+ zm_timerstart(pzm, pzm->timeout);
+
+ /* Read a block of data. read() will return: (1) nread > 0 and nread
+ * <= CONFIG_SYSTEM_ZMODEM_RCVBUFSIZE on success, (2) nread == 0 on end
+ * of file, or (3) nread < 0 on a read error or interruption by a
+ * signal.
+ */
+
+ nread = read(pzm->remfd, pzm->rcvbuf, CONFIG_SYSTEM_ZMODEM_RCVBUFSIZE);
+
+ /* Stop the timer */
+
+ (void)zm_timerstop(pzm);
+ sched_unlock();
+
+ /* EOF from the remote peer can only mean that we lost the connection
+ * somehow.
+ */
+
+ if (nread == 0)
+ {
+ zmdbg("ERROR: Unexpected end-of-file\n");
+ return -ENOTCONN;
+ }
+
+ /* Did some error occur? */
+
+ else if (nread < 0)
+ {
+ int errorcode = errno;
+
+ /* EINTR is not an error... it simply means that this read was
+ * interrupted by an signal before it obtained in data. However,
+ * the signal may be SIGALRM indicating an timeout condition.
+ * We will know in this case because the signal handler will set
+ * ZM_FLAG_TIMEOUT.
+ */
+
+ if (errorcode == EINTR)
+ {
+ /* Check for a timeout */
+
+ if ((pzm->flags & ZM_FLAG_TIMEOUT) != 0)
+ {
+ /* Yes... a timeout occurred */
+
+ zm_timeout(pzm);
+ }
+
+ /* No.. then just ignore the EINTR. */
+ }
+ else
+ {
+ /* But anything else is bad and we will return the failure
+ * in those cases.
+ */
+
+ zmdbg("ERROR: read failed: %d\n", errorcode);
+ return -errorcode;
+ }
+ }
+
+ /* Then provide that data to the state machine via zm_parse().
+ * zm_parse() will return a non-zero value if we need to terminate
+ * the loop (with a negative value indicating a failure).
+ */
+
+ else /* nread > 0 */
+ {
+ ret = zm_parse(pzm, nread);
+ if (ret < 0)
+ {
+ zmdbg("ERROR: zm_parse failed: %d\n", ret);
+ }
+ }
+ }
+ while (ret == OK);
+
+ return ret;
+}
+
+
+/****************************************************************************
+ * Name: zm_readstate
+ *
+ * Description:
+ * Enter PSTATE_DATA.
+ *
+ ****************************************************************************/
+
+void zm_readstate(FAR struct zm_state_s *pzm)
+{
+ zmdbg("PSTATE %d:%d->%d.%d\n",
+ pzm->pstate, pzm->psubstate, PSTATE_DATA, PDATA_READ);
+
+ pzm->pstate = PSTATE_DATA;
+ pzm->psubstate = PDATA_READ;
+ pzm->pktlen = 0;
+ pzm->ncrc = 0;
+}
+
+/****************************************************************************
+ * Name: zm_timeout
+ *
+ * Description:
+ * Called by the watchdog logic if/when a timeout is detected.
+ *
+ ****************************************************************************/
+
+int zm_timeout(FAR struct zm_state_s *pzm)
+{
+ return zm_event(pzm, ZME_TIMEOUT);
+}