diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-07-13 13:19:15 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-07-13 13:19:15 -0600 |
commit | 8c2f7661b762fb378415e2df4b2634910acd3eb4 (patch) | |
tree | 92510b249f88031efd0bf40bfaf06a76e45adab7 /apps/system/zmodem/zm_state.c | |
parent | 92c70c86d8d7c023e16c87a1bd39cc0d7981d017 (diff) | |
download | nuttx-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.c | 960 |
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); +} |