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_send.c | |
parent | 92c70c86d8d7c023e16c87a1bd39cc0d7981d017 (diff) | |
download | px4-nuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.tar.gz px4-nuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.tar.bz2 px4-nuttx-8c2f7661b762fb378415e2df4b2634910acd3eb4.zip |
More Zmodem bugfixes and new files
Diffstat (limited to 'apps/system/zmodem/zm_send.c')
-rw-r--r-- | apps/system/zmodem/zm_send.c | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/apps/system/zmodem/zm_send.c b/apps/system/zmodem/zm_send.c new file mode 100644 index 000000000..5982834da --- /dev/null +++ b/apps/system/zmodem/zm_send.c @@ -0,0 +1,1684 @@ +/**************************************************************************** + * system/zmodem/zm_send.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 <fcntl.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 + ****************************************************************************/ + +/* Zmodem transmit states. + * + * A simple transaction, one file, no errors, no CHALLENGE, overlapped I/O. + * These happen when zm_initialize() is called: + * + * Sender Receiver State + * -------------- ------------ -------- + * "rz\r" ----> N/A + * ZRQINIT ----> ZMS_START + * <---- ZRINIT + * ZSINIT ----> ZMS_INITACK + * <---- ZACK End-of-Transfer + * + * These happen each time that zm_send() is called: + * + * Sender Receiver State + * -------------- ------------ -------- + * ZFILE ----> ZMS_FILEWAIT + * <---- ZRPOS + * ZCRC ----> ZMS_CRCWAIT + * <---- ZRPOS + * ZDATA ----> ZMS_SENDING + * Data packets ----> [Perhaps ZMS_SENDWAIT<->ZMS_SENDING] + * Last packet ----> ZMS_SENDDONE + * ZEOF ----> ZMS_SENDEOF + * <---- ZRINIT + * ZFIN ----> ZMS_FINISH + * <---- ZFIN End-of-Transfer + * + * And, finally, when zm_release() is called: + * + * Sender Receiver State + * -------------- ------------ -------- + * OO ----> + */ + +enum zmodem_state_e +{ + ZMS_START = 0, /* ZRQINIT sent, waiting for ZRINIT from receiver */ + ZMS_INITACK, /* Received ZRINIT, sent ZSINIT, waiting for ZACK */ + ZMS_FILEWAIT, /* Sent file header, waiting for ZRPOS */ + ZMS_CRCWAIT, /* Sent file CRC, waiting for ZRPOS */ + ZMS_SENDING, /* Sending data subpackets, ready for interrupt */ + ZMS_SENDWAIT, /* Waiting for ZACK */ + ZMS_SENDDONE, /* File finished, need to send ZEOF */ + ZMS_SENDEOF, /* Sent ZEOF, waiting for ZACK */ + ZMS_FINISH, /* Sent ZFIN, waiting for ZFIN */ + ZMS_COMMAND, /* Waiting for command data */ + ZMS_MESSAGE, /* Waiting for message from received */ + ZMS_DONE /* Finished with the file transfer */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Transition actions */ + +static int zms_rinit(FAR struct zm_state_s *pzm); +static int zms_attention(FAR struct zm_state_s *pzm); +static int zms_challenge(FAR struct zm_state_s *pzm); +static int zms_abort(FAR struct zm_state_s *pzm); +static int zms_ignore(FAR struct zm_state_s *pzm); +static int zms_command(FAR struct zm_state_s *pzm); +static int zms_message(FAR struct zm_state_s *pzm); +static int zms_stderrdata(FAR struct zm_state_s *pzm); +static int zms_initdone(FAR struct zm_state_s *pzm); +static int zms_sendzsinit(FAR struct zm_state_s *pzm); +static int zms_sendfilename(FAR struct zm_state_s *pzm); +static int zms_endoftransfer(FAR struct zm_state_s *pzm); +static int zms_fileskip(FAR struct zm_state_s *pzm); +static int zms_sendfiledata(FAR struct zm_state_s *pzm); +static int zms_sendpacket(FAR struct zm_state_s *pzm); +static int zms_filecrc(FAR struct zm_state_s *pzm); +static int zms_sendack(FAR struct zm_state_s *pzm); +static int zms_sendwaitack(FAR struct zm_state_s *pzm); +static int zms_sendnak(FAR struct zm_state_s *pzm); +static int zms_sendrpos(FAR struct zm_state_s *pzm); +static int zms_senddoneack(FAR struct zm_state_s *pzm); +static int zms_resendeof(FAR struct zm_state_s *pzm); +static int zms_xfrdone(FAR struct zm_state_s *pzm); +static int zms_finish(FAR struct zm_state_s *pzm); +static int zms_timeout(FAR struct zm_state_s *pzm); +static int zms_cmdto(FAR struct zm_state_s *pzm); +static int zms_doneto(FAR struct zm_state_s *pzm); +static int zms_error(FAR struct zm_state_s *pzm); + +/* Internal helpers */ + +static int zms_startfiledata(FAR struct zms_state_s *pzms); +static int zms_sendfile(FAR struct zms_state_s *pzms, FAR const char *filename, + FAR const char *rfilename, uint8_t f0, uint8_t f1); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* Events handled in state ZMS_START - ZRQINIT sent, waiting for ZRINIT from + * receiver + */ + +static const struct zm_transition_s g_zms_start[] = +{ + {ZME_RINIT, true, ZMS_START, zms_rinit}, + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_CHALLENGE, true, ZMS_START, zms_challenge}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_NAK, false, ZMS_START, zms_ignore}, + {ZME_COMMAND, false, ZMS_COMMAND, zms_command}, + {ZME_STDERR, false, ZMS_MESSAGE, zms_message}, + {ZME_TIMEOUT, false, ZMS_START, zms_timeout}, + {ZME_ERROR, false, ZMS_START, zms_error}, +}; + +/* Events handled in state ZMS_INITACK - Received ZRINIT, sent (optional) + * ZSINIT, waiting for ZACK + */ + +static const struct zm_transition_s g_zms_init[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_ACK, true, ZMS_INITACK, zms_initdone}, + {ZME_NAK, true, ZMS_INITACK, zms_sendzsinit}, + {ZME_RINIT, true, ZMS_INITACK, zms_rinit}, + {ZME_CHALLENGE, true, ZMS_INITACK, zms_challenge}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_COMMAND, false, ZMS_COMMAND, zms_command}, + {ZME_STDERR, false, ZMS_MESSAGE, zms_message}, + {ZME_TIMEOUT, false, ZMS_INITACK, zms_timeout}, + {ZME_ERROR, false, ZMS_INITACK, zms_error}, +}; + +/* Events handled in state ZMS_FILEWAIT- Sent file header, waiting for ZRPOS */ + +static const struct zm_transition_s g_zms_filewait[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_RPOS, true, ZMS_SENDING, zms_sendfiledata}, + {ZME_SKIP, true, ZMS_FILEWAIT, zms_fileskip}, + {ZME_CRC, true, ZMS_FILEWAIT, zms_filecrc}, + {ZME_NAK, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_CHALLENGE, true, ZMS_FILEWAIT, zms_challenge}, + {ZME_COMMAND, false, ZMS_COMMAND, zms_command}, + {ZME_STDERR, false, ZMS_MESSAGE, zms_message}, + {ZME_TIMEOUT, false, ZMS_FILEWAIT, zms_timeout}, + {ZME_ERROR, false, ZMS_FILEWAIT, zms_error}, +}; + +/* Events handled in state ZMS_CRCWAIT - Sent file CRC, waiting for ZRPOS + * response. + */ + +static const struct zm_transition_s g_zms_crcwait[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_RPOS, true, ZMS_SENDING, zms_sendfiledata}, + {ZME_SKIP, true, ZMS_FILEWAIT, zms_fileskip}, + {ZME_NAK, true, ZMS_CRCWAIT, zms_filecrc}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_CRC, false, ZMS_CRCWAIT, zms_filecrc}, + {ZME_CHALLENGE, false, ZMS_CRCWAIT, zms_challenge}, + {ZME_COMMAND, false, ZMS_COMMAND, zms_command}, + {ZME_STDERR, false, ZMS_MESSAGE, zms_message}, + {ZME_TIMEOUT, false, ZMS_CRCWAIT, zms_timeout}, + {ZME_ERROR, false, ZMS_CRCWAIT, zms_error}, +}; + +/* Events handled in state ZMS_SENDING - Sending data subpackets, ready for + * interrupt + */ + +static const struct zm_transition_s g_zms_sending[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_ACK, false, ZMS_SENDING, zms_sendack}, + {ZME_RPOS, true, ZMS_SENDING, zms_sendrpos}, + {ZME_SKIP, true, ZMS_FILEWAIT, zms_fileskip}, + {ZME_NAK, true, ZMS_SENDING, zms_sendnak}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_TIMEOUT, false, ZMS_SENDING, zms_sendpacket}, + {ZME_ERROR, false, ZMS_SENDING, zms_error}, +}; + +/* Events handled in state ZMS_SENDWAIT - Waiting for ZACK */ + +static const struct zm_transition_s g_zms_sendwait[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_ACK, false, ZMS_SENDING, zms_sendwaitack}, + {ZME_RPOS, false, ZMS_SENDWAIT, zms_sendrpos}, + {ZME_SKIP, true, ZMS_FILEWAIT, zms_fileskip}, + {ZME_NAK, false, ZMS_SENDING, zms_sendnak}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_TIMEOUT, false, ZMS_SENDWAIT, zms_timeout}, + {ZME_ERROR, false, ZMS_SENDWAIT, zms_error}, +}; + +/* Events handled in state ZMS_SENDDONE - File sent, need to send ZEOF */ + +static const struct zm_transition_s g_zms_senddone[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_ACK, false, ZMS_SENDWAIT, zms_senddoneack}, + {ZME_RPOS, true, ZMS_SENDING, zms_sendrpos}, + {ZME_SKIP, true, ZMS_FILEWAIT, zms_fileskip}, + {ZME_NAK, true, ZMS_SENDING, zms_sendnak}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_ERROR, false, ZMS_SENDWAIT, zms_error}, +}; + +/* Events handled in state ZMS_SENDEOF - Sent ZEOF, waiting for ZACK or + * ZRINIT + * + * Paragraph 8.2: "The sender sends a ZEOF header with the file ending + * offset equal to the number of characters in the file. The receiver + * compares this number with the number of characters received. If the + * receiver has received all of the file, it closes the file. If the + * file close was satisfactory, the receiver responds with ZRINIT. If + * the receiver has not received all the bytes of the file, the receiver + * ignores the ZEOF because a new ZDATA is coming. If the receiver cannot + * properly close the file, a ZFERR header is sent. + */ + +const struct zm_transition_s g_zms_sendeof[] = +{ + {ZME_RINIT, true, ZMS_START, zms_endoftransfer}, + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_ACK, false, ZMS_SENDEOF, zms_ignore}, + {ZME_RPOS, true, ZMS_SENDWAIT, zms_sendrpos}, + {ZME_SKIP, true, ZMS_START, zms_fileskip}, + {ZME_NAK, true, ZMS_SENDEOF, zms_resendeof}, + {ZME_RINIT, true, ZMS_FILEWAIT, zms_sendfilename}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_TIMEOUT, false, ZMS_SENDEOF, zms_timeout}, + {ZME_ERROR, false, ZMS_SENDEOF, zms_error}, +}; + +/* Events handled in state ZMS_FINISH - Sent ZFIN, waiting for ZFIN */ + +static const struct zm_transition_s g_zms_finish[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_FIN, true, ZMS_DONE, zms_xfrdone}, + {ZME_NAK, true, ZMS_FINISH, zms_finish}, + {ZME_RINIT, true, ZMS_FINISH, zms_finish}, + {ZME_ABORT, true, ZMS_FINISH, zms_abort}, + {ZME_FERR, true, ZMS_FINISH, zms_abort}, + {ZME_TIMEOUT, false, ZMS_FINISH, zms_timeout}, + {ZME_ERROR, false, ZMS_FINISH, zms_error} +}; + +/* Events handled in state ZMS_COMMAND - Waiting for command data */ + +static struct zm_transition_s g_zms_command[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_DATARCVD, false, ZMS_COMMAND, zms_ignore}, + {ZME_TIMEOUT, false, ZMS_COMMAND, zms_cmdto}, + {ZME_ERROR, false, ZMS_COMMAND, zms_error} +}; + +/* Events handled in state ZMS_MESSAGE - Waiting for stderr data */ + +static struct zm_transition_s g_zms_stderr[] = +{ + {ZME_SINIT, false, ZMS_START, zms_attention}, + {ZME_DATARCVD, false, ZMS_MESSAGE, zms_stderrdata}, + {ZME_TIMEOUT, false, ZMS_MESSAGE, zms_cmdto}, + {ZME_ERROR, false, ZMS_MESSAGE, zms_error} +}; + +/* Events handled in state ZMS_DONE - Finished with transfer */ + +static struct zm_transition_s g_zms_done[] = +{ + {ZME_TIMEOUT, false, ZMS_DONE, zms_doneto}, + {ZME_ERROR, false, ZMS_DONE, zms_error} +}; + +/* State x Event table for Zmodem receive. The order of states must + * exactly match the order defined in enum zms_e + */ + +static FAR const struct zm_transition_s * const g_zms_evtable[] = +{ + g_zms_start, /* ZMS_START - ZRQINIT sent, waiting for ZRINIT from receiver */ + g_zms_init, /* ZMS_INITACK - Received ZRINIT, sent ZSINIT, waiting for ZACK */ + g_zms_filewait, /* ZMS_FILEWAIT - Sent file header, waiting for ZRPOS */ + g_zms_crcwait, /* ZMS_CRCWAIT - Sent file CRC, waiting for ZRPOS response */ + g_zms_sending, /* ZMS_SENDING - Sending data subpackets, ready for interrupt */ + g_zms_sendwait, /* ZMS_SENDWAIT - Waiting for ZACK */ + g_zms_senddone, /* ZMS_SENDDONE - File sent, need to send ZEOF */ + g_zms_sendeof, /* ZMS_SENDEOF - Sent ZEOF, waiting for ZACK */ + g_zms_finish, /* ZMS_FINISH - Sent ZFIN, waiting for ZFIN */ + g_zms_command, /* ZMS_COMMAND - Waiting for command data */ + g_zms_stderr, /* ZMS_MESSAGE - Waiting for message from receiver */ + g_zms_done /* ZMS_DONE - Finished with transfer */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zms_rinit + * + * Description: + * Received ZRINIT. Usually received while in start state, this can + * also be an attempt to resync after a protocol failure. + * + ****************************************************************************/ + +static int zms_rinit(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + uint16_t rcaps; + + zmdbg("ZMS_STATE %d\n", pzm->state); + + /* hdrdata[0] is the header type; header[1-4] is payload: + * + * F0 and F1 contain the bitwise OR of the receiver capability flags + * P0 and ZP1 contain the size of the receiver's buffer in bytes (or 0 + * if nonstop I/O is allowed. + */ + + pzms->rcvmax = (uint16_t)pzm->hdrdata[2] << 8 | (uint16_t)pzm->hdrdata[1]; + rcaps = (uint16_t)pzm->hdrdata[3] << 8 | (uint16_t)pzm->hdrdata[4]; + + /* Set flags associated with the capabilities */ + + rcaps &= ~(ZM_FLAG_CRC32 | ZM_FLAG_ESCCTRL); + if ((rcaps & CANFC32) != 0) + { + pzm->flags |= ZM_FLAG_CRC32; + } + + if ((rcaps & ESCCTL) != 0) + { + pzm->flags |= ZM_FLAG_ESCCTRL; + } + + /* Enable hardware flow control if we will be streaming */ + +#ifdef CONFIG_SYSTEM_ZMODEM_FULLSTREAMING + (void)zms_hwflowcontrol(pzmr->cmn.remfd, true); +#endif + +#if defined(CONFIG_SYSTEM_ZMODEM_SENDSAMPLE) || defined(CONFIG_SYSTEM_ZMODEM_SENDATTN) + if ((rcaps & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) && pzms->rcvmax == 0) + { + pzms->strtype = ZCRCG; + } + else +#else + if ((rcaps & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) && pzms->rcvmax == 0) + { + pzms->strtype = ZCRCQ; + } + else +#endif + { + pzms->strtype = ZCRCG; + } + +#ifdef CONFIG_SYSTEM_ZMODEM_ALWAYSSINT + return zms_sendzsinit(pzm); +#else +# ifdef CONFIG_SYSTEM_ZMODEM_SENDATTN + if (pzms->attn != NULL) + { + return zms_sendzsinit(pzm); + } + else +# endif + { + return ZM_XFRDONE; + } +#endif +} + +/**************************************************************************** + * Name: zms_attention + * + * Description: + * Received ZSINIT. The receiver wants something. Set the interrupt flag + * and handle this later in zms_sendpacket(). + * + ****************************************************************************/ + +static int zms_attention(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + pzm->flags |= ZM_FLAG_INTERRUPT; + return OK; +} + +/**************************************************************************** + * Name: zms_challenge + * + * Description: + * Answer challenge from receiver + * + ****************************************************************************/ + +static int zms_challenge(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + return zm_sendhexhdr(pzm, ZACK, pzm->hdrdata + 1); +} + +/**************************************************************************** + * Name: zms_abort + * + * Description: + * Receiver has cancelled + * + ****************************************************************************/ + +static int zms_abort(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + return zm_sendhexhdr(pzm, ZFIN, g_zeroes); +} + +/**************************************************************************** + * Name: zms_ignore + * + * Description: + * Ignore the header + * + ****************************************************************************/ + +static int zms_ignore(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + return OK; +} + +/**************************************************************************** + * Name: zms_command + * + * Description: + * Remote command received -- refuse it. + * + ****************************************************************************/ + +static int zms_command(FAR struct zm_state_s *pzm) +{ + uint8_t rbuf[4]; + + zmdbg("ZMS_STATE %d\n", pzm->state); + + rbuf[0] = EPERM; + rbuf[1] = 0; + rbuf[2] = 0; + rbuf[3] = 0; + return zm_sendhexhdr(pzm, ZCOMPL, rbuf); +} + +/**************************************************************************** + * Name: zms_message + * + * Description: + * The remote system wants to put a message on stderr + * + ****************************************************************************/ + +static int zms_message(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + zm_readstate(pzm); + return OK; +} + +/**************************************************************************** + * Name: zms_stderrdata + * + * Description: + * The remote system wants to put a message on stderr + * + ****************************************************************************/ + +static int zms_stderrdata(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d\n", pzm->state); + pzm->rcvbuf[pzm->rcvndx] = '\0'; + fprintf(stderr, "Message: %s", (char*)pzm->rcvbuf); + return OK; +} + +/**************************************************************************** + * Name: zms_initdone + * + * Description: + * Received ZRINIT, sent ZRINIT, waiting for ZACK, ZACK received. This + * completes the initialization sequence. Returning ZM_XFRDONE will + * alloc zm_initialize() to return. + * + ****************************************************************************/ + +static int zms_initdone(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d->%d\n", pzm->state, ZMS_DONE); + pzm->state = ZMS_DONE; + return ZM_XFRDONE; +} + +/**************************************************************************** + * Name: zms_sendzsinit + * + * Description: + * The remote system wants to put a message on stderr + * + ****************************************************************************/ + +static int zms_sendzsinit(FAR struct zm_state_s *pzm) +{ +#ifdef CONFIG_SYSTEM_ZMODEM_SENDATTN + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + FAR char *at = (pzms->attn != NULL) ? pzms->attn : ""; +#endif + int ret; + + /* Change to ZMS_INITACK state */ + + zmdbg("ZMS_STATE %d->%d\n", pzm->state, ZMS_INITACK); + pzm->state = ZMS_INITACK; + + /* Send the ZSINIT header (optional) + * + * Paragraph 11.3 ZSINIT. "The Sender sends flags followed by a binary + * data subpacket terminated with ZCRCW." + */ + + ret = zm_sendbinhdr(pzm, ZSINIT, g_zeroes); + if (ret >= 0) + { + /* Paragraph 11.3 "The data subpacket contains the null terminated + * Attn sequence, maximum length 32 bytes including the terminating + * null." + * + * We expect a ZACK next. + */ + +#ifdef CONFIG_SYSTEM_ZMODEM_SENDATTN + /* Send the NUL-terminated attention string */ + + ret = zm_senddata(pzm, (FAR uint8_t *)at, strlen(at) + 1); +#else + /* Send a null string */ + + ret = zm_senddata(pzm, g_zeroes, 1); +#endif + } + + return ret; +} + +/**************************************************************************** + * Name: zms_sendfilename + * + * Description: + * Send ZFILE header and filename. Wait for a response from receiver. + * + ****************************************************************************/ + +static int zms_sendfilename(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + FAR uint8_t *ptr = pzm->scratch; + int len; + int ret; + + zmdbg("ZMS_STATE %d->%d\n", pzm->state, ZMS_FILEWAIT); + + pzm->state = ZMS_FILEWAIT; + ret = zm_sendbinhdr(pzm, ZFILE, pzms->fflags); + if (ret < 0) + { + zmdbg("ERROR: zm_sendbinhdr failed: %d\n", ret); + return ret; + } + + /* Paragraph 13: + * Pathname + * The pathname (conventionally, the file name) is sent as a null + * terminated ASCII string. + */ + + len = strlen(pzms->rfilename); + memcpy(ptr, pzms->rfilename, len + 1); + ptr += len + 1; + + /* Paragraph 13: + * + * Length + * The file length ... is stored as a decimal string counting the + * number of data bytes in the file. + * Modification Date + * A single space separates the modification date from the file + * length. ... The mod date is sent as an octal number giving ... + * A date of 0 implies the modification date is unknown and should be + * left as the date the file is received. + * File Mode + * A single space separates the file mode from the modification date. + * The file mode is stored as an octal string. Unless the file + * originated from a Unix system, the file mode is set to 0. + * Serial Number + * A single space separates the serial number from the file mode. + * The serial number of the transmitting program is stored as an + * octal string. Programs which do not have a serial number should + * omit this field, or set it to 0. + * Number of Files Remaining + * Iff the number of files remaining is sent, a single space separates + * this field from the previous field. This field is coded as a + * decimal number, and includes the current file. + * Number of Bytes Remaining + * Iff the number of bytes remaining is sent, a single space + * separates this field from the previous field. This field is coded + * as a decimal number, and includes the current file + * File Type + * Iff the file type is sent, a single space separates this field from + * the previous field. This field is coded as a decimal number. + * Currently defined values are: + * + * 0 Sequential file - no special type + * 1 Other types to be defined. + * + * The file information is terminated by a null. + */ + +#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS + sprintf((FAR char *)ptr, "%ld %lo 0 %d 1 %ld 0", + (unsigned long)pzms->filesize, (unsigned long)pzms->timestamp, + CONFIG_SYSTEM_ZMODEM_SERIALNO, (unsigned long)pzms->filesize); +#else + sprintf((FAR char *)ptr, "%ld 0 0 %d 1 %ld 0", + (unsigned long)pzms->filesize, CONFIG_SYSTEM_ZMODEM_SERIALNO, + (unsigned long)pzms->filesize); +#endif + + ptr += strlen((char *)ptr); + *ptr++ = '\0'; + + len = ptr - pzm->scratch; + DEBUGASSERT(len < CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE); + return zm_senddata(pzm, pzm->scratch, len); +} + +/**************************************************************************** + * Name: zms_fileskip + * + * Description: + * The entire file has been transferred, ZEOF has been sent to the remote + * receiver, and the receiver has returned ZRINIT. Time to send ZFIN. + * + ****************************************************************************/ + +static int zms_endoftransfer(FAR struct zm_state_s *pzm) +{ + zmdbg("ZMS_STATE %d send ZFIN\n", pzm->state); + pzm->state = ZMS_FINISH; + + return zm_sendhexhdr(pzm, ZFIN, g_zeroes); +} + +/**************************************************************************** + * Name: zms_fileskip + * + * Description: + * Received ZSKIP, receiver doesn't want this file. + * + ****************************************************************************/ + +static int zms_fileskip(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + + zmdbg("ZMS_STATE %d\n", pzm->state); + close(pzms->infd); + pzms->infd = -1; + return ZM_XFRDONE; +} + +/**************************************************************************** + * Name: zms_sendfiledata + * + * Description: + * Send a chunk of file data in response to a ZRPOS. This may be followed + * followed by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW dependinig on protocol flags. + * + ****************************************************************************/ + +static int zms_sendfiledata(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + + pzm->flags &= ~ZM_FLAG_WAIT; + return zms_startfiledata(pzms); +} + +/**************************************************************************** + * Name: zms_sendpacket + * + * Description: + * Send a chunk of file data in response to a ZRPOS. This may be followed + * followed by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW dependinig on protocol flags. + * + * This function is called after ZDATA is send, after previous data was + * ACKed, and on certain error conditions where it is necessary to re-send + * the file data. + * + ****************************************************************************/ + +static int zms_sendpacket(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + ssize_t nwritten; + int32_t unacked; + bool bcrc32; + uint32_t crc; + uint8_t by[4]; + uint8_t *ptr; + uint8_t type; + bool wait = false; + int sndsize; + int pktsize; + int ret; + int i; + + /* ZCRCE: CRC next, frame ends, header follows ZCRCG: CRC next, frame + * continues nonstop ZCRCQ: CRC next, send ZACK, frame continues nonstop + * ZCRCW: CRC next, send ZACK, frame ends, header follows. + */ + + if ((pzm->flags & ZM_FLAG_INTERRUPT) != 0) + { + /* Receiver sent an interrupt. Enter a wait state and see what + * they want. Next header *should* be ZRPOS. + */ + + zmdbg("ZMS_STATE %d->%d: Interrupt\n", pzm->state, ZMS_SENDWAIT); + + pzm->state = ZMS_SENDWAIT; + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + pzm->flags &= ~ZM_FLAG_INTERRUPT; + return 0; + } + + /* This is the number of byte left in the file to be sent */ + + sndsize = pzms->filesize - pzms->offset; + + /* This is the nubmer of bytes that have been sent but not yet ackowledged. */ + + unacked = pzms->offset - pzms->lastoffs; + + /* Can we still send? If so, how much? If rcvmax is zero, then the + * remote can handle full streaming and we never have to wait. Otherwise, + * we have to restrict the total number of unacknowledged bytes to rcvmax. + */ + + zmdbg("sndsize: %d unacked: %d rcvmax: %d\n", + sndsize, unacked, pzms->rcvmax); + + if (pzms->rcvmax != 0) + { + /* If we were to send 'sndsize' more bytes, would that exceed recvmax? */ + + if (sndsize + unacked > pzms->rcvmax) + { + /* Yes... clip the maximum so that we stay within that limit */ + + int maximum = pzms->rcvmax - unacked; + if (sndsize < maximum) + { + sndsize = maximum; + } + + wait = true; + zmdbg("Clipped sndsize: %d\n", sndsize); + } + } + + /* Can we send anything? */ + + if (sndsize <= 0) + { + /* No, not now. Keep waiting */ + + zmdbg("ZMS_STATE %d->%d\n", pzm->state, ZMS_SENDWAIT); + + pzm->state = ZMS_SENDWAIT; + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + return OK; + } + + /* Determine what kind of packet to send */ + + if ((pzm->flags & ZM_FLAG_WAIT) != 0) + { + type = ZCRCW; + pzm->flags &= ~ZM_FLAG_WAIT; + } + else if (wait) + { + type = ZCRCW; + } + else + { + type = pzms->strtype; + } + + /* Read characters from file and put into buffer until buffer is full or + * file is exhausted + */ + + bcrc32 = ((pzm->flags & ZM_FLAG_CRC32) != 0); + crc = bcrc32 ? 0xffffffff : 0; + pzm->flags &= ~ZM_FLAG_ATSIGN; + + ptr = pzm->scratch; + pktsize = 0; + + while (pktsize <= (CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE - 10) && + (ret = zm_getc(pzms->infd)) != EOF) + { + /* Add the new value to the accumulated CRC */ + + uint8_t ch = (uint8_t)ret; + if (!bcrc32) + { + crc = (uint32_t)crc16part(&ch, 1, (uint16_t)crc); + } + else + { + crc = crc32part(&ch, 1, crc); + } + + /* Put the character into the buffer, escaping as necessary */ + + ptr = zm_putzdle(pzm, ptr, ch); + + /* Recalculate the accumulated packet size to handle expansion due to + * escaping. + */ + + pktsize = (int32_t)(ptr - pzm->scratch); + + /* And increment the file offset */ + + pzms->offset++; + } + + /* If we've reached file end, a ZEOF header will follow. If there's room + * in the outgoing buffer for it, end the packet with ZCRCE and append the + * ZEOF header. If there isn't room, we'll have to do a ZCRCW + */ + + pzm->flags &= ~ZM_FLAG_EOF; + if (ret == EOF) + { + pzm->flags |= ZM_FLAG_EOF; + if (wait || (pzms->rcvmax != 0 && pktsize < 24)) + { + type = ZCRCW; + } + else + { + type = ZCRCE; + } + } + + /* Save the ZDLE in the transmit buffer */ + + *ptr++ = ZDLE; + + /* Save the type */ + + if (!bcrc32) + { + crc = (uint32_t)crc16part(&type, 1, (uint16_t)crc); + } + else + { + crc = crc32part(&type, 1, crc); + } + + *ptr++ = type; + + /* Update the CRC and put the CRC in the transmit buffer */ + + if (!bcrc32) + { + 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); + } + } + + /* Get the final packet size */ + + pktsize = ptr - pzm->scratch; + DEBUGASSERT(pktsize < CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE); + + /* And send the packet */ + + dbg("Sending %d bytes. New offset: %ld\n", + pktsize, (unsigned long)pzms->offset); + + nwritten = zm_remwrite(pzm->remfd, pzm->scratch, pktsize); + if (nwritten < 0) + { + zmdbg("ERROR: zm_remwrite failed: %d\n", (int)nwritten); + return (int)nwritten; + } + + /* Then do what? That depends on the type of the transfer */ + + switch (type) + { + /* That was the last packet. Send ZCRCE to indicate the end of file */ + + case ZCRCE: /* CRC next, frame ends, header follows */ + zmdbg("ZMS_STATE %d->%d: ZCRCE\n", pzm->state, ZMS_SENDEOF); + + pzm->state = ZMS_SENDEOF; + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + zm_be32toby(pzms->offset, by); + return zm_sendhexhdr(pzm, ZEOF, by); + + /* We need to want for ZACK */ + + case ZCRCW: /* CRC next, send ZACK, frame ends */ + if ((pzm->flags & ZM_FLAG_EOF) != 0) + { + zmdbg("ZMS_STATE %d->%d: EOF\n", pzm->state, ZMS_SENDDONE); + pzm->state = ZMS_SENDDONE; + } + else + { + zmdbg("ZMS_STATE %d->%d: Not EOF\n", pzm->state, ZMS_SENDWAIT); + pzm->state = ZMS_SENDWAIT; + } + + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + break; + + /* No response is expected */ + + case ZCRCG: /* CRC next, frame continues nonstop */ + case ZCRCQ: /* CRC next, send ZACK, frame continues nonstop */ + default: + zmdbg("ZMS_STATE %d->%d: Default\n", pzm->state, ZMS_SENDING); + + pzm->state = ZMS_SENDING; + pzm->timeout = 0; + break; + } + + return OK; +} + +/**************************************************************************** + * Name: zms_filecrc + * + * Description: + * ZFILE has been sent and waiting for ZCRC. ZRPOS received. + * + ****************************************************************************/ + +static int zms_filecrc(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + uint8_t by[4]; + uint32_t crc; + + crc = zm_filecrc(pzm, pzms->filename); + zmdbg("ZMS_STATE %d: CRC %08x\n", pzm->state, crc); + + zm_be32toby(crc, by); + return zm_sendhexhdr(pzm, ZCRC, by); +} + +/**************************************************************************** + * Name: zms_sendack + * + * Description: + * An ACK arrived while transmitting data. Update last known receiver + * offset, and try to send more data. + * + ****************************************************************************/ + +static int zms_sendack(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + off_t offset; + + /* Paragraph 11.4 ZACK. Acknowledgment to a ZSINIT frame, ..., ZCRCQ or + * ZCRCW data subpacket. ZP0 to ZP3 contain file offset. + */ + + offset = zm_bytobe32(pzm->hdrdata + 1); + + if (offset > pzms->lastoffs) + { + pzms->lastoffs = offset; + } + + zmdbg("ZMS_STATE %d: offset: %ld\n", pzm->state, (unsigned long)pzms->offset); + return OK; +} + +/**************************************************************************** + * Name: zms_sendwaitack + * + * Description: + * An ACK arrived while waiting to transmit data. Update last known + * receiver offset, and try to send more data. + * + ****************************************************************************/ + +static int zms_sendwaitack(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + uint8_t by[4]; + off_t offset; + int ret; + + /* Paragraph 11.4 ZACK. Acknowledgment to a ZSINIT frame, ..., ZCRCQ or + * ZCRCW data subpacket. ZP0 to ZP3 contain file offset. + */ + + offset = zm_bytobe32(pzm->hdrdata + 1); + + if (offset > pzms->lastoffs) + { + pzms->lastoffs = offset; + } + + zmdbg("ZMS_STATE %d: offset: %ld\n", pzm->state, (unsigned long)offset); + + /* Now send the next data packet */ + + zm_be32toby(pzms->offset, by); + ret = zm_sendbinhdr(pzm, ZDATA, by); + if (ret != OK) + { + return ret; + } + + return zms_sendpacket(pzm); +} + +/**************************************************************************** + * Name: zms_sendnak + * + * Description: + * ZDATA header was corrupt. Start from beginning + * + ****************************************************************************/ + +static int zms_sendnak(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + off_t offset; + + /* Save the ZRPOS file offset */ + + pzms->offset = pzms->zrpos; + + /* TODO: What is the correct thing to do if lseek fails? Send ZEOF? */ + + offset = lseek(pzms->infd, pzms->offset, SEEK_SET); + if (offset == (off_t)-1) + { + int errorcode = errno; + + zmdbg("ERROR: Failed to seek to %ld: %d\n", + (unsigned long)pzms->offset, errorcode); + DEBUGASSERT(errorcode > 0); + return -errorcode; + } + + zmdbg("ZMS_STATE %d: offset: %ld\n", pzm->state, (unsigned long)pzms->offset); + + return zms_sendpacket(pzm); +} + +/**************************************************************************** + * Name: zms_sendrpos + * + * Description: + * Received ZRPOS while sending a file. Set the new offset and try again. + * + ****************************************************************************/ + +static int zms_sendrpos(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + + pzm->nerrors++; + pzm->flags |= ZM_FLAG_WAIT; + return zms_startfiledata(pzms); +} + +/**************************************************************************** + * Name: zms_senddoneack + * + * Description: + * ACK arrived after last file packet sent. Send the ZEOF + * + ****************************************************************************/ + +static int zms_senddoneack(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + uint8_t by[4]; + off_t offset; + + /* Paragraph 11.4 ZACK. Acknowledgment to a ZSINIT frame, ..., ZCRCQ or + * ZCRCW data subpacket. ZP0 to ZP3 contain file offset. + */ + + offset = zm_bytobe32(pzm->hdrdata + 1); + + if (offset > pzms->lastoffs) + { + pzms->lastoffs = offset; + } + + zmdbg("ZMS_STATE %d->%d: offset: %ld\n", + pzm->state, ZMS_SENDEOF, (unsigned long)pzms->offset); + + /* Now send the ZEOF */ + + pzm->state = ZMS_SENDEOF; + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + zm_be32toby(pzms->offset, by); + return zm_sendhexhdr(pzm, ZEOF, by); +} + +/**************************************************************************** + * Name: zms_resendeof + * + * Description: + * ACK arrived after last file packet sent. Send the ZEOF + * + ****************************************************************************/ + +static int zms_resendeof(FAR struct zm_state_s *pzm) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)pzm; + uint8_t by[4]; + + zm_be32toby(pzms->offset, by); + return zm_sendhexhdr(pzm, ZEOF, by); +} + +/**************************************************************************** + * Name: zms_xfrdone + * + * Description: + * Sent ZFIN, received ZFIN in response. This transfer is complete. + * We will not send the "OO" until the last file has been sent. + * + ****************************************************************************/ + +static int zms_xfrdone(FAR struct zm_state_s *pzm) +{ + zmdbg("Transfer complete\n"); + return ZM_XFRDONE; +} + +/**************************************************************************** + * Name: zms_finish + * + * Description: + * Sent ZFIN, received ZNAK or ZRINIT + * + ****************************************************************************/ + +static int zms_finish(FAR struct zm_state_s *pzm) +{ + return ZM_XFRDONE; +} + +/**************************************************************************** + * Name: zms_timeout + * + * Description: + * An timeout occurred in this state + * + ****************************************************************************/ + +static int zms_timeout(FAR struct zm_state_s *pzm) +{ + zmdbg("ERROR: Receiver did not respond\n"); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: zms_cmdto + * + * Description: + * Timed out waiting for command or stderr data + * + ****************************************************************************/ + +static int zms_cmdto(FAR struct zm_state_s *pzm) +{ + zmdbg("ERROR: No command received\n"); + return -ETIMEDOUT; +} + +/**************************************************************************** + * Name: zms_doneto + * + * Description: + * Timed out in ZMS_DONE state + * + ****************************************************************************/ + +static int zms_doneto(FAR struct zm_state_s *pzm) +{ + zmdbg("Timeout if ZMS_DONE\n"); + return ZM_XFRDONE; +} + +/**************************************************************************** + * Name: zms_error + * + * Description: + * An unexpected event occurred in this state + * + ****************************************************************************/ + +static int zms_error(FAR struct zm_state_s *pzm) +{ + zmdbg("Unhandled event, header=%d\n", pzm->hdrdata[0]); + pzm->flags |= ZM_FLAG_WAIT; + return OK; +} + +/**************************************************************************** + * Name: zms_sendfiledata + * + * Description: + * Send a chunk of file data in response to a ZRPOS. This may be followed + * followed by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW dependinig on protocol flags. + * + ****************************************************************************/ + +static int zms_startfiledata(FAR struct zms_state_s *pzms) +{ + off_t offset; + int ret; + + zmdbg("ZMS_STATE %d: offset %ld nerrors %d\n", + pzms->cmn.state, (unsigned long)pzms->offset, pzms->cmn.nerrors); + + /* Paragraph 8.2: "A ZRPOS header from the receiver initiates transmission + * of the file data starting at the offset in the file specified in the + * ZRPOS header." + */ + + pzms->zrpos = zm_bytobe32(pzms->cmn.hdrdata + 1); + pzms->offset = pzms->zrpos; + pzms->lastoffs = pzms->zrpos; + + /* See to the requested file position */ + + offset = lseek(pzms->infd, pzms->offset, SEEK_SET); + if (offset == (off_t)-1) + { + int errorcode = errno; + + zmdbg("ERROR: Failed to seek to %ld: %d\n", + (unsigned long)pzms->offset, errorcode); + DEBUGASSERT(errorcode > 0); + return -errorcode; + } + + /* Paragraph 8.2: "The sender sends a ZDATA binary header (with file + * position) followed by one or more data subpackets." + */ + + zmdbg("ZMS_STATE %d: Send ZDATA offset %ld\n", + pzms->cmn.state, (unsigned long)pzms->offset); + + ret = zm_sendbinhdr(&pzms->cmn, ZDATA, pzms->cmn.hdrdata + 1); + if (ret != OK) + { + return ret; + } + + return zms_sendpacket(&pzms->cmn); +} + +/**************************************************************************** + * Name: zms_sendfile + * + * Description: + * Begin transmission of a file + * + ****************************************************************************/ + +/* Called by user to begin transmission of a file */ + +static int zms_sendfile(FAR struct zms_state_s *pzms, FAR const char *filename, + FAR const char *rfilename, uint8_t f0, uint8_t f1) +{ + struct stat buf; + int ret; + + DEBUGASSERT(pzms && filename && rfilename); + zmdbg("filename: %s rfilename: %s f0: %02x f1: %02x\n", + filename, rfilename, f0, f1); + + /* TODO: The local file name *must* be an absolute patch for now. This if + * environment variables are supported, then any relative pathes could be + * extended using the contents of the current working directory CWD. + */ + + if (filename[0] != '/') + { + zmdbg("ERROR: filename must be an absolute path: %s\n", filename); + return -ENOSYS; + } + + /* Get information about the local file */ + + ret = stat(filename, &buf); + if (ret < 0) + { + int errorcode = errno; + DEBUGASSERT(errorcode > 0); + zmdbg("Failed to stat %s: %d\n", filename, errorcode); + return -errorcode; + } + + /* Open the local file for reading */ + + pzms->infd = open(filename, O_RDONLY); + if (pzms->infd < 0) + { + int errorcode = errno; + DEBUGASSERT(errorcode > 0); + zmdbg("Failed to open %s: %d\n", filename, errorcode); + return -errorcode; + } + + /* Initialize for the transfer */ + + pzms->cmn.flags &= ~ZM_FLAG_EOF; + pzms->filename = filename; + pzms->rfilename = rfilename; + DEBUGASSERT(pzms->filename && pzms->rfilename); + + pzms->fflags[3] = f0; + pzms->fflags[2] = f1; + pzms->fflags[1] = 0; + pzms->fflags[0] = 0; + pzms->offset = 0; + pzms->lastoffs = 0; + + pzms->filesize = buf.st_size; +#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS + pzms->timestamp = buf.st_mtime; +#endif + + zmdbg("ZMS_STATE %d->%d\n", pzms->cmn.state, ZMS_FILEWAIT); + + pzms->cmn.state = ZMS_FILEWAIT; + + zmdbg("ZMS_STATE %d: Send ZFILE(%s)\n", pzms->cmn.state, pzms->rfilename); + return zms_sendfilename(&pzms->cmn); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zms_initialize + * + * Description: + * Initialize for Zmodem send operation. The overall usage is as follows: + * + * 1) Call zms_initialize() to create the partially initialized struct + * zms_state_s instance. + * 2) Make any custom settings in the struct zms_state_s instance. + * 3) Create a stream instance to get the "file" to transmit and add + * the filename to pzms + * 4) Call zms_send() to transfer the file. + * 5) Repeat 3) and 4) to transfer as many files as desired. + * 6) Call zms_release() when the final file has been transferred. + * + * Input Parameters: + * remfd - The R/W file/socket descriptor to use for communication with the + * remote peer. + * + ****************************************************************************/ + +ZMSHANDLE zms_initialize(int remfd) +{ + FAR struct zms_state_s *pzms; + FAR struct zm_state_s *pzm; + ssize_t nwritten; + int ret; + + DEBUGASSERT(remfd >= 0); + + /* Allocate the instance */ + + pzms = (FAR struct zms_state_s *)zalloc(sizeof(struct zms_state_s)); + if (pzms) + { + pzm = &pzms->cmn; + pzm->evtable = g_zms_evtable; + pzm->state = ZMS_START; + pzm->pstate = PSTATE_IDLE; + pzm->psubstate = PIDLE_ZPAD; + pzm->remfd = remfd; + + /* Create a timer to handle timeout events */ + + ret = zm_timerinit(pzm); + if (ret < 0) + { + zmdbg("ERROR: zm_timerinit failed: %d\n", ret); + goto errout; + } + + /* Send "rz\r" to the remote end. + * + * Paragraph 8.1: "The sending program may send the string "rz\r" to + * invoke the receiving program from a possible command mode. The + * "rz" followed by carriage return activates a ZMODEM receive program + * or command if it were not already active. + */ + + nwritten = zm_remwrite(pzm->remfd, (uint8_t *) "rz\r", 3); + if (nwritten < 0) + { + zmdbg("ERROR: zm_remwrite failed: %d\n", (int)nwritten); + goto errout_with_timer; + } + + /* Send ZRQINIT + * + * Paragraph 8.1: "Then the sender may send a ZRQINIT header. The + * ZRQINIT header causes a previously started receive program to send + * its ZRINIT header without delay." + */ + + ret = zm_sendhexhdr(&pzms->cmn, ZRQINIT, g_zeroes); + if (ret < 0) + { + zmdbg("ERROR: zm_sendhexhdr failed: %d\n", ret); + goto errout_with_timer; + } + + /* Set up a timeout for the response */ + + pzm->timeout = CONFIG_SYSTEM_ZMODEM_RESPTIME; + zmdbg("ZMS_STATE %d sent ZRQINIT\n", pzm->state); + + /* Now drive the state machine by reading data from the remote peer + * and providing that data to the parser. zm_datapump runs until an + * irrecoverable error is detected or until the ZRQINIT is ACK-ed by + * the remote receiver. + */ + + ret = zm_datapump(&pzms->cmn); + if (ret < 0) + { + zmdbg("ERROR: zm_datapump failed: %d\n", ret); + goto errout_with_timer; + } + } + + return (ZMSHANDLE)pzms; + +errout_with_timer: + (void)zm_timerrelease(&pzms->cmn); +errout: + free(pzms); + return (ZMSHANDLE)NULL; +} + +/**************************************************************************** + * Name: zms_send + * + * Description: + * Send a file. + * + * Input Parameters: + * handle - Handle previoulsy returned by xms_initialize() + * filename - The name of the local file to transfer + * rfilename - The name of the remote file name to create + * option - Describes optional transfer behavior + * f1 - The F1 transfer flags + * skip - True: Skip if file not present at receiving end. + * + * Assumptions: + * The filename and rfilename pointers refer to memory that will persist + * at least until the transfer completes. + * + ****************************************************************************/ + +int zms_send(ZMSHANDLE handle, FAR const char *filename, + FAR const char *rfilename, enum zm_xfertype_e xfertype, + enum zm_option_e option, bool skip) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)handle; + uint8_t f1; + int ret; + + /* At this point either (1) zms_intiialize() has just been called or + * (2) the previous file has been sent. + */ + + DEBUGASSERT(pzms && filename && rfilename && pzms->cmn.state == ZMS_DONE); + + /* Set the ZMSKNOLOC option is so requested */ + + f1 = option; + if (skip) + { + f1 |= ZMSKNOLOC; + } + + /* Initiate sending of the file. This will open the source file, + * initialize data structures and set the ZMSS_FILEWAIT state. + */ + + ret = zms_sendfile(pzms, filename, rfilename, (uint8_t)xfertype, f1); + if (ret < 0) + { + zmdbg("ERROR: zms_sendfile failed: %d\n", ret); + return ret; + } + + /* Now drive the state machine by reading data from the remote peer + * and providing that data to the parser. zm_datapump runs until an + * irrecoverable error is detected or until the file is sent correctly. + */ + + return zm_datapump(&pzms->cmn); +} + +/**************************************************************************** + * Name: zms_release + * + * Description: + * Called by the user when there are no more files to send. + * + ****************************************************************************/ + +int zms_release(ZMSHANDLE handle) +{ + FAR struct zms_state_s *pzms = (FAR struct zms_state_s *)handle; + ssize_t nwritten; + int ret = OK; + + /* Send "OO" */ + + nwritten = zm_remwrite(pzms->cmn.remfd, (FAR const uint8_t*)"OO", 2); + if (nwritten < 0) + { + zmdbg("ERROR: zm_remwrite failed: %d\n", (int)nwritten); + ret = (int)nwritten; + } + + /* Release the timer resources */ + + (void)zm_timerrelease(&pzms->cmn); + + /* And free the Zmodem state structure */ + + free(pzms); + return ret; +} |