From 8efbedf68d8cd929312a40d5c8f26e16258b135d Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 12 Jul 2013 16:28:35 -0600 Subject: A few more zmodem files; still missing a few but those are still fragile and like to undergo some resign before they are usable --- apps/system/zmodem/Makefile | 33 +- apps/system/zmodem/zm.h | 791 +++++++++++++++++++++++++++++++++++++ apps/system/zmodem/zm_dumpbuffer.c | 119 ++++++ apps/system/zmodem/zm_proto.c | 499 +++++++++++++++++++++++ apps/system/zmodem/zm_utils.c | 450 +++++++++++++++++++++ apps/system/zmodem/zm_watchdog.c | 197 +++++++++ 6 files changed, 2074 insertions(+), 15 deletions(-) create mode 100644 apps/system/zmodem/zm.h create mode 100644 apps/system/zmodem/zm_dumpbuffer.c create mode 100644 apps/system/zmodem/zm_proto.c create mode 100644 apps/system/zmodem/zm_utils.c create mode 100644 apps/system/zmodem/zm_watchdog.c (limited to 'apps') diff --git a/apps/system/zmodem/Makefile b/apps/system/zmodem/Makefile index f62aa818d..7f2bc2277 100644 --- a/apps/system/zmodem/Makefile +++ b/apps/system/zmodem/Makefile @@ -38,40 +38,43 @@ include $(APPDIR)/Make.defs ifeq ($(WINTOOL),y) -INCDIROPT = -w +INCDIROPT = -w endif # Zmodem sz and rz commands -PRIORITY = SCHED_PRIORITY_DEFAULT -STACKSIZE = 768 +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 768 -ASRCS = -CSRCS = sz_main.c rz_main.c +ASRCS = -AOBJS = $(ASRCS:.S=$(OBJEXT)) -COBJS = $(CSRCS:.c=$(OBJEXT)) +CSRCS = sz_main.c +CSRCS += rz_main.c +CSRCS += zm_proto.c zm_watchdog.c zm_utils.c zm_dumpbuffer.c -SRCS = $(ASRCS) $(CSRCS) -OBJS = $(AOBJS) $(COBJS) +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) ifeq ($(CONFIG_WINDOWS_NATIVE),y) - BIN = ..\..\libapps$(LIBEXT) + BIN = ..\..\libapps$(LIBEXT) else ifeq ($(WINTOOL),y) - BIN = ..\\..\\libapps$(LIBEXT) + BIN = ..\\..\\libapps$(LIBEXT) else - BIN = ../../libapps$(LIBEXT) + BIN = ../../libapps$(LIBEXT) endif endif -ROOTDEPPATH = --dep-path . +ROOTDEPPATH = --dep-path . # Common build -VPATH = +VPATH = -all: .built +all: .built .PHONY: context depend clean distclean $(AOBJS): %$(OBJEXT): %.S diff --git a/apps/system/zmodem/zm.h b/apps/system/zmodem/zm.h new file mode 100644 index 000000000..3747b0feb --- /dev/null +++ b/apps/system/zmodem/zm.h @@ -0,0 +1,791 @@ +/**************************************************************************** + * apps/system/zmodem/zm.h + * + * 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 + * + * 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. + * + ****************************************************************************/ + +#ifndef __APPS_SYSTEM_XMODEM_ZM_H +#define __APPS_SYSTEM_XMODEM_ZM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* ZModem *******************************************************************/ +/* Zmodem ZRINIT flags. These bits describe the cababilities of the receiver. + * Reference: Paragraph 11.2: + */ + +#define CANFDX (1 << 0) /* Rx can send and receive true FDX */ +#define CANOVIO (1 << 1) /* Rx can receive data during disk I/O */ +#define CANBRK (1 << 2) /* Rx can send a break signal */ +#define CANCRY (1 << 3) /* Receiver can decrypt */ +#define CANLZW (1 << 4) /* Receiver can uncompress */ +#define CANFC32 (1 << 5) /* Receiver can use 32 bit Frame Check */ +#define ESCCTL (1 << 6) /* Receiver expects ctl chars to be escaped */ +#define ESC8 (1 << 7) /* Receiver expects 8th bit to be escaped */ + +/* Zmodem ZSINIT flags. These bits describe the capabilities of the sender */ + +#define TESCCTL (1 << 6) /* Sender needs control chars escaped */ +#define TESC8 (1 << 7) /* Sender needs 8th bit escaped. */ + +/* ZFILE transfer flags */ +/* F0 */ + +#define ZCBIN 1 /* Binary transfer */ +#define ZCNL 2 /* Convert NL to local eol convention */ +#define ZCRESUM 3 /* Resume interrupted xfer or append to file. */ + +/* F1 */ + +#define ZMNEWL 1 /* Transfer if source newer or longer */ +#define ZMCRC 2 /* Transfer if different CRC or length */ +#define ZMAPND 3 /* Append to existing file, if any */ +#define ZMCLOB 4 /* Replace existing file */ +#define ZMNEW 5 /* Transfer if source is newer */ +#define ZMDIFF 6 /* Transfer if dates or lengths different */ +#define ZMPROT 7 /* Protect: transfer only if dest doesn't exist */ +#define ZMCHNG 8 /* Change filename if destination exists */ +#define ZMMASK 0x1f +#define ZMSKNOLOC (1 << 7) /* Skip if not present at receiving end */ + +/* F2 */ + +#define ZTLZW 1 /* lzw compression */ +#define ZTRLE 3 /* Run-length encoding */ + +/* F3 */ + +#define ZCANVHDR 1 /* Variable headers ok */ +#define ZRWOVR 4 /* Byte position for receive window override/256 */ +#define ZXSPARS 64 /* Encoding for sparse file ops. */ + +/* ATTN string special characters. All other characters sent verbose */ + +#define ATTNBRK 0xdd /* Send break signal */ +#define ATTNPSE 0xde /* Pause for one second */ + +/* Zmodem header types */ + +#define ZRQINIT 0 /* Request receive init */ +#define ZRINIT 1 /* Receive init */ +#define ZSINIT 2 /* Send init sequence, define Attn */ +#define ZACK 3 /* ACK */ +#define ZFILE 4 /* File name, from sender */ +#define ZSKIP 5 /* Skip file command, from receiver */ +#define ZNAK 6 /* Last packet was garbled */ +#define ZABORT 7 /* Abort */ +#define ZFIN 8 /* Finish session */ +#define ZRPOS 9 /* Resume file from this position, from receiver */ +#define ZDATA 10 /* Data packets to follow, from sender */ +#define ZEOF 11 /* End of file, from sender */ +#define ZFERR 12 /* Fatal i/o error, from receiver */ +#define ZCRC 13 /* Request for file crc, from receiver */ +#define ZCHALLENGE 14 /* "Send this number back to me", from receiver */ +#define ZCOMPL 15 /* Request is complete */ +#define ZCAN 16 /* Other end cancelled with CAN-CAN-CAN-CAN-CAN */ +#define ZFREECNT 17 /* Request for free bytes on filesystem */ +#define ZCOMMAND 18 /* Command, from sending program */ +#define ZSTDERR 19 /* Output this message to stderr */ + +/* Zmodem character definitions */ + +#define ZDLE ASCII_CAN /* Zmodem escape is CAN */ +#define ZDLEE (ZDLE^0x40) /* Escaped ZDLE */ +#define ZPAD '*' /* pad */ +#define ZBIN 'A' /* 16-bit CRC binary header */ +#define ZHEX 'B' /* 16-bit CRC hex header */ +#define ZBIN32 'C' /* 32-bit CRC binary header */ +#define ZBINR32 'D' /* RLE packed binary frame w/32-bit CRC */ +#define ZVBIN 'a' /* Alternate ZBIN */ +#define ZVHEX 'b' /* Alternate ZHEX */ +#define ZVBIN32 'c' /* Alternate ZBIN32 */ +#define ZVBINR32 'd' /* Alternate ZBINR32 */ +#define ZRESC 0x7f /* RLE flag/escape character */ + +/* ZDLE escape sequences */ + +#define ZCRCE 'h' /* CRC next, frame ends, header follows */ +#define ZCRCG 'i' /* CRC next, frame continues nonstop */ +#define ZCRCQ 'j' /* CRC next, send ZACK, frame continues nonstop */ +#define ZCRCW 'k' /* CRC next, send ZACK, frame ends */ +#define ZRUB0 'l' /* Translate to 0x7f */ +#define ZRUB1 'm' /* Translate to 0xff */ + +/* Implementation ***********************************************************/ +/* Zmodem Events (same as frame type + data received and error events) */ + +#define ZME_RQINIT ZRQINIT /* Request receive init */ +#define ZME_RINIT ZRINIT /* Receive init */ +#define ZME_SINIT ZSINIT /* Send init sequence, define Attn */ +#define ZME_ACK ZACK /* ACK */ +#define ZME_FILE ZFILE /* File name, from sender */ +#define ZME_SKIP ZSKIP /* Skip file command, from receiver */ +#define ZME_NAK ZNAK /* Last packet was garbled */ +#define ZME_ABORT ZABORT /* Abort */ +#define ZME_FIN ZFIN /* Finish session */ +#define ZME_RPOS ZRPOS /* Resume file from this position, from receiver */ +#define ZME_DATA ZDATA /* Data packets to follow, from sender */ +#define ZME_EOF ZEOF /* End of file, from sender */ +#define ZME_FERR ZFERR /* Fatal i/o error, from receiver */ +#define ZME_CRC ZCRC /* Request for file CRC, from receiver */ +#define ZME_CHALLENGE ZCHALLENGE /* "send this number back to me", from receiver */ +#define ZME_COMPL ZCOMPL /* Request is complete */ +#define ZME_CAN ZCAN /* Other end cancelled with CAN-CAN-CAN-CAN-CAN */ +#define ZME_FREECNT ZFREECNT /* Request for free bytes on filesystem */ +#define ZME_COMMAND ZCOMMAND /* Command, from sending program */ +#define ZME_STDERR ZSTDERR /* Output this message to stderr */ + +#define ZME_DATARCVD 253 /* Data received */ +#define ZME_TIMEOUT 254 /* Timeout */ +#define ZME_ERROR 255 /* Protocol error */ + +/* Bit values for flags in struct zm_state_s */ + +#define ZM_FLAG_CRC32 (1 << 0) /* Use 32-bit CRC */ +#define ZM_FLAG_CRKOK (1 << 1) /* CRC is okay */ +#define ZM_FLAG_EOF (1 << 2) /* End of file reached */ +#define ZM_FLAG_ATSIGN (1 << 3) /* Last char was '@' */ +#define ZM_FLAG_ESCCTRL (1 << 4) /* Other end requests ctrl chars be escaped */ +#define ZM_FLAG_ESC (1 << 5) /* Next character is escaped */ +#define ZM_FLAG_INTERRUPT (1 << 6) /* Received attention signal */ +#define ZM_FLAG_WAIT (1 << 7) /* Next send should wait */ +#define ZM_FLAG_APPEND (1 << 8) /* Append to the existing file */ +#define ZM_FLAG_TIMEOUT (1 << 9) /* A timeout has been detected */ + +/* zm_parse() success/error return code definitions: + * + * < 0 : Transfer terminated due to an error + * = 0 : Transfer still in progress + * > 0 : Transfer completed successfully + */ + +#define ZM_XFRDONE 1 /* Success - Transfer complete */ + +/* Debug Definitions ********************************************************/ + +/* Non-standard debug selectable with CONFIG_DEBUG_ZMODEM. Debug output goes + * to stderr (not syslog). Enabling this kind of debug output if your are + * trying to use the console device I/O for file transfer is obviously a bad + * idea (unless, perhaps, you redirect stdin and stdout). + * + * See also CONFIG_SYSTEM_ZMODEM_DUMPBUFFER. + */ + +#ifdef CONFIG_DEBUG_ZMODEM +# define zmprintf(format, arg...) fprintf(stderr, format, ##arg) +# define zmdbg(format, arg...) fprintf(stderr, EXTRA_FMT format EXTRA_ARG, ##arg) +#else +# undef CONFIG_SYSTEM_ZMODEM_DUMPBUFFER +# ifdef CONFIG_CPP_HAVE_VARARGS +# define zmprintf(x...) +# define zmdbg(x...) +# else +# define zmprintf (void) +# define zmdbg (void) +# endif +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ +/* The state of the parser */ + +enum parser_state_e +{ + PSTATE_IDLE = 0, /* Nothing in progress */ + PSTATE_HEADER, /* Parsing a header following ZPAD ZDLE */ + PSTATE_DATA, /* Sending data */ + PSTATE_FINISH /* Waiting for termination handshake */ +}; + +/* PSTATE_IDLE substates */ + +enum pidle_substate_e +{ + PIDLE_ZPAD = 0, /* Waiting for ZPAD */ + PIDLE_ZDLE, /* Waiting for ZDLE */ + PIDLE_ZDLEE /* Waiting for ZDLEE */ +}; + +/* PSTATE_HEADER substates */ + +enum pheader_substate_e +{ + PHEADER_FORMAT = 0, /* Waiting for the header format {ZBIN, ZBIN32, ZHEX} */ + PHEADER_PAYLOAD, /* Waiting for header data {Type Pn/Fn CRC} */ + PHEADER_LSPAYLOAD, /* Waiting for LS nibble of header data (ZHEX only) */ +}; + +/* PSTATE_DATA substates */ + +enum pdata_substate_e +{ + PDATA_READ = 0, /* Waiting for ZDLE */ + PDATA_CRC /* Have the packet type, accumulating the CRC */ +}; + +/* PSTATE_FINISH substates */ + +enum pfinish_substate_e +{ + PFINISH_1STO = 0, /* Waiting for first 'O' */ + PFINISH_2NDO /* Waiting for second 'O' */ +}; + +/* This type describes the method to perform actions at the time of + * a state transition. + */ + +struct zm_state_s; +typedef int (*action_t)(FAR struct zm_state_s *pzm); + +/* State transition table entry. There is one row of the table per possible state. + * Each row is a row of all reasonable events for this state and long the the + * appropriate state transition and transition action. + */ + +struct zm_transition_s +{ + uint8_t type; /* Event (Frame type) */ + uint8_t next; /* Next state */ + bool bdiscard; /* TRUE: discard buffered input */ + action_t action; /* Transition action */ +}; + +/* Common state information. This structure contains all of the top-level + * information needed by the common Zmodem receive and transmit parsing. + */ + +struct zm_state_s +{ + /* User-provided values ***************************************************/ + + /* These file/socket descriptors are used to interact with the remote end */ + + int fdin; /* Probably 0 (stdin) */ + int fdout; /* probably 1 (stdout) */ + + /* System internal values *************************************************/ + + /* evtable[] is the state transition table that controls the state for this + * current action. Different state transitions tables are used for Zmodem + * vs. XY modem and for receive and for tansmit. + */ + + FAR const struct zm_transition_s * const * evtable; + + /* State information, common fields, plus parser-specific fields. + * Notes: + * (1) Only valid during parsing. + */ + + uint8_t pstate; /* Current parser state */ + uint8_t psubstate; /* Current parser sub-state (1) */ + uint8_t state; /* Current transfer state; index into evtable[] */ + uint8_t timeout; /* Timeout in seconds for incoming data */ + uint8_t ncrc; /* Number of bytes in CRC: 2 or 4 (1) */ + uint8_t ncan; /* Number of consecutive CAN chars received (1) */ + uint8_t hdrfmt; /* Header format {ZBIN, ZBIN32, or ZHEX} */ + uint8_t hdrndx; /* Index into hdrdata (1) */ + uint8_t hdrdata[9]; /* 1-byte + 4-byte payload + 2- or 4-byte CRC */ + uint8_t pkttype; /* Type of data packet {ZCRCW, ZCRCE, ZCRCG, ZCRCQ} */ + uint16_t rcvlen; /* Number valid bytes in rcvbuf[] */ + uint16_t rcvndx; /* Index to the next valid bytes in rcvbuf[] (1) */ + uint16_t pktlen; /* Number valid bytes in pktbuf[] */ + uint16_t flags; /* See ZM_FLAG_* definitions */ + uint16_t nerrors; /* Number of data errors */ + timer_t timer; /* Watchdog timer */ + int remfd; /* The R/W file descritor used for communication with remote */ + + /* Buffers. + * + * rcvbuf - Data from the remote peer is receive this buffer + * pktbuf - un-escaped remote peer data is parsed into this buffer + * scratch - Holds data sent to the remote peer. Since the data is this + * buffer is short lived, this buffer may also be used for other + * scratch purposes. + */ + + uint8_t rcvbuf[CONFIG_SYSTEM_ZMODEM_RCVBUFSIZE]; + uint8_t pktbuf[CONFIG_SYSTEM_ZMODEM_PKTBUFSIZE]; + uint8_t scratch[CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE]; +}; + +/* Receive state information */ + +struct zmr_state_s +{ + /* Common state data ******************************************************/ + + struct zm_state_s cmn; + + /* State data unique to the Zmodem receive implementation *****************/ + + uint8_t rcaps; /* Receiver capabilities */ + uint8_t scaps; /* Sender capabilities */ + uint8_t f0; /* Transfer flag F0 */ + uint8_t f1; /* Transfer flag F1 */ +#if 0 /* Not used */ + uint8_t f2; /* Transfer flag F2 */ + uint8_t f3; /* Transfer flag F3 */ +#endif + uint8_t pkttype; /* Type of packet */ + uint8_t ntimeouts; /* Number of timeouts */ + uint32_t crc; /* Remove file CRC */ + FAR char *filename; /* Local filename */ + FAR char *attn; /* Attention string received from remote peer */ + off_t offset; /* Current file offset */ + off_t filesize; /* Remote file size */ +#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS + time_t timestamp; /* Remote time stamp */ +#endif + int outfd; /* Local output file descriptor */ +}; + +/* Send state information */ + +struct zms_state_s +{ + /* Common state data ******************************************************/ + + struct zm_state_s cmn; + + /* State data unique to the Zmodem send implementation ********************/ + + uint8_t strtype; /* Streaming type: ZCRCG or ZCRCQ */ + uint8_t fflags[4]; /* File xfer flags */ + uint16_t rcvmax; /* Max packet size the remote can receive. */ +#ifdef CONFIG_SYSTEM_ZMODEM_TIMESTAMPS + uint32_t timestamp; /* Local file timestamp */ +#endif +#ifdef CONFIG_SYSTEM_ZMODEM_SENDATTN + FAR char *attn; /* Attention string */ +#endif + FAR const char *filename; /* Local filename */ + FAR const char *rfilename; /* Remote filename */ + off_t offset; /* Current file offset */ + off_t lastoffs; /* Last acknowledged file offset */ + off_t zrpos; /* Last offset from ZRPOS */ + off_t filesize; /* Size of the file to send */ + int infd; /* Local input file descriptor */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/* A handy sequence of 4 zeros */ + +EXTERN const uint8_t g_zeroes[4]; + +/* Paragraph 8.4. Session Abort Sequence + * + * "If the receiver is receiving data in streaming mode, the Attn sequence + * is executed to interrupt data transmission before the Cancel sequence is + * sent. The Cancel sequence consists of eight CAN characters and ten + * backspace characters. ZMODEM only requires five Cancel characters, the + * other three are "insurance". + * + * "The trailing backspace characters attempt to erase the effects of the + * CAN characters if they are received by a command interpreter. + */ + +#define CANISTR_SIZE (8+10) + +EXTERN const uint8_t g_canistr[CANISTR_SIZE]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_bytobe32 + * + * Description: + * Convert a sequence of four bytes into a 32-bit value. The byte + * sequence is assumed to be big-endian. + * + ****************************************************************************/ + +uint32_t zm_bytobe32(FAR const uint8_t *val8); + +/**************************************************************************** + * Name: zm_bytobe32 + * + * Description: + * Convert a 32-bit value in a sequence of four bytes in big-endian byte + * order. + * + ****************************************************************************/ + +void zm_be32toby(uint32_t val32, FAR uint8_t *val8); + +/**************************************************************************** + * Name: zm_encnibble + * + * Description: + * Encode an 4-bit binary value to a single hex "digit". + * + ****************************************************************************/ + +char zm_encnibble(uint8_t nibble); + +/**************************************************************************** + * Name: zm_encnibble + * + * Description: + * Decode an 4-bit binary value from a single hex "digit". + * + ****************************************************************************/ + +uint8_t zm_decnibble(char hex); + +/**************************************************************************** + * Name: zm_puthex8 + * + * Description: + * Convert an 8-bit binary value to 2 hex "digits". + * + ****************************************************************************/ + +FAR uint8_t *zm_puthex8(FAR uint8_t *ptr, uint8_t ch); + +/**************************************************************************** + * Name: zm_read + * + * Description: + * Read a buffer of data from a read-able stream. + * + ****************************************************************************/ + +ssize_t zm_read(int fd, FAR uint8_t *buffer, size_t buflen); + +/**************************************************************************** + * Name: zm_getc + * + * Description: + * Read a one byte of data from a read-able stream. + * + ****************************************************************************/ + +int zm_getc(int fd); + +/**************************************************************************** + * Name: zm_write + * + * Description: + * Write a buffer of data to a write-able stream. + * + ****************************************************************************/ + +ssize_t zm_write(int fd, FAR const uint8_t *buffer, size_t buflen); + +/**************************************************************************** + * Name: zm_remwrite + * + * Description: + * Write a buffer of data to the remote peer. + * + ****************************************************************************/ + +#ifdef CONFIG_SYSTEM_ZMODEM_DUMPBUFFER +ssize_t zm_remwrite(int fd, FAR const uint8_t *buffer, size_t buflen); +#else +# define zm_remwrite(f,b,s) zm_write(f,b,s) +#endif + +/**************************************************************************** + * Name: zm_writefile + * + * Description: + * Write a buffer of data to file, performing newline conversions as + * necessary. + * + * NOTE: Not re-entrant. CR-LF sequences that span buffer boundaries are + * not guaranteed to to be handled correctly. + * + ****************************************************************************/ + +int zm_writefile(int fd, FAR const uint8_t *buffer, size_t buflen, bool zcnl); + +/**************************************************************************** + * Name: zm_filecrc + * + * Description: + * Perform CRC32 calculation on a file. + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +uint32_t zm_filecrc(FAR struct zm_state_s *pzm, FAR const char *filename); + +/**************************************************************************** + * Name: zm_putzdle + * + * Description: + * Transfer a value to a buffer performing ZDLE escaping if necessary + * + * Input Parameters: + * pzm - Zmodem session state + * buffer - Buffer in which to add the possibly escaped character + * ch - The raw, unescaped character to be added + * + ****************************************************************************/ + +FAR uint8_t *zm_putzdle(FAR struct zm_state_s *pzm, FAR uint8_t *buffer, + uint8_t ch); + +/**************************************************************************** + * Name: zm_senddata + * + * Description: + * Send data to the remote peer performing CRC operations as necessary + * (ZBIN or ZBIN32 format assumed, ZCRCW terminator is always used) + * + * Input Parameters: + * pzm - Zmodem session state + * buffer - Buffer of data to be sent (must not be pzm->rcvbuf) + * buflen - The number of bytes in buffer to be sent + * + ****************************************************************************/ + +int zm_senddata(FAR struct zm_state_s *pzm, FAR const uint8_t *buffer, + size_t buflen); + +/**************************************************************************** + * Name: zm_sendhexhdr + * + * Description: + * Send a ZHEX header to the remote peer performing CRC operations as + * necessary. + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZRINIT, ZRQINIT, ZDATA, ZACK, ZNAK, ZCRC, ZRPOS, + * ZCOMPL, ZEOF, ZFIN} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendhexhdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer); + +/**************************************************************************** + * Name: zm_sendbin16hdr + * + * Description: + * Send a ZBIN header to the remote peer performing CRC operations as + * necessary. Normally called indirectly through zm_sendbinhdr(). + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbin16hdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer); + +/**************************************************************************** + * Name: zm_sendbin32hdr + * + * Description: + * Send a ZBIN32 header to the remote peer performing CRC operations as + * necessary. Normally called indirectly through zm_sendbinhdr(). + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbin32hdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer); + +/**************************************************************************** + * Name: zm_sendbinhdr + * + * Description: + * Send a binary header to the remote peer. This is a simple wrapping + * function for zm_sendbin16hdr() and zm_sendbin32hdr(). It decides on + * the correct CRC format and re-directs the call appropriately. + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbinhdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer); + +/**************************************************************************** + * Name: zm_parse + * + * Description: + * New data from the remote peer is available in pzm->rcvbuf. The number + * number of bytes of new data is given by rcvlen. + * + * This function will parse the data in the buffer and, based on the + * current state and the contents of the buffer, will drive the Zmodem + * state machine. + * + ****************************************************************************/ + +int zm_parse(FAR struct zm_state_s *pzm, size_t rcvlen); + +/**************************************************************************** + * Name: zm_readstate + * + * Description: + * Enter PSTATE_DATA. + * + ****************************************************************************/ + +void zm_readstate(FAR struct zm_state_s *pzm); + +/**************************************************************************** + * Name: zm_timeout + * + * Description: + * Called by the watchdog logic if/when a timeout is detected. + * + ****************************************************************************/ + +int zm_timeout(FAR struct zm_state_s *pzm); + +/**************************************************************************** + * Name: zm_timerinit + * + * Description: + * Create the POSIX timer used to manage timeouts and attach the SIGALRM + * signal handler to catch the timeout events. + * + ****************************************************************************/ + +int zm_timerinit(FAR struct zm_state_s *pzm); + +/**************************************************************************** + * Name: zm_timerstart + * + * Description: + * Start, restart, or stop the timer. + * + ****************************************************************************/ + +int zm_timerstart(FAR struct zm_state_s *pzm, unsigned int sec); + +/**************************************************************************** + * Name: zm_timerstop + * + * Description: + * Stop the timer. + * + ****************************************************************************/ + +#define zm_timerstop(p) zm_timerstart(p,0) + +/**************************************************************************** + * Name: zm_timerrelease + * + * Description: + * Destroy the timer and and detach the signal handler. + * + ****************************************************************************/ + +int zm_timerrelease(FAR struct zm_state_s *pzm); + +/**************************************************************************** + * Name: zm_dumpbuffer + * + * Description: + * Dump a buffer of zmodem data. + * + ****************************************************************************/ + +#ifdef CONFIG_SYSTEM_ZMODEM_DUMPBUFFER +void zm_dumpbuffer(FAR const char *msg, FAR const void *buffer, size_t buflen); +#else +# define zm_dumpbuffer(m,b,s) +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __APPS_SYSTEM_XMODEM_ZM_H */ diff --git a/apps/system/zmodem/zm_dumpbuffer.c b/apps/system/zmodem/zm_dumpbuffer.c new file mode 100644 index 000000000..d26f5f659 --- /dev/null +++ b/apps/system/zmodem/zm_dumpbuffer.c @@ -0,0 +1,119 @@ +/**************************************************************************** + * apps/system/zmodem/zm_dumpbuffer.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 "zm.h" + +#ifdef CONFIG_SYSTEM_ZMODEM_DUMPBUFFER + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_dumpbuffer + * + * Description: + * Dump a buffer of zmodem data. + * + ****************************************************************************/ + +void zm_dumpbuffer(FAR const char *msg, FAR const void *buffer, size_t buflen) +{ + FAR const uint8_t *ptr = (FAR const uint8_t *)buffer; + size_t i; + int j, k; + + zmprintf("%s [%p]:\n", msg, ptr); + for (i = 0; i < buflen; i += 32) + { + zmprintf("%04x: ", i); + for (j = 0; j < 32; j++) + { + k = i + j; + + if (j == 16) + { + zmprintf(" "); + } + + if (k < buflen) + { + zmprintf("%02x", ptr[k]); + } + else + { + zmprintf(" "); + } + } + + zmprintf(" "); + for (j = 0; j < 32; j++) + { + k = i + j; + + if (j == 16) + { + zmprintf(" "); + } + + if (k < buflen) + { + if (ptr[k] >= 0x20 && ptr[k] < 0x7f) + { + zmprintf("%c", ptr[k]); + } + else + { + zmprintf("."); + } + } + } + zmprintf("\n"); + } +} + +#endif /* CONFIG_SYSTEM_ZMODEM_DUMPBUFFER */ diff --git a/apps/system/zmodem/zm_proto.c b/apps/system/zmodem/zm_proto.c new file mode 100644 index 000000000..4ef1c143d --- /dev/null +++ b/apps/system/zmodem/zm_proto.c @@ -0,0 +1,499 @@ +/**************************************************************************** + * system/zmodem/zm_proto.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 + * + * 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 "zm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Paragraph 8.4. Session Abort Sequence + * + * "If the receiver is receiving data in streaming mode, the Attn sequence + * is executed to interrupt data transmission before the Cancel sequence is + * sent. The Cancel sequence consists of eight CAN characters and ten + * backspace characters. ZMODEM only requires five Cancel characters, the + * other three are "insurance". + * + * "The trailing backspace characters attempt to erase the effects of the + * CAN characters if they are received by a command interpreter. + */ + +#define CANISTR_SIZE (8+10) + +const uint8_t g_canistr[CANISTR_SIZE] = +{ + /* Eight CAN characters */ + + ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN, ASCII_CAN, + ASCII_CAN, ASCII_CAN, + + /* Ten backspace characters */ + + ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS, + ASCII_BS, ASCII_BS, ASCII_BS, ASCII_BS +}; + +/**************************************************************************** + * Public Function Protypes + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_putzdle + * + * Description: + * Transfer a value to a buffer performing ZDLE escaping if necessary. + * + * Input Parameters: + * pzm - Zmodem session state + * buffer - Buffer in which to add the possibly escaped character + * ch - The raw, unescaped character to be added + * + ****************************************************************************/ + +FAR uint8_t *zm_putzdle(FAR struct zm_state_s *pzm, FAR uint8_t *buffer, + uint8_t ch) +{ + uint8_t ch7 = ch & 0x7f; + + /* Check if this character requires ZDLE escaping. + * + * The Zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and a CR + * following '@' be escaped. + */ + + if (ch == ZDLE || + ch7 == ASCII_DLE || + ch7 == ASCII_DC1 || + ch7 == ASCII_DC3 || + ch7 == ASCII_GS || + (ch7 == '\r' && (pzm->flags & ZM_FLAG_ATSIGN) != 0) || + (ch7 < ' ' && (pzm->flags & ZM_FLAG_ESCCTRL) != 0) || + ch7 == ASCII_DEL || + ch == 0xff + ) + { + /* Yes... save the data link escape the character */ + + *buffer++ = ZDLE; + + /* And modify the character itself as appropriate */ + + if (ch == ASCII_DEL) + { + ch = ZRUB0; + } + else if (ch == 0xff) + { + ch = ZRUB1; + } + else + { + ch ^= 0x40; + } + } + + /* Save the possibly escaped character */ + + *buffer++ = ch; + + /* Check if the character is the AT sign */ + + if (ch7 == '@') + { + pzm->flags |= ZM_FLAG_ATSIGN; + } + else + { + pzm->flags &= ~ZM_FLAG_ATSIGN; + } + + return buffer; +} + +/**************************************************************************** + * Name: zm_senddata + * + * Description: + * Send data to the remote peer performing CRC operations as required + * (ZBIN or ZBIN32 format assumed, ZCRCW terminator is always used) + * + * Input Parameters: + * pzm - Zmodem session state + * buffer - Buffer of data to be sent (must not be pzm->rcvbuf) + * buflen - The number of bytes in buffer to be sent + * + ****************************************************************************/ + +int zm_senddata(FAR struct zm_state_s *pzm, FAR const uint8_t *buffer, + size_t buflen) +{ + uint8_t *ptr = pzm->rcvbuf; + ssize_t nwritten; + uint32_t crc; + uint8_t zbin; + uint8_t term; + + /* Make select ZBIN or ZBIN32 format and the ZCRCW terminator */ + + if ((pzm->flags & ZM_FLAG_CRC32) != 0) + { + zbin = ZBIN32; + } + else + { + zbin = ZBIN; + } + + term = ZCRCW; + zmdbg("zbin=%c, buflen=%d, term=%c\n", zbin, buflen, term); + + /* Transfer the data to the I/O buffer, accumulating the CRC */ + + crc = (zbin == ZBIN) ? 0 : 0xffffffff; + while (buflen-- > 0) + { + if (zbin == ZBIN) + { + crc = (uint32_t)crc16part(buffer, 1, (uint16_t)crc); + } + else /* zbin = ZBIN32 */ + { + crc = crc32part(buffer, 1, crc); + } + + ptr = zm_putzdle(pzm, ptr, *buffer++); + } + + /* Trasnfer the data link escape character (without updating the CRC) */ + + *ptr++ = ZDLE; + + /* Transfer the terminating character, updating the CRC */ + + if (zbin == ZBIN) + { + crc = (uint32_t)crc16part((FAR const uint8_t *)&term, 1, (uint16_t)crc); + } + else + { + crc = crc32part((FAR const uint8_t *)&term, 1, crc); + } + + *ptr++ = term; + + /* Calcualate and transfer the final CRC value */ + + if (zbin == ZBIN) + { + crc = (uint32_t)crc16part(g_zeroes, 2, (uint16_t)crc); + ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff); + ptr = zm_putzdle(pzm, ptr, crc & 0xff); + } + else + { + crc = ~crc; + for (buflen = 4; --buflen >= 0; crc >>= 8) + { + ptr = zm_putzdle(pzm, ptr, crc & 0xff); + } + } + + /* Send the header */ + + nwritten = zm_remwrite(pzm->remfd, pzm->rcvbuf, ptr - pzm->rcvbuf); + return nwritten < 0 ? (int)nwritten : OK; +} + +/**************************************************************************** + * Name: zm_sendhexhdr + * + * Description: + * Send a ZHEX header to the remote peer performing CRC operations as + * necessary. + * + * Hex header: + * ZPAD ZPAD ZDLE ZHEX type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2 CR LF [XON] + * Payload length: 16 (14 hex digits, cr, lf, ignoring optional XON) + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZRINIT, ZRQINIT, ZDATA, ZACK, ZNAK, ZCRC, ZRPOS, + * ZCOMPL, ZEOF, ZFIN} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendhexhdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer) +{ + FAR uint8_t *ptr; + ssize_t nwritten; + uint16_t crc; + int i; + + zmdbg("Sending type %d: %02x %02x %02x %02x\n", + type, buffer[0], buffer[1], buffer[2], buffer[3]); + + /* ZPAD ZPAD ZDLE ZHEX */ + + ptr = pzm->scratch; + *ptr++ = ZPAD; + *ptr++ = ZPAD; + *ptr++ = ZDLE; + *ptr++ = ZHEX; + + /* type */ + + crc = crc16part((FAR const uint8_t *)&type, 1, 0); + ptr = zm_puthex8(ptr, type); + + /* f3/p0 f2/p1 f1/p2 f0/p3 */ + + crc = crc16part(buffer, 4, crc); + for (i = 0; i < 4; i++) + { + ptr = zm_puthex8(ptr, *buffer++); + } + + /* crc-1 crc-2 */ + + crc = crc16part(g_zeroes, 2, crc); + ptr = zm_puthex8(ptr, (crc >> 8) & 0xff); + ptr = zm_puthex8(ptr, crc & 0xff); + + /* CR LF */ + + *ptr++ = '\r'; + *ptr++ = '\n'; + + /* [XON] */ + + if (type != ZACK && type != ZFIN) + { + *ptr++ = ASCII_XON; + } + + /* Send the header */ + + nwritten = zm_remwrite(pzm->remfd, pzm->scratch, ptr - pzm->scratch); + return nwritten < 0 ? (int)nwritten : OK; +} + +/**************************************************************************** + * Name: zm_sendbin16hdr + * + * Description: + * Send a ZBIN header to the remote peer performing CRC operations as + * necessary. Normally called indirectly through zm_sendbinhdr(). + * + * 16-bit binary header: + * ZPAD ZDLE ZBIN type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2 + * Payload length: 7 (type, 4 bytes data, 2 byte CRC) + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbin16hdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer) +{ + FAR uint8_t *ptr; + ssize_t nwritten; + uint16_t crc; + int buflen; + int i; + + zmdbg("Sending type %d: %02x %02x %02x %02x\n", + type, buffer[0], buffer[1], buffer[2], buffer[3]); + + /* XPAD ZDLE ZBIN */ + + ptr = pzm->scratch; + *ptr++ = ZPAD; + *ptr++ = ZDLE; + *ptr++ = ZBIN; + + /* type */ + + crc = crc16part((FAR const uint8_t *)&type, 1, 0); + ptr = zm_putzdle(pzm, ptr, type); + + /* f3/p0 f2/p1 f1/p2 f0/p3 */ + + crc = crc16part(buffer, 4, crc); + for (i = 0; i < 4; i++) + { + ptr = zm_putzdle(pzm, ptr, *buffer++); + } + + /* crc-1 crc-2 */ + + crc = crc16part(g_zeroes, 2, crc); + ptr = zm_putzdle(pzm, ptr, (crc >> 8) & 0xff); + ptr = zm_putzdle(pzm, ptr, crc & 0xff); + + /* Send the header */ + + buflen = ptr - pzm->scratch; + nwritten = zm_remwrite(pzm->remfd, pzm->scratch, buflen); + return nwritten < 0 ? (int)nwritten : OK; +} + +/**************************************************************************** + * Name: zm_sendbin32hdr + * + * Description: + * Send a ZBIN32 header to the remote peer performing CRC operations as + * necessary. Normally called indirectly through zm_sendbinhdr(). + * + * 32-bit inary header: + * ZPAD ZDLE ZBIN32 type f3/p0 f2/p1 f1/p2 f0/p3 crc-1 crc-2 crc-3 crc-4 + * Payload length: 9 (type, 4 bytes data, 4 byte CRC) + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbin32hdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer) +{ + FAR uint8_t *ptr; + ssize_t nwritten; + uint32_t crc; + int buflen; + int i; + + zmdbg("Sending type %d: %02x %02x %02x %02x\n", + type, buffer[0], buffer[1], buffer[2], buffer[3]); + + /* XPAD ZDLE ZBIN32 */ + + ptr = pzm->scratch; + *ptr++ = ZPAD; + *ptr++ = ZDLE; + *ptr++ = ZBIN32; + + /* type */ + + ptr = zm_putzdle(pzm, ptr, type); + crc = crc32part((FAR const uint8_t *)&type, 1, 0xffffffffL); + + /* f3/p0 f2/p1 f1/p2 f0/p3 */ + + crc = crc32part(buffer, 4, crc); + for (i = 0; i < 4; i++) + { + ptr = zm_putzdle(pzm, ptr, *buffer++); + } + + /* crc-1 crc-2 crc-3 crc-4 */ + + crc = ~crc; + for (i = 0; i < 4; i++, crc >>= 8) + { + ptr = zm_putzdle(pzm, ptr, crc & 0xff); + } + + /* Send the header */ + + buflen = ptr - pzm->scratch; + nwritten = zm_remwrite(pzm->remfd, pzm->scratch, buflen); + return nwritten < 0 ? (int)nwritten : OK; +} + +/**************************************************************************** + * Name: zm_sendbinhdr + * + * Description: + * Send a binary header to the remote peer. This is a simple wrapper + * function for zm_sendbin16hdr() and zm_sendbin32hdr(). It decides on + * the correct CRC format and re-directs the call appropriately. + * + * Input Parameters: + * pzm - Zmodem session state + * type - Header type {ZSINIT, ZFILE, ZDATA, ZDATA} + * buffer - 4-byte buffer of data to be sent + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ****************************************************************************/ + +int zm_sendbinhdr(FAR struct zm_state_s *pzm, int type, + FAR const uint8_t *buffer) +{ + if ((pzm->flags & ZM_FLAG_CRC32) == 0) + { + return zm_sendbin16hdr(pzm, type, buffer); + } + else + { + return zm_sendbin16hdr(pzm, type, buffer); + } +} diff --git a/apps/system/zmodem/zm_utils.c b/apps/system/zmodem/zm_utils.c new file mode 100644 index 000000000..f9fa2ae08 --- /dev/null +++ b/apps/system/zmodem/zm_utils.c @@ -0,0 +1,450 @@ +/**************************************************************************** + * system/zmodem/zm_utils.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 "zm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const uint8_t g_zeroes[4] = { 0, 0, 0, 0 }; + +/**************************************************************************** + * Public Function Protypes + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_bytobe32 + * + * Description: + * Convert a sequence of four bytes into a 32-bit value. The byte + * sequence is assumed to be big-endian. + * + ****************************************************************************/ + +uint32_t zm_bytobe32(FAR const uint8_t *val8) +{ + return + (uint32_t)val8[3] << 24 | + (uint32_t)val8[2] << 16 | + (uint32_t)val8[1] << 8 | + (uint32_t)val8[0]; +} + +/**************************************************************************** + * Name: zm_be32toby + * + * Description: + * Convert a 32-bit value in a sequence of four bytes in big-endian byte + * order. + * + ****************************************************************************/ + +void zm_be32toby(uint32_t val32, FAR uint8_t *val8) +{ + val8[0] = (uint8_t)( val32 & 0xff); + val8[1] = (uint8_t)((val32 >> 8) & 0xff); + val8[2] = (uint8_t)((val32 >> 16) & 0xff); + val8[3] = (uint8_t)((val32 >> 24) & 0xff); +} + +/**************************************************************************** + * Name: zm_encnibble + * + * Description: + * Encode an 4-bit binary value to a single hex "digit". + * + ****************************************************************************/ + +char zm_encnibble(uint8_t nibble) +{ + if (nibble < 10) + { + return nibble + '0'; + } + else + { + return nibble + 'a' - 10; + } +} + +/**************************************************************************** + * Name: zm_encnibble + * + * Description: + * Decode an 4-bit binary value from a single hex "digit". + * + ****************************************************************************/ + +uint8_t zm_decnibble(char hex) +{ + if (hex <= '9') + { + return hex - '0'; + } + else if (hex <= 'F') + { + return hex - 'A' + 10; + } + else + { + return hex - 'a' + 10; + } +} + +/**************************************************************************** + * Name: zm_puthex8 + * + * Description: + * Convert an 8-bit binary value to 2 hex "digits". + * + ****************************************************************************/ + +FAR uint8_t *zm_puthex8(FAR uint8_t *ptr, uint8_t ch) +{ + *ptr++ = zm_encnibble((ch >> 4) & 0xf); + *ptr++ = zm_encnibble(ch & 0xf); + return ptr; +} + +/**************************************************************************** + * Name: zm_read + * + * Description: + * Read a buffer of data from a read-able stream. + * + ****************************************************************************/ + +ssize_t zm_read(int fd, FAR uint8_t *buffer, size_t buflen) +{ + ssize_t nread; + + /* Read reading as necessary until the requested buffer data is successfully + * read or until an end of file indication or irrecoverable error is + * encountered. + * + * This loop will only execute if the read is interrupted by a signal. + */ + + nread = 0; + do + { + /* Get the next gulp of data from the file. On success, read will return + * (1) nread > 0 and nread <= buflen, (2) nread == 0 on end of file, or + * (3) nread < 0 on a read error. + */ + + nread = read(fd, buffer, buflen); + + /* Did some error occur? */ + + if (nread < 0) + { + int errorcode = errno; + + /* EINTR is not an error... it simply means that this read was + * interrupted by an signal before it obtained in data. + */ + + if (errorcode != EINTR) + { + /* But anything else is bad and we will return the failure + * in those cases. + */ + + zmdbg("ERROR: read failed: %d\n", errorcode); + DEBUGASSERT(errorcode != 0); + return -errorcode; + } + } + } + while (nread < 0); + + return (int)nread; +} + +/**************************************************************************** + * Name: zm_getc + * + * Description: + * Read a one byte of data from a read-able stream. + * + ****************************************************************************/ + +int zm_getc(int fd) +{ + ssize_t nread; + uint8_t ch; + + nread = zm_read(fd, &ch, 1); + if (nread <= 0) + { + return EOF; + } + + return ch; +} + +/**************************************************************************** + * Name: zm_write + * + * Description: + * Write a buffer of data to a write-able stream. + * + ****************************************************************************/ + +ssize_t zm_write(int fd, FAR const uint8_t *buffer, size_t buflen) +{ + ssize_t nwritten; + size_t total = 0; + + /* Read reading as necessary until the requested buffer is filled or until + * an end of file indication or irrecoverable error is encountered. + */ + + while (total < buflen) + { + /* Get the next gulp of data from the file */ + + nwritten = write(fd, buffer, buflen); + + if (nwritten < 0) + { + int errorcode = errno; + + /* EINTR is not an error... it simply means that this read was + * interrupted by an signal before it obtained in data. + */ + + if (errorcode != EINTR) + { + zmdbg("ERROR: write failed: %d\n", errorcode); + DEBUGASSERT(errorcode != 0); + return -errorcode; + } + } + else + { + /* Updates counts and pointers for the next read */ + + total += nwritten; + buffer += nwritten; + buflen -= nwritten; + } + } + + return (int)total; +} + +/**************************************************************************** + * Name: zm_remwrite + * + * Description: + * Write a buffer of data to the remote peer. + * + ****************************************************************************/ + +#ifdef CONFIG_SYSTEM_ZMODEM_DUMPBUFFER +ssize_t zm_remwrite(int fd, FAR const uint8_t *buffer, size_t buflen) +{ + zm_dumpbuffer("Sending", buffer, buflen); + return zm_write(fd, buffer, buflen); +} +#endif + +/**************************************************************************** + * Name: zm_putc + * + * Description: + * Write a one byte of data to a write-able stream. + * + ****************************************************************************/ + +#if 0 /* Not used */ +int zm_putc(int fd, uint8_t ch) +{ + ssize_t nwritten; + + nwritten = zm_write(fd, &ch, 1); + if (nwritten <= 0) + { + return EOF + } + + return ch; +} +#endif + +/**************************************************************************** + * Name: zm_writefile + * + * Description: + * Write a buffer of data to file, performing newline conversions as + * necessary. + * + * NOTE: Not re-entrant. CR-LF sequences that span buffer boundaries are + * not guaranteed to to be handled correctly. + * + ****************************************************************************/ + +int zm_writefile(int fd, FAR const uint8_t *buffer, size_t buflen, bool zcnl) +{ + int ret; + + /* If zcnl set, convert newlines to Unix convention */ + + if (zcnl) + { + static bool newline = false; + FAR const uint8_t *start; + uint8_t ch; + int nbytes; + + start = buffer; + nbytes = 0; + ret = OK; + + /* Loop for each character in the buffer */ + + for (; buflen > 0 && ret == OK; buflen--) + { + /* Get the next character in the buffer */ + + ch = *buffer++; + + /* Convert CR-LF, LF-CR, CR, and LF to LF */ + + if (ch == '\n' || ch == '\r') + { + if (nbytes > 0) + { + ret = zm_write(fd, start, nbytes); + start = buffer; + nbytes = 0; + } + + if (ret == OK && !newline) + { + ret = zm_write(fd, (FAR uint8_t *)"\n", 1); + newline = true; + } + } + else + { + /* Increment the number of bytes we need to write beginning at + * start. We want to write as many contiguous bytes as possible + * for performance reasons. + */ + + nbytes++; + newline = false; + } + } + + /* Write any trainling data that does not end with a newline */ + + if (ret == OK && nbytes > 0) + { + ret = zm_write(fd, start, nbytes); + } + } + else + { + /* We are not modifying newlines, let zm_write() do the whole job */ + + ret = zm_write(fd, buffer, buflen); + } + + return ret; +} + +/************************************************************************************************ + * Name: zm_filecrc + * + * Description: + * Perform CRC32 calculation on a file. + * + * Assumptions: + * The allocated I/O buffer is available to buffer file data. + * + ************************************************************************************************/ + +uint32_t zm_filecrc(FAR struct zm_state_s *pzm, FAR const char *filename) +{ + uint32_t crc; + ssize_t nread; + int fd; + + /* Open the file for reading */ + + fd = open(filename, O_RDONLY); + if (fd < 0) + { + /* This should not happen */ + + zmdbg("ERROR: Failed to open %s: %d\n", filename, errno); + return 0; + } + + /* Calculate the file CRC */ + + crc = 0xffffffff; + while ((nread = zm_read(fd, pzm->scratch, CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE)) > 0) + { + crc = crc32part(pzm->rcvbuf, nread, crc); + } + + /* Close the file and return the CRC */ + + close(fd); + return ~crc; +} diff --git a/apps/system/zmodem/zm_watchdog.c b/apps/system/zmodem/zm_watchdog.c new file mode 100644 index 000000000..48b983acf --- /dev/null +++ b/apps/system/zmodem/zm_watchdog.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * system/zmodem/zm_watchdog.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 "zm.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_expiry + * + * Description: + * SIGALRM signal handler. Simply posts the timeout event. + * + ****************************************************************************/ + +static void zm_expiry(int signo, FAR siginfo_t *info, FAR void *context) +{ + FAR struct zm_state_s *pzm = (FAR struct zm_state_s *)info->si_value.sival_ptr; + + /* Just set the timeout flag. If the Zmodem logic was truly waiting, then + * the signal should wake it up and it should then process the timeout + * condition. + * + * REVISIT: This is a read-modify-write operation and has the potential + * for atomicity issue. We might need to use a dedicated boolean value + * to indicate to timeout! + */ + + pzm->flags |= ZM_FLAG_TIMEOUT; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: zm_timerinit + * + * Description: + * Create the POSIX timer used to manage timeouts and attach the SIGALRM + * signal handler to catch the timeout events. + * + ****************************************************************************/ + +int zm_timerinit(FAR struct zm_state_s *pzm) +{ + struct sigevent toevent; + struct sigaction act; + int ret; + + /* Create a POSIX timer to handle timeouts */ + + toevent.sigev_notify = SIGEV_SIGNAL; + toevent.sigev_signo = SIGALRM; + toevent.sigev_value.sival_ptr = pzm; + + ret = timer_create(CLOCK_REALTIME, &toevent, &pzm->timer); + if (ret < 0) + { + int errorcode = errno; + zmdbg("ERROR: Failed to create a timer: %d\n", errorcode); + return -errorcode; + } + + /* Attach a signal handler to catch the timeout */ + + act.sa_sigaction = zm_expiry; + act.sa_flags = SA_SIGINFO; + sigemptyset(&act.sa_mask); + + ret = sigaction(SIGALRM, &act, NULL); + if (ret < 0) + { + int errorcode = errno; + zmdbg("ERROR: Failed to attach a signal handler: %d\n", errorcode); + return -errorcode; + } + + return OK; +} + +/**************************************************************************** + * Name: zm_timerstart + * + * Description: + * Start, restart, or stop the timer. + * + ****************************************************************************/ + +int zm_timerstart(FAR struct zm_state_s *pzm, unsigned int sec) +{ + struct itimerspec todelay; + int ret; + + /* Start, restart, or stop the timer */ + + todelay.it_interval.tv_sec = 0; /* Nonrepeating */ + todelay.it_interval.tv_nsec = 0; + todelay.it_value.tv_sec = sec; + todelay.it_value.tv_nsec = 0; + + ret = timer_settime(pzm->timer, 0, &todelay, NULL); + if (ret < 0) + { + int errorcode = errno; + zmdbg("ERROR: Failed to set the timer: %d\n", errorcode); + return -errorcode; + } + + return OK; +} + +/**************************************************************************** + * Name: zm_timerrelease + * + * Description: + * Destroy the timer and and detach the signal handler. + * + ****************************************************************************/ + +int zm_timerrelease(FAR struct zm_state_s *pzm) +{ + struct sigaction act; + int result; + int ret = OK; + + /* Delete the POSIX timer */ + + result = timer_delete(pzm->timer); + if (result < 0) + { + int errorcode = errno; + zmdbg("ERROR: Failed to delete the timer: %d\n", errorcode); + ret = -errorcode; + } + + /* Revert to the default signal behavior */ + + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + + result = sigaction(SIGALRM, &act, NULL); + if (result < 0) + { + int errorcode = errno; + zmdbg("ERROR: Failed to detach the signal handler: %d\n", errorcode); + ret = -errorcode; + } + + return ret; +} -- cgit v1.2.3