/**************************************************************************** * system/zmodem/zm_send.c * * Copyright (C) 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }