nuttx-apps/system/zmodem/zm_send.c
Xiang Xiao 857158451b Unify the void cast usage
1.Remove void cast for function because many place ignore the returned value witout cast
2.Replace void cast for variable with UNUSED macro

Change-Id: Ie644129a563244a6397036789c4c3ea83c4e9b09
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-01-02 23:21:01 +08:00

1731 lines
53 KiB
C

/****************************************************************************
* 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 "system/zmodem.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 ---->
* Data packets ----> ZMS_SENDING /ZMS_SENDWAIT
* 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, /* Streaming 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_zrinit(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_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_zrinit},
{ZME_SINIT, false, ZMS_START, zms_ignore},
{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_zmr_initack[] =
{
{ZME_SINIT, false, ZMS_START, zms_ignore},
{ZME_ACK, true, ZMS_INITACK, zms_initdone},
{ZME_NAK, true, ZMS_INITACK, zms_sendzsinit},
{ZME_RINIT, true, ZMS_INITACK, zms_zrinit},
{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_ignore},
{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_ignore},
{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_zmr_sending[] =
{
{ZME_SINIT, false, ZMS_START, zms_attention},
{ZME_ACK, false, ZMS_SENDING, zms_sendpacket},
{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_ignore},
{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_ignore},
{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_ignore},
{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_ignore},
{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_message[] =
{
{ZME_SINIT, false, ZMS_START, zms_ignore},
{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_zmr_initack, /* 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_zmr_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_message, /* ZMS_MESSAGE: Waiting for message from receiver */
g_zms_done /* ZMS_DONE: Finished with transfer */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: zms_zrinit
*
* Description:
* Received ZRINIT. Usually received while in start state, this can
* also be an attempt to resync after a protocol failure.
*
****************************************************************************/
static int zms_zrinit(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 */
if ((rcaps & CANFC32) != 0)
{
pzm->flags |= ZM_FLAG_CRC32;
}
if ((rcaps & ESCCTL) != 0)
{
pzm->flags |= ZM_FLAG_ESCCTRL;
}
/* Check if the receiver supports full-duplex streaming
*
* ZCRCW:
* "If the receiver cannot overlap serial and disk I/O, it uses the
* ZRINIT frame to specify a buffer length which the sender will
* not overflow. The sending program sends a ZCRCW data subpacket
* and waits for a ZACK header before sending the next segment of
* the file.
*
* ZCRCG
* "A data subpacket terminated by ZCRCG and CRC does not elicit a
* response unless an error is detected; more data subpacket(s)
* follow immediately."
*
*
* In order to support ZCRCG, this logic must be able to sample the
* reverse channel while streaming to determine if the receiving wants
* interrupt the transfer (CONFIG_SYSTEM_ZMODEM_RCVSAMPLE).
*
* ZCRCQ
* "ZCRCQ data subpackets expect a ZACK response with the
* receiver's file offset if no error, otherwise a ZRPOS response
* with the last good file offset. Another data subpacket
* continues immediately. ZCRCQ subpackets are not used if the
* receiver does not indicate FDX ability with the CANFDX bit.
*/
#ifdef CONFIG_SYSTEM_ZMODEM_RCVSAMPLE
/* We support CANFDX. We can do ZCRCG if the remote sender does too */
if ((rcaps & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) && pzms->rcvmax == 0)
{
pzms->dpkttype = ZCRCG;
}
#else
/* We don't support CANFDX. We can do ZCRCQ if the remote sender does */
if ((rcaps & (CANFDX | CANOVIO)) == (CANFDX | CANOVIO) && pzms->rcvmax == 0)
{
/* For the local sender, this is just like ZCRCW */
pzms->dpkttype = ZCRCQ;
}
#endif
/* Otherwise, we have to do ZCRCW */
else
{
pzms->dpkttype = ZCRCW;
}
#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
{
zmdbg("ZMS_STATE %d->%d\n", pzm->state, );
pzm->state = ZMS_DONE;
return ZM_XFRDONE;
}
#endif
}
/****************************************************************************
* Name: zms_attention
*
* Description:
* Received ZSINIT while sending data. The receiver wants something.
* Switch tot he ZMS_SENDWAIT state and wait. A ZRPOS should be forth-
* coming.
*
****************************************************************************/
static int zms_attention(FAR struct zm_state_s *pzm)
{
zmdbg("ZMS_STATE %d\n", pzm->state);
/* In the case of full streaming, the presence of pending read data should
* cause the sending logic to break out of the loop and handle the received
* data, getting us to this point.
*/
if (pzm->state == ZMS_SENDING || pzm->state == ZMS_SENDWAIT)
{
/* 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;
}
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->pktbuf[pzm->pktlen] = '\0';
fprintf(stderr, "Message: %s", (char*)pzm->pktbuf);
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;
/* Loop, sending packets while we can if the receiver supports streaming
* data.
*/
do
{
/* 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
*
* ZCRCW:
* "If the receiver cannot overlap serial and disk I/O, it uses the
* ZRINIT frame to specify a buffer length which the sender will
* not overflow. The sending program sends a ZCRCW data subpacket
* and waits for a ZACK header before sending the next segment of
* the file.
*
* ZCRCG
* "A data subpacket terminated by ZCRCG and CRC does not elicit a
* response unless an error is detected; more data subpacket(s)
* follow immediately."
*
* ZCRCQ
* "ZCRCQ data subpackets expect a ZACK response with the
* receiver's file offset if no error, otherwise a ZRPOS response
* with the last good file offset. Another data subpacket
* continues immediately. ZCRCQ subpackets are not used if the
* receiver does not indicate FDX ability with the CANFDX bit.
*/
if ((pzm->flags & ZM_FLAG_WAIT) != 0)
{
type = ZCRCW;
pzm->flags &= ~ZM_FLAG_WAIT;
}
else if (wait)
{
type = ZCRCW;
}
else
{
type = pzms->dpkttype;
}
/* 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)
{
ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff);
ptr = zm_putzdle(pzm, ptr, crc & 0xff);
}
else
{
/* REVISIT: Why complemented? */
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 */
zmdbg("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, transfer ends, ZEOF 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, transfer 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 -- we are streaming */
case ZCRCG: /* Transfer continues non-stop */
case ZCRCQ: /* Expect ZACK, transfer may continues non-stop */
default:
zmdbg("ZMS_STATE %d->%d: Default\n", pzm->state, ZMS_SENDING);
pzm->state = ZMS_SENDING;
break;
}
}
#ifdef CONFIG_SYSTEM_ZMODEM_RCVSAMPLE
while (pzm->state == ZMS_SENDING && !zm_rcvpending(pzm));
#else
while (0);
#endif
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_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:
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 */
zm_timerrelease(&pzms->cmn);
/* Make sure that the file is closed */
if (pzms->infd)
{
close(pzms->infd);
}
/* And free the Zmodem state structure */
free(pzms);
return ret;
}