diff options
author | Nick Van Doorn <vandoorn.nick@gmail.com> | 2018-09-11 18:57:15 -0700 |
---|---|---|
committer | Nick Van Doorn <vandoorn.nick@gmail.com> | 2018-09-11 18:57:15 -0700 |
commit | ecde0e8def9473be4095261bc3d242eef22c18c4 (patch) | |
tree | 4e67bddba71d6722fa62b0583b5caf1d24b21523 | |
parent | dd19abb0dab39605777dc9cf0b8f3073f7fae8c1 (diff) |
Swap for xmodem1k
-rw-r--r-- | upload/xmodem.c | 2084 | ||||
-rw-r--r-- | upload/xmodem.h | 308 |
2 files changed, 241 insertions, 2151 deletions
diff --git a/upload/xmodem.c b/upload/xmodem.c index dd471d7..a97327c 100644 --- a/upload/xmodem.c +++ b/upload/xmodem.c @@ -1,1879 +1,269 @@ -////////////////////////////////////////////////////////////////////////////// -// // -// _ // -// __ __ _ __ ___ ___ __| | ___ _ __ ___ ___ // -// \ \/ /| '_ ` _ \ / _ \ / _` | / _ \| '_ ` _ \ / __| // -// > < | | | | | || (_) || (_| || __/| | | | | | _| (__ // -// /_/\_\|_| |_| |_| \___/ \__,_| \___||_| |_| |_|(_)\___| // -// // -// // -////////////////////////////////////////////////////////////////////////////// -// // -// Copyright (c) 2012 by S.F.T. Inc. - All rights reserved // -// Use, copying, and distribution of this software are licensed according // -// to the GPLv2, LGPLv2, or BSD license, as appropriate (see COPYING) // -// // -////////////////////////////////////////////////////////////////////////////// - -// XMODEM adapted for arduino and POSIX systems. Windows code incomplete - +/* + * Copyright 2001-2010 Georges Menie (www.menie.org) + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the University of California, Berkeley 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 REGENTS 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 REGENTS AND 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. + */ + +/* this code needs standard functions memcpy() and memset() + and input/output functions _inbyte() and _outbyte(). + the prototypes of the input/output functions are: + int _inbyte(unsigned short timeout); // msec timeout + void _outbyte(int c); + */ + +#include "crc16.h" #include "xmodem.h" -// special I/O when linked into 'SFTARDCAL' application -#ifdef SFTARDCAL -int my_read(SERIAL_TYPE iFile, void* pBuf, int cbBuf); -int my_write(SERIAL_TYPE iFile, const void* pBuf, int cbBuf); -void my_flush(SERIAL_TYPE iFile); -#endif // SFTARDCAL - -// internal structure definitions - -// Windows requires a different way of specifying structure packing -#ifdef WIN32 -#define PACKED -#pragma pack(push, 1) -#else // POSIX, ARDUINO -#define PACKED __attribute__((__packed__)) -#endif // WIN32 vs THE REST OF THE WORLD - -#define _SOH_ 1 /* start of packet - note XMODEM-1K uses '2' */ -#define _EOT_ 4 -#define _ENQ_ 5 -#define _ACK_ 6 -#define _NAK_ 21 /* NAK character */ -#define _CAN_ 24 /* CAN character CTRL+X */ - -/** \file xmodem.c - * \brief main source file for S.F.T. XMODEM library - * - * S.F.T. XMODEM library -**/ - -/** \ingroup xmodem_internal - * \brief Structure defining an XMODEM CHECKSUM packet - * -\code -typedef struct _XMODEM_BUF_ -{ - char cSOH; // ** SOH byte goes here ** - unsigned char aSEQ, aNotSEQ; // ** 1st byte = seq#, 2nd is ~seq# ** - char aDataBuf[128]; // ** the actual data itself! ** - unsigned char bCheckSum; // ** checksum gets 1 byte ** -} PACKED XMODEM_BUF; -\endcode - * -**/ -typedef struct _XMODEM_BUF_ { - char cSOH; ///< SOH byte goes here - unsigned char aSEQ, aNotSEQ; ///< 1st byte = seq#, 2nd is ~seq# - char aDataBuf[128]; ///< the actual data itself! - unsigned char bCheckSum; ///< checksum gets 1 byte -} PACKED XMODEM_BUF; - -/** \ingroup xmodem_internal - * \brief Structure defining an XMODEM CRC packet - * -\code -typedef struct _XMODEMC_BUF_ -{ - char cSOH; // ** SOH byte goes here ** - unsigned char aSEQ, aNotSEQ; // ** 1st byte = seq#, 2nd is ~seq# ** - char aDataBuf[128]; // ** the actual data itself! ** - unsigned short wCRC; // ** CRC gets 2 bytes, high endian ** -} PACKED XMODEMC_BUF; -\endcode - * -**/ -typedef struct _XMODEMC_BUF_ { - char cSOH; ///< SOH byte goes here - unsigned char aSEQ, aNotSEQ; ///< 1st byte = seq#, 2nd is ~seq# - char aDataBuf[128]; ///< the actual data itself! - unsigned short wCRC; ///< CRC gets 2 bytes, high endian -} PACKED XMODEMC_BUF; - -#ifdef WIN32 -// restore default packing -#pragma pack(pop) -#endif // WIN32 - -/** \ingroup xmodem_internal - * \brief Structure that identifies the XMODEM communication state - * -\code -typedef struct _XMODEM_ -{ - SERIAL_TYPE ser; // identifies the serial connection, data type is -OS-dependent - FILE_TYPE file; // identifies the file handle, data type is OS-dependent - union - { - XMODEM_BUF xbuf; // XMODEM CHECKSUM buffer - XMODEMC_BUF xcbuf; // XMODEM CRC buffer - } buf; // union of both buffers, total length 133 bytes - unsigned char bCRC; // non-zero for CRC, zero for checksum -} XMODEM; -\endcode - * -**/ -typedef struct _XMODEM_ { - SERIAL_TYPE - ser; ///< identifies the serial connection, data type is OS-dependent - FILE_TYPE file; ///< identifies the file handle, data type is OS-dependent - - union { - XMODEM_BUF xbuf; ///< XMODEM CHECKSUM buffer - XMODEMC_BUF xcbuf; ///< XMODEM CRC buffer - } buf; ///< union of both buffers, total length 133 bytes - - unsigned char bCRC; ///< non-zero for CRC, zero for checksum - -} XMODEM; - -#ifdef DEBUG_CODE -static char szERR[32]; // place for error messages, up to 16 characters - -const char* XMGetError(void) { - return szERR; -} -#endif // DEBUG_CODE - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) -void debug_dump_buffer(int iDir, const void* pBuf, int cbBuf) { - int i1, i2; - const unsigned char *p1, *p2; - - if (cbBuf <= 0) { - return; - } - - p1 = p2 = (const unsigned char*)pBuf; - - for (i1 = 0, i2 = 0; i1 <= cbBuf; i1++, p1++) { - if (!i1 || i2 >= 16 || i1 == cbBuf) { - if (i1) { - while (i2 < 16) { - fputs(" ", stderr); // fill up spaces where data would be - i2++; - } - - fputs(" : ", stderr); - - while (p2 < p1) { - if (*p2 >= 32 && *p2 <= 127) { - fputc(*p2, stderr); - } else { - fputc('.', stderr); - } - - p2++; - } - - fputc('\n', stderr); - } - - if (!i1 && iDir > 0) { - fputs("--> ", stderr); - } else if (!i1 && iDir < 0) { - fputs("<-- ", stderr); - } else { - fputs(" ", stderr); - } - - i2 = 0; - p2 = p1; // make sure - } - - if (i1 < cbBuf) { - if (!i2) { - fprintf(stderr, "%02x: %02x", i1, *p1); - } else { - fprintf(stderr, ", %02x", *p1); - } - - i2++; +#define SOH 0x01 +#define STX 0x02 +#define EOT 0x04 +#define ACK 0x06 +#define NAK 0x15 +#define CAN 0x18 +#define CTRLZ 0x1A + +#define DLY_1S 1000 +#define MAXRETRANS 25 +#define TRANSMIT_XMODEM_1K + +static int check(int crc, const unsigned char* buf, int sz) { + if (crc) { + unsigned short crc = crc16_ccitt(buf, sz); + unsigned short tcrc = (buf[sz] << 8) + buf[sz + 1]; + if (crc == tcrc) + return 1; + } else { + int i; + unsigned char cks = 0; + for (i = 0; i < sz; ++i) { + cks += buf[i]; } + if (cks == buf[sz]) + return 1; } - fputc('\n', stderr); - fflush(stderr); + return 0; } -#endif // STAND_ALONE, DEBUG_CODE - -// char iBinaryTransfer = 0, iDisableRXOVER = 0; - -/** \ingroup xmodem_internal - * \brief Calculate checksum for XMODEM packet - * - * \param lpBuf A pointer to the XMODEM data buffer - * \param cbBuf The length of the XMODEM data buffer (typically 128) - * \return An unsigned char value to be assigned to the 'checksum' element in - *the XMODEM packet - * -**/ -unsigned char CalcCheckSum(const char* lpBuf, short cbBuf) { - short iC, i1; - iC = 0; - - for (i1 = 0; i1 < cbBuf; i1++) { - iC += lpBuf[i1]; - } - - return (unsigned char)(iC & 0xff); +static void flushinput(void) { + while (_inbyte(((DLY_1S)*3) >> 1) >= 0) + ; } -/** \ingroup xmodem_internal - * \brief Calculate checksum for XMODEM packet - * - * \param sVal An unsigned short integer to be made 'high endian' by flipping - *bytes (as needed) - * \return A (possibly) byte-flipped high-endian unsigned short integer - * - * This function assumes low-endian for Arduino, and performs a universal - *operation - * for 'indeterminate' architectures. -**/ -static unsigned short my_htons(unsigned short sVal) { - union { - unsigned char aVal[2]; - unsigned short sVal; - } a, b; - - // tweeked for size and speed. enjoy. - - b.sVal = sVal; - -#ifdef ARDUINO - - a.aVal[0] = b.aVal[1]; // no math involved, pre-optimized code - a.aVal[1] = b.aVal[0]; - -#else - - a.aVal[0] = (unsigned char)(sVal >> 8); // less optimized but universal code - a.aVal[1] = (unsigned char)(sVal & 0xff); - -#endif // ARDUINO - - return a.sVal; -} - -/** \ingroup xmodem_internal - * \brief Calculate 16-bit CRC for XMODEM packet - * - * \param lpBuf A pointer to the XMODEM data buffer - * \param cbBuf The length of the XMODEM data buffer (typically 128) - * \return A high-endian 16-bit (unsigned short) value to be assigned to the - *'CRC' element in the XMODEM packet - * - * This method uses the 'long way' which is SMALLER CODE for microcontrollers, - *but eats up a bit more CPU. - * Otherwise, you'd have to pre-build the 256 byte table and use "the table - *lookup" method. -**/ -unsigned short CalcCRC(const char* lpBuf, short cbBuf) { - unsigned short wCRC; - short i1, i2, iAX; - char cAL; - - // ** this function returns 2-byte string containing - // ** the CRC calculation result, as high endian - - wCRC = 0; - - for (i1 = 0; i1 < cbBuf; i1++) { - cAL = lpBuf[i1]; - - iAX = (unsigned short)cAL << 8; - - wCRC = iAX ^ wCRC; - - for (i2 = 0; i2 < 8; i2++) { - iAX = wCRC; - - if (iAX & 0x8000) { - wCRC <<= 1; - wCRC ^= 0x1021; - } else { - wCRC <<= 1; +int xmodemReceive(unsigned char* dest, int destsz) { + unsigned char + xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */ + unsigned char* p; + int bufsz, crc = 0; + unsigned char trychar = 'C'; + unsigned char packetno = 1; + int i, c, len = 0; + int retry, retrans = MAXRETRANS; + + for (;;) { + for (retry = 0; retry < 16; ++retry) { + if (trychar) + _outbyte(trychar); + if ((c = _inbyte((DLY_1S) << 1)) >= 0) { + switch (c) { + case SOH: + bufsz = 128; + goto start_recv; + case STX: + bufsz = 1024; + goto start_recv; + case EOT: + flushinput(); + _outbyte(ACK); + return len; /* normal end */ + case CAN: + if ((c = _inbyte(DLY_1S)) == CAN) { + flushinput(); + _outbyte(ACK); + return -1; /* canceled by remote */ + } + break; + default: + break; + } } } - } - - return my_htons(wCRC); -} - -// void WaitASecond() -//{ -//#ifdef ARDUINO -// delay(1000); -//#elif defined(WIN32) -// Sleep(1000); -//#else // -// usleep(1000000); -//#endif // ARDUINO -//} - -#ifndef ARDUINO -#ifdef WIN32 -#define MyMillis GetTickCount -#else // WIN32 - -/** \ingroup xmodem_internal - * \brief Return internal 'milliseconds' value for timing purposes - * - * \return A calculated 'milliseconds' value as an unsigned long integer - * - * This function returns the 'unsigned long' integer value for elapsed time - *based - * on the result of the 'gettimeofday()' API function. On 32-bit and Windows - *systems - * the value might wrap around, so you should be careful with your time - *comparisons (see the - * code _I_ wrote for the right way to do it). On 64-bit POSIX systems, this - *value will - * always increase.\n - * NOTE: Win32 defines this as a macro (see above) for the 'GetTickCount()' - *api, which - * returns a 32-bit value. POSIX x86 returns 32-bit, x64 returns - *64-bit. YMMV. -**/ -unsigned long MyMillis(void) { - struct timeval tv; - - gettimeofday(&tv, NULL); // 2nd parameter is obsolete anyway - - // NOTE: this won't roll over the way 'GetTickCount' does in WIN32 so I'll - // truncate it - // down to a 32-bit value to make it happen. Everything that uses - // 'MyGetTickCount' - // must handle this rollover properly using 'int' and not 'long' (or - // cast afterwards) - return ((unsigned int)((unsigned long)tv.tv_sec * 1000L + - (unsigned long)tv.tv_usec / 1000L)); -} -#endif // WIN32 -#endif // ARDUINO - -// Function GenerateSEQ (wSeq%) As String -// -// GenerateSEQ = Chr$(wSeq%) + Chr$(Not (wSeq%) And &HFF) -// -// End Function - -/** \ingroup xmodem_internal - * \brief Generate a sequence number pair, place into XMODEM_BUF - * - * \param pBuf A pointer to an XMODEM_BUF structure - * \param bSeq An unsigned char, typically cast from an unsigned long 'block - *number' - * - * This function generates the sequence pair for the XMODEM packet. The 'block - *number' - * is initially assigned a value of '1', and increases by 1 for each successful - *packet. - * That value is 'truncated' to a single byte and assigned as a sequence number - *for the - * packet itself. -**/ -void GenerateSEQ(XMODEM_BUF* pBuf, unsigned char bSeq) { - pBuf->aSEQ = bSeq; - pBuf->aNotSEQ = ~bSeq; -} - -/** \ingroup xmodem_internal - * \brief Generate a sequence number pair, place into XMODEMC_BUF (the CRC - *version) - * - * \param pBuf A pointer to an XMODEM_BUF structure - * \param bSeq An unsigned char, typically cast from an unsigned long 'block - *number' - * - * This function generates the sequence pair for the XMODEM packet. The 'block - *number' - * is initially assigned a value of '1', and increases by 1 for each successful - *packet. - * That value is 'truncated' to a single byte and assigned as a sequence number - *for the - * packet itself. -**/ -void GenerateSEQC(XMODEMC_BUF* pBuf, unsigned char bSeq) { - pBuf->aSEQ = bSeq; - pBuf->aNotSEQ = - (255 - bSeq); //~bSeq; these should be the same but for now I do this... -} - -/** \ingroup xmodem_internal - * \brief Get an XMODEM block from the serial device - * - * \param ser A 'SERIAL_TYPE' identifier for the serial connection - * \param pBuf A pointer to the buffer that receives the data - * \param cbSize The number of bytes/chars to read - * \return The number of bytes/chars read, 0 if timed out (no data), < 0 on - *error - * - * Call this function to read data from the serial port, specifying the number - *of - * bytes to read. This function times out after no data transferred (silence) - *for - * a period of 'SILENCE_TIMEOUT' milliseconds. This allows spurious data - *transfers - * to continue as long as there is LESS THAN 'SILENCE_TIMEOUT' between bytes, - *and - * also allows VERY SLOW BAUD RATES (as needed). However, if the transfer - *takes longer - * than '10 times SILENCE_TIMEOUT', the function will return the total number - *of bytes - * that were received within that time.\n - * The default value of 5 seconds, extended to 50 seconds, allows a worst-case - *baud - * rate of about 20. This should not pose a problem. If it does, edit the - *code. -**/ -short GetXmodemBlock(SERIAL_TYPE ser, char* pBuf, short cbSize) { - unsigned long ulCur; - short cb1; -// ** This function obtains a buffer of 'cbSize' bytes, ** -// ** waiting a maximum of 5 seconds (of silence) to get it. ** -// ** It returns the data within 'pBuf', returning the actual ** -// ** number of bytes transferred. ** - -#ifdef ARDUINO - char* p1; - short i1; - - p1 = pBuf; - cb1 = 0; - - ulCur = millis(); - ser->setTimeout(SILENCE_TIMEOUT); // 5 seconds [of silence] - - for (i1 = 0; i1 < cbSize; i1++) { - if (ser->readBytes(p1, 1) != - 1) // 5 seconds of "silence" is what fails this - { - break; - } - - cb1++; - p1++; - - if ((millis() - ulCur) > - (unsigned long)(10L * SILENCE_TIMEOUT)) // 10 times SILENCE TIMEOUT for - // TOTAL TIMEOUT - { - break; // took too long, I'm going now - } - } - -#elif defined(SFTARDCAL) -#ifndef WIN32 - struct pollfd aFD[2]; -#endif // WIN32 - int i1; - unsigned long ulStart; - - ulStart = ulCur = MyMillis(); - - cb1 = 0; - - do { -#ifndef WIN32 - aFD[0].fd = ser; - aFD[0].events = POLLIN | POLLERR; - aFD[0].revents = 0; - - i1 = poll(aFD, 1, 100); - if (!i1) { + if (trychar == 'C') { + trychar = NAK; continue; } - - if (i1 < 0) { - fprintf(stderr, "poll error %d\n", errno); - return -1; - } -#endif // WIN32 - -#ifdef WIN32 - if (my_pollin(ser) > 0) -#else // WIN32 - if (aFD[0].revents & POLLIN) -#endif // WIN32 - { - i1 = my_read(ser, pBuf + cb1, cbSize - cb1); - - if (i1 > 0) { - cb1 += i1; - ulCur = MyMillis(); - } - } else { - MySleep(1); - } - } while (!QuitFlag() && cb1 < cbSize && - (MyMillis() - ulCur) < SILENCE_TIMEOUT && - (MyMillis() - ulStart) < (unsigned long)10L * SILENCE_TIMEOUT); - -#elif defined(WIN32) - -#error no win32 code yet - -#else // POSIX - unsigned long ulStart; - char* p1; - int i1, i2; - - if (fcntl(ser, F_SETFL, O_NONBLOCK) == -1) { - static int iFailFlag = 0; - - if (!iFailFlag) { - fprintf(stderr, "Warning: 'fcntl(O_NONBLOCK)' failed, errno = %d\n", - errno); - fflush(stderr); - iFailFlag = 1; - } - } - - p1 = pBuf; - cb1 = 0; - - ulStart = ulCur = MyMillis(); - - for (i1 = 0; i1 < cbSize; i1++) { - while ((i2 = read(ser, p1, 1)) != 1) { - if (i2 < 0 && errno != EAGAIN) { - // read error - exit now - // return cb1; // how many bytes I actually read - goto the_end; - } else { - usleep(1000); // 1 msec - - if ((MyMillis() - ulCur) > SILENCE_TIMEOUT || // too much silence? - (MyMillis() - ulStart) > - 10 * SILENCE_TIMEOUT) // too long for transfer - { - // return cb1; // finished (return how many bytes I actually - // read) - goto the_end; + flushinput(); + _outbyte(CAN); + _outbyte(CAN); + _outbyte(CAN); + return -2; /* sync error */ + + start_recv: + if (trychar == 'C') + crc = 1; + trychar = 0; + p = xbuff; + *p++ = c; + for (i = 0; i < (bufsz + (crc ? 1 : 0) + 3); ++i) { + if ((c = _inbyte(DLY_1S)) < 0) + goto reject; + *p++ = c; + } + + if (xbuff[1] == (unsigned char)(~xbuff[2]) && + (xbuff[1] == packetno || xbuff[1] == (unsigned char)packetno - 1) && + check(crc, &xbuff[3], bufsz)) { + if (xbuff[1] == packetno) { + register int count = destsz - len; + if (count > bufsz) + count = bufsz; + if (count > 0) { + memcpy(&dest[len], &xbuff[3], count); + len += count; } + ++packetno; + retrans = MAXRETRANS + 1; } - } - - // here it succeeds - - cb1++; - p1++; - - if ((MyMillis() - ulStart) > - 10 * SILENCE_TIMEOUT) // 10 times SILENCE TIMEOUT for TOTAL TIMEOUT - { - break; // took too long, I'm going now - } - } - -the_end: - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "GetXmodemBlock - request %d, read %d errno=%d\n", cbSize, - cb1, errno); - fflush(stderr); - //#ifdef DEBUG_CODE - debug_dump_buffer(-1, pBuf, cb1); -//#endif // DEBUG_CODE -#endif // STAND_ALONE - -#endif // ARDUINO - - return cb1; // what I actually read -} - -/** \ingroup xmodem_internal - * \brief Write a single character to the serial device - * - * \param ser A 'SERIAL_TYPE' identifier for the serial connection - * \param bVal The byte to send - * \return The number of bytes/chars written, or < 0 on error - * - * Call this function to write one byte of data to the serial port. Typically - * this is used to send things like an ACK or NAK byte. -**/ -int WriteXmodemChar(SERIAL_TYPE ser, unsigned char bVal) { - int iRval; -#ifdef ARDUINO - - iRval = ser->write(bVal); -// ser->flush(); // force sending it - -#elif defined(SFTARDCAL) - char buf[2]; // use size of '2' to avoid warnings about array size of '1' - - buf[0] = (char)bVal; - return (short)my_write(ser, buf, 1); - -#elif defined(WIN32) - -#error no win32 code yet - -#else // POSIX - char buf[2]; // use size of '2' to avoid warnings about array size of '1' - - if (fcntl(ser, F_SETFL, 0) == -1) // set blocking mode - { - static int iFailFlag = 0; - - if (!iFailFlag) { - fprintf(stderr, "Warning: 'fcntl(O_NONBLOCK)' failed, errno = %d\n", - errno); - iFailFlag = 1; - } - } - - buf[0] = bVal; // in case args are passed by register - - iRval = write(ser, buf, 1); - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "WriteXmodemChar - returns %d\n", iRval); - if (iRval > 0) { - debug_dump_buffer(1, buf, 1); - } -#endif // STAND_ALONE, DEBUG_CODE -#endif // ARDUINO - - return iRval; -} - -/** \ingroup xmodem_internal - * \brief Send an XMODEM block via the serial device - * - * \param ser A 'SERIAL_TYPE' identifier for the serial connection - * \param pBuf A pointer to the buffer that receives the data - * \param cbSize The number of bytes/chars to write - * \return The number of bytes/chars written, < 0 on error - * - * Call this function to write data via the serial port, specifying the number - *of - * bytes to write. -**/ -int WriteXmodemBlock(SERIAL_TYPE ser, const void* pBuf, int cbSize) { - int iRval; -#ifdef ARDUINO - - iRval = ser->write((const uint8_t*)pBuf, cbSize); -// ser->flush(); // force sending it before returning - -#elif defined(SFTARDCAL) - - return (short)my_write(ser, pBuf, cbSize); - -#elif defined(WIN32) - -#error no win32 code yet - -#else // POSIX - - if (fcntl(ser, F_SETFL, 0) == -1) // set blocking mode - { - static int iFailFlag = 0; - - if (!iFailFlag) { - fprintf(stderr, "Warning: 'fcntl(O_NONBLOCK)' failed, errno = %d\n", - errno); - fflush(stderr); - iFailFlag = 1; - } - } - - iRval = write(ser, pBuf, cbSize); - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "\r\nWriteXmodemBlock - returns %d\n", iRval); - fflush(stderr); - - if (iRval > 0) { - debug_dump_buffer(1, pBuf, cbSize); - } -#endif // STAND_ALONE, DEBUG_CODE -#endif - - return iRval; -} - -/** \ingroup xmodem_internal - * \brief Read all input from the serial port until there is 1 second of - *'silence' - * - * \param ser A 'SERIAL_TYPE' identifier for the serial connection - * - * Call this function to read ALL data from the serial port, until there is a - *period - * with no data (i.e. 'silence') for 1 second. At that point the function will - *return.\n - * Some operations require that any bad data be flushed out of the input to - *prevent - * synchronization problems. By using '1 second of silence' it forces - *re-synchronization - * to occur in one shot, with the possible exception of VERY noisy lines. The - *down side - * is that it may slow down transfers with a high data rate. -**/ -void XModemFlushInput(SERIAL_TYPE ser) { -#ifdef ARDUINO - unsigned long ulStart; - - ulStart = millis(); - - do { - if (ser->available()) { - ser->read(); // don't care about the data - ulStart = millis(); // reset time - } else { - delay(1); - } - - } while ((millis() - ulStart) < 1000); - -#elif defined(SFTARDCAL) - - my_flush(ser); - -#elif defined(WIN32) - -#error no win32 code yet - -#else // POSIX - unsigned long ulStart; - int i1; -#ifdef DEBUG_CODE - unsigned char buf[16]; - int cbBuf; -#else // DEBUG_CODE - unsigned char buf[2]; -#endif // DEBUG_CODE - - if (fcntl(ser, F_SETFL, O_NONBLOCK) == -1) { - static int iFailFlag = 0; - - if (!iFailFlag) { - fprintf(stderr, "Warning: 'fcntl(O_NONBLOCK)' failed, errno = %d\n", - errno); - iFailFlag = 1; - } - } - - ulStart = MyMillis(); -#ifdef DEBUG_CODE - cbBuf = 0; -#endif // DEBUG_CODE - while ((MyMillis() - ulStart) < 1000) { -#ifdef DEBUG_CODE - i1 = read(ser, &(buf[cbBuf]), 1); -#else // DEBUG_CODE - i1 = read(ser, buf, 1); -#endif // DEBUG_CODE - if (i1 == 1) { -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - cbBuf++; - if (cbBuf >= sizeof(buf)) { - debug_dump_buffer(-1, buf, cbBuf); - cbBuf = 0; + if (--retrans <= 0) { + flushinput(); + _outbyte(CAN); + _outbyte(CAN); + _outbyte(CAN); + return -3; /* too many retry error */ } -#endif // STAND_ALONE, DEBUG_CODE - ulStart = MyMillis(); - } else { - usleep(1000); + _outbyte(ACK); + continue; } + reject: + flushinput(); + _outbyte(NAK); } - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - if (cbBuf > 0) { - debug_dump_buffer(-1, buf, cbBuf); - } -#endif // STAND_ALONE, DEBUG_CODE - -#endif // ARDUINO -} - -/** \ingroup xmodem_internal - * \brief Terminate the XMODEM connection - * - * \param pX A pointer to the 'XMODEM' object identifying the transfer - * - * Call this function prior to ending the XMODEM transfer. Currently the only - * thing it does is flush the input. -**/ -void XmodemTerminate(XMODEM* pX) { - XModemFlushInput(pX->ser); - - // TODO: close files? -} - -/** \ingroup xmodem_internal - * \brief Validate the sequence number of a received XMODEM block - * - * \param pX A pointer to an 'XMODEM_BUF' - * \param bSeq The expected sequence number (block & 255) - * \return A zero value on success, non-zero otherwise - * - * Call this function to validate a packet's sequence number against the block - *number -**/ -short ValidateSEQ(XMODEM_BUF* pX, unsigned char bSeq) { - return pX->aSEQ != 255 - pX->aNotSEQ || // ~(pX->aNotSEQ) || - pX->aSEQ != bSeq; // returns TRUE if not valid } -/** \ingroup xmodem_internal - * \brief Validate the sequence number of a received XMODEM block (CRC version) - * - * \param pX A pointer to an 'XMODEMC_BUF' - * \param bSeq The expected sequence number (block & 255) - * \return A zero value on success, non-zero otherwise - * - * Call this function to validate a packet's sequence number against the block - *number -**/ -short ValidateSEQC(XMODEMC_BUF* pX, unsigned char bSeq) { - return pX->aSEQ != 255 - pX->aNotSEQ || // ~(pX->aNotSEQ) || - pX->aSEQ != bSeq; // returns TRUE if not valid -} - -/** \ingroup xmodem_internal - * \brief Generic function to receive a file via XMODEM (CRC or Checksum) - * - * \param pX A pointer to an 'XMODEM_BUF' with valid bCRC, ser, and file - *members - * \return A zero value on success, negative on error, positive on cancel - * - * The calling function will need to poll for an SOH from the server using 'C' - *and 'NAK' - * characters (as appropriate) until an SOH is received. That value must be - *assigned - * to the 'buf' union (as appropriate), and the bCRC member assigned to - *non-zero if - * the server responded to 'C', or zero if it responded to 'NAK'. With the - *bCRC, - * ser, and file members correctly assigned, call THIS function to receive - *content - * via XMODEM and write it to 'file'.\n - * This function will return zero on success, a negative value on error, and a - *positive - * value if the transfer was canceled by the server. -**/ -int ReceiveXmodem(XMODEM* pX) { -#ifdef WIN32 - DWORD cbWrote; -#endif // WIN32 - int ecount, ec2; - long etotal, filesize, block; - unsigned char cY; // the char to send in response to a packet -// NOTE: to allow debugging the CAUSE of an xmodem block's failure, i1, i2, and -// i3 -// are assigned to function return values and reported in error messages. -#ifdef DEBUG_CODE - short i1, i2, i3; -#define DEBUG_I1 i1 = -#define DEBUG_I2 i2 = -#define DEBUG_I3 i3 = -#else // DEBUG_CODE -#define DEBUG_I1 /*normally does nothing*/ -#define DEBUG_I2 /*normally does nothing*/ -#define DEBUG_I3 /*normally does nothing*/ -#endif // DEBUG_CODE - - ecount = 0; - etotal = 0; - filesize = 0; - block = 1; - - // ** already got the first 'SOH' character on entry to this function ** - - // Form2.Show 0 '** modeless show of form2 (CANSEND) ** - // Form2!Label1.FloodType = 0 - // Form2.Caption = "* XMODEM(Checksum) BINARY RECEIVE *" - // Form2!Label1.Caption = "Errors: 0 Bytes: 0" - - pX->buf.xbuf.cSOH = (char)1; // assumed already got this, put into buffer - - do { - if (!pX->bCRC && - ((DEBUG_I1 GetXmodemBlock(pX->ser, ((char*)&(pX->buf.xbuf)) + 1, - sizeof(pX->buf.xbuf) - 1)) != - sizeof(pX->buf.xbuf) - 1 || - (DEBUG_I2 ValidateSEQ(&(pX->buf.xbuf), block & 255)) || - (DEBUG_I3 CalcCheckSum(pX->buf.xbuf.aDataBuf, - sizeof(pX->buf.xbuf.aDataBuf)) != - pX->buf.xbuf.bCheckSum))) { -// did not receive properly -// TODO: deal with repeated packet, sequence number for previous packet - -#ifdef DEBUG_CODE - sprintf(szERR, "A%ld,%d,%d,%d,%d,%d", block, i1, i2, i3, - pX->buf.xbuf.aSEQ, pX->buf.xbuf.aNotSEQ); -//#ifdef STAND_ALONE -// fprintf(stderr, "TEMPORARY (csum): seq=%x, ~seq=%x i1=%d, i2=%d, -// i3=%d\n", pX->buf.xbuf.aSEQ, pX->buf.xbuf.aNotSEQ, i1, i2, i3); -//#endif // STAND_ALONE -#endif // DEBUG_CODE - - XModemFlushInput(pX->ser); // necessary to avoid problems - - cY = _NAK_; // send NAK (to get the checksum version) - ecount++; // for this packet - etotal++; - } else if (pX->bCRC && - ((DEBUG_I1 GetXmodemBlock(pX->ser, ((char*)&(pX->buf.xcbuf)) + 1, - sizeof(pX->buf.xcbuf) - 1)) != - sizeof(pX->buf.xcbuf) - 1 || - (DEBUG_I2 ValidateSEQC(&(pX->buf.xcbuf), block & 255)) || - (DEBUG_I3 CalcCRC(pX->buf.xcbuf.aDataBuf, - sizeof(pX->buf.xbuf.aDataBuf)) != - pX->buf.xcbuf.wCRC))) { -// did not receive properly -// TODO: deal with repeated packet, sequence number for previous packet - -#ifdef DEBUG_CODE - sprintf(szERR, "B%ld,%d,%d,%d,%d,%d", block, i1, i2, i3, - pX->buf.xcbuf.aSEQ, pX->buf.xcbuf.aNotSEQ); -//#ifdef STAND_ALONE -// fprintf(stderr, "TEMPORARY (CRC): seq=%x, ~seq=%x i1=%d, i2=%d, -// i3=%d\n", pX->buf.xcbuf.aSEQ, pX->buf.xcbuf.aNotSEQ, i1, i2, i3); -//#endif // STAND_ALONE -#endif // DEBUG_CODE - - XModemFlushInput(pX->ser); // necessary to avoid problems - - if (block > 1) { - cY = _NAK_; // TODO do I need this? - } else { - cY = - 'C'; // send 'CRC' NAK (the character 'C') (to get the CRC version) - } - ecount++; // for this packet - etotal++; - } else { -#ifdef ARDUINO - if (pX->file.write((const uint8_t*)&(pX->buf.xbuf.aDataBuf), - sizeof(pX->buf.xbuf.aDataBuf)) != - sizeof(pX->buf.xbuf.aDataBuf)) { - return -2; // write error on output file - } -#elif defined(WIN32) - cbWrote = 0; - if (!WriteFile(pX->file, &(pX->buf.xbuf.aDataBuf), - sizeof(pX->buf.xbuf.aDataBuf), &cbWrote, NULL) || - cbWrote != sizeof(pX->buf.xbuf.aDataBuf)) { - XmodemTerminate(pX); - return -2; // write error on output file - } -#else // ARDUINO - if (write(pX->file, &(pX->buf.xbuf.aDataBuf), - sizeof(pX->buf.xbuf.aDataBuf)) != - sizeof(pX->buf.xbuf.aDataBuf)) { - XmodemTerminate(pX); - return -2; // write error on output file +int xmodemTransmit(unsigned char* src, int srcsz) { + unsigned char + xbuff[1030]; /* 1024 for XModem 1k + 3 head chars + 2 crc + nul */ + int bufsz, crc = -1; + unsigned char packetno = 1; + int i, c, len = 0; + int retry; + + for (;;) { + for (retry = 0; retry < 16; ++retry) { + if ((c = _inbyte((DLY_1S) << 1)) >= 0) { + switch (c) { + case 'C': + crc = 1; + goto start_trans; + case NAK: + crc = 0; + goto start_trans; + case CAN: + if ((c = _inbyte(DLY_1S)) == CAN) { + _outbyte(ACK); + flushinput(); + return -1; /* canceled by remote */ + } + break; + default: + break; + } } -#endif // ARDUINO - cY = _ACK_; // send ACK - block++; - filesize += sizeof(pX->buf.xbuf.aDataBuf); // TODO: need method to avoid - // extra crap at end of file - ecount = 0; // zero out error count for next packet } - -#if defined(STAND_ALONE) || defined(SFTARDCAL) - fprintf(stderr, - "block %ld %ld bytes %d errors\r" -#ifndef SFTARDCAL - "\n" -#endif // SFTARDCAL - , - block - 1, filesize, ecount); -#endif // STAND_ALONE - - ec2 = 0; // ** error count #2 ** - - while (ecount < TOTAL_ERROR_COUNT && - ec2 < ACK_ERROR_COUNT) // ** loop to get SOH or EOT character ** - { - WriteXmodemChar(pX->ser, cY); // ** output appropriate command char ** - - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1) { - if (pX->buf.xbuf.cSOH == _CAN_) // ** CTRL-X 'CAN' - terminate - { - XmodemTerminate(pX); - return 1; // terminated - } else if (pX->buf.xbuf.cSOH == _EOT_) // ** EOT - end - { - WriteXmodemChar( - pX->ser, - _ACK_); // ** send an ACK (most XMODEM protocols expect THIS) - // WriteXmodemChar(pX->ser, _ENQ_); // ** send an ENQ - - return 0; // I am done - } else if (pX->buf.xbuf.cSOH == _SOH_) // ** SOH - sending next packet - { - break; // leave this loop + _outbyte(CAN); + _outbyte(CAN); + _outbyte(CAN); + flushinput(); + return -2; /* no sync */ + + for (;;) { + start_trans: +#ifdef TRANSMIT_XMODEM_1K + xbuff[0] = STX; + bufsz = 1024; +#else + xbuff[0] = SOH; + bufsz = 128; +#endif + xbuff[1] = packetno; + xbuff[2] = ~packetno; + c = srcsz - len; + if (c > bufsz) + c = bufsz; + if (c > 0) { + memset(&xbuff[3], 0, bufsz); + memcpy(&xbuff[3], &src[len], c); + if (c < bufsz) + xbuff[3 + c] = CTRLZ; + if (crc) { + unsigned short ccrc = crc16_ccitt(&xbuff[3], bufsz); + xbuff[bufsz + 3] = (ccrc >> 8) & 0xFF; + xbuff[bufsz + 4] = ccrc & 0xFF; } else { - // TODO: deal with repeated packet, i.e. previous sequence number - - XModemFlushInput(pX->ser); // necessary to avoid problems (since the - // character was unexpected) - // if I was asking for the next block, and got an unexpected - // character, do a NAK; otherwise, - // just repeat what I did last time - - if (cY == _ACK_) // ACK - { - cY = _NAK_; // NACK + unsigned char ccks = 0; + for (i = 3; i < bufsz + 3; ++i) { + ccks += xbuff[i]; } - - ec2++; + xbuff[bufsz + 3] = ccks; } - } else { - ecount++; // increase total error count, and try writing the 'ACK' or - // 'NACK' again - } - } - - if (ec2 >= ACK_ERROR_COUNT) // wasn't able to get a packet - { - break; - } - - } while (ecount < TOTAL_ERROR_COUNT); - - XmodemTerminate(pX); - return 1; // terminated -} - -/** \ingroup xmodem_internal - * \brief Generic function to send a file via XMODEM (CRC or Checksum) - * - * \param pX A pointer to an 'XMODEM_BUF' with valid ser, and file members, and - *the polled - * 'NAK' value assigned to the cSOH member (first byte) within the 'buf' union. - * \return A zero value on success, negative on error, positive on cancel - * - * The calling function will need to poll for a 'C' or NAK from the client (as - *appropriate) - * and assign that character to the cSOH member in the 'buf' union (either xbuf - *or xcbuf since - * the 'cSOH' will always be the first byte). Then call this function to send - *content - * via XMODEM from 'file'.\n - * It is important to record the NAK character before calling this function - *since the 'C' or - * 'NAK' value will be used to determine whether to use CRC or CHECKSUM.\n - * This function will return zero on success, a negative value on error, and a - *positive - * value if the transfer was canceled by the receiver. -**/ -int SendXmodem(XMODEM* pX) { -#ifdef WIN32 - DWORD cbRead; -#endif // WIN32 - int ecount, ec2; - short i1; - long etotal, filesize, filepos, block; - - ecount = 0; - etotal = 0; - filesize = 0; - filepos = 0; - block = 1; - - pX->bCRC = - 0; // MUST ASSIGN TO ZERO FIRST or XMODEM-CHECKSUM may not work properly - -// ** already got first 'NAK' character on entry as pX->buf.xbuf.cSOH ** - -#ifdef ARDUINO - - filesize = pX->file.size(); - -#else // ARDUINO - -#ifdef WIN32 - filesize = (long)SetFilePointer(pX->file, 0, NULL, FILE_END); -#else // WIN32 - filesize = (long)lseek(pX->file, 0, SEEK_END); -#endif // WIN32 - if (filesize < 0) // not allowed - { -#ifdef STAND_ALONE - fputs("SendXmodem fail (file size)\n", stderr); -#endif // STAND_ALONE - return -1; - } - -#ifdef WIN32 - SetFilePointer(pX->file, 0, NULL, FILE_BEGIN); -#else // WIN32 - lseek(pX->file, 0, SEEK_SET); // position at beginning -#endif // WIN32 - -#endif // ARDUINO - - do { - // ** depending on type of transfer, place the packet - // ** into pX->buf with all fields appropriately filled. - - if (filepos >= filesize) // end of transfer - { - for (i1 = 0; i1 < 8; i1++) { - WriteXmodemChar(pX->ser, - _EOT_); // ** send an EOT marking end of transfer - - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) != - 1) // this takes up to 5 seconds - { - // nothing returned - try again? - // break; // for now I loop, uncomment to bail out - } else if (pX->buf.xbuf.cSOH == - _ENQ_ // an 'ENQ' (apparently some expect this) - || pX->buf.xbuf.cSOH == _ACK_ // an 'ACK' (most XMODEM - // implementations expect - // this) - || pX->buf.xbuf.cSOH == _CAN_) // CTRL-X = TERMINATE - { - // both normal and 'abnormal' termination. - break; + for (retry = 0; retry < MAXRETRANS; ++retry) { + for (i = 0; i < bufsz + 4 + (crc ? 1 : 0); ++i) { + _outbyte(xbuff[i]); + } + if ((c = _inbyte(DLY_1S)) >= 0) { + switch (c) { + case ACK: + ++packetno; + len += bufsz; + goto start_trans; + case CAN: + if ((c = _inbyte(DLY_1S)) == CAN) { + _outbyte(ACK); + flushinput(); + return -1; /* canceled by remote */ + } + break; + case NAK: + default: + break; + } + } } - } - - XmodemTerminate(pX); - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "SendXmodem return %d\n", i1 >= 8 ? 1 : 0); -#endif // STAND_ALONE - return i1 >= 8 ? 1 : 0; // return 1 if receiver choked on the 'EOT' - // marker, else 0 for 'success' - } - -// TODO: progress indicator [can be LCD for arduino, blinky lights, ??? and -// of course stderr for everyone else] -// If filesize& <> 0 Then Form2!Label1.FloodPercent = 100 * filepos& / -// filesize& - -#if defined(STAND_ALONE) || defined(SFTARDCAL) - fprintf(stderr, - "block %ld %ld of %ld bytes %d errors\r" -#ifndef SFTARDCAL - "\n" -#endif // SFTARDCAL - , - block, filepos, filesize, ecount); -#endif // STAND_ALONE - - if (pX->buf.xbuf.cSOH != 'C' // XMODEM CRC - && pX->buf.xbuf.cSOH != (char)_NAK_) // NAK - { - // increase error count, bail if it's too much - - ec2++; - } - -#ifdef ARDUINO - pX->file.seek(filepos); // in case I'm doing a 'retry' and I have to - // re-read part of the file -#elif defined(WIN32) - SetFilePointer(pX->file, filepos, NULL, FILE_BEGIN); -#else // ARDUINO - lseek(pX->file, filepos, SEEK_SET); // same reason as above -#endif // ARDUINO - - // fortunately, xbuf and xcbuf are the same through the end of 'aDataBuf' so - // I can read the file NOW using 'xbuf' for both CRC and CHECKSUM versions - - if ((filesize - filepos) >= sizeof(pX->buf.xbuf.aDataBuf)) { -#ifdef ARDUINO - i1 = pX->file.read(pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf)); -#elif defined(WIN32) - cbRead = 0; - if (!ReadFile(pX->file, pX->buf.xbuf.aDataBuf, - sizeof(pX->buf.xbuf.aDataBuf), &cbRead, NULL)) { - i1 = -1; - } else { - i1 = (int)cbRead; - } -#else // ARDUINO - i1 = - read(pX->file, pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf)); -#endif // ARDUINO - - if (i1 != sizeof(pX->buf.xcbuf.aDataBuf)) { - // TODO: read error - send a ctrl+x ? - } - } else { - memset(pX->buf.xcbuf.aDataBuf, '\x1a', - sizeof(pX->buf.xcbuf.aDataBuf)); // fill with ctrl+z which is what - // the spec says -#ifdef ARDUINO - i1 = pX->file.read(pX->buf.xbuf.aDataBuf, filesize - filepos); -#elif defined(WIN32) - cbRead = 0; - if (!ReadFile(pX->file, pX->buf.xbuf.aDataBuf, filesize - filepos, - &cbRead, NULL)) { - i1 = -1; + _outbyte(CAN); + _outbyte(CAN); + _outbyte(CAN); + flushinput(); + return -4; /* xmit error */ } else { - i1 = (int)cbRead; - } -#else // ARDUINO - i1 = read(pX->file, pX->buf.xbuf.aDataBuf, filesize - filepos); -#endif // ARDUINO - - if (i1 != (filesize - filepos)) { - // TODO: read error - send a ctrl+x ? - } - } - - if (pX->buf.xbuf.cSOH == - 'C' || // XMODEM CRC 'NAK' (first time only, typically) - ((pX->buf.xbuf.cSOH == _ACK_ || pX->buf.xbuf.cSOH == _NAK_) && - pX->bCRC)) // identifies ACK/NACK with XMODEM CRC - { - pX->bCRC = 1; // make sure (only matters the first time, really) - - // calculate the CRC, assign to the packet, and then send it - - pX->buf.xcbuf.cSOH = 1; // must send SOH as 1st char - pX->buf.xcbuf.wCRC = - CalcCRC(pX->buf.xcbuf.aDataBuf, sizeof(pX->buf.xcbuf.aDataBuf)); - - GenerateSEQC(&(pX->buf.xcbuf), (unsigned char)block); - - // send it - - i1 = WriteXmodemBlock(pX->ser, &(pX->buf.xcbuf), sizeof(pX->buf.xcbuf)); - if (i1 != sizeof(pX->buf.xcbuf)) // write error - { - // TODO: handle write error (send ctrl+X ?) - } - } else if (pX->buf.xbuf.cSOH == _NAK_ || // 'NAK' (checksum method, may - // also be with CRC method) - (pX->buf.xbuf.cSOH == _ACK_ && - !pX->bCRC)) // identifies ACK with XMODEM CHECKSUM - { - pX->bCRC = 0; // make sure (this ALSO allows me to switch modes on error) - - // calculate the CHECKSUM, assign to the packet, and then send it - - pX->buf.xbuf.cSOH = 1; // must send SOH as 1st char - pX->buf.xbuf.bCheckSum = - CalcCheckSum(pX->buf.xbuf.aDataBuf, sizeof(pX->buf.xbuf.aDataBuf)); - - GenerateSEQ(&(pX->buf.xbuf), (unsigned char)block); - - // send it - - i1 = WriteXmodemBlock(pX->ser, &(pX->buf.xbuf), sizeof(pX->buf.xbuf)); - if (i1 != sizeof(pX->buf.xbuf)) // write error - { - // TODO: handle write error (send ctrl+X ?) - } - } - - ec2 = 0; - - while (ecount < TOTAL_ERROR_COUNT && - ec2 < ACK_ERROR_COUNT) // loop to get ACK or NACK - { - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1) { - if (pX->buf.xbuf.cSOH == _CAN_) // ** CTRL-X - terminate - { - XmodemTerminate(pX); - - return 1; // terminated - } else if (pX->buf.xbuf.cSOH == _NAK_ || // ** NACK - pX->buf.xbuf.cSOH == 'C') // ** CRC NACK - { - break; // exit inner loop and re-send packet - } else if (pX->buf.xbuf.cSOH == _ACK_) // ** ACK - sending next packet - { - filepos += sizeof(pX->buf.xbuf.aDataBuf); - block++; // increment file position and block count - - break; // leave inner loop, send NEXT packet - } else { - XModemFlushInput(pX->ser); // for now, do this here too - ec2++; + for (retry = 0; retry < 10; ++retry) { + _outbyte(EOT); + if ((c = _inbyte((DLY_1S) << 1)) == ACK) + break; } - } else { - ecount++; // increase total error count, then loop back and re-send - // packet - break; + flushinput(); + return (c == ACK) ? len : -5; } } - - if (ec2 >= ACK_ERROR_COUNT) { - break; // that's it, I'm done with this - } - - } while ( - ecount < - TOTAL_ERROR_COUNT /* * 2 */); // twice error count allowed for sending - - // TODO: progress indicator - // If filesize& <> 0 And filepos& <= filesize& Then - // Form2!Label1.FloodPercent = 100 * filepos& / filesize& - // Else - // Form2!Label1.FloodPercent = 100 - // End If - - // ** at this point it is important to indicate the errors - // ** and flush all buffers, and terminate process! - - XmodemTerminate(pX); -#ifdef STAND_ALONE - fputs("SendXmodem fail (total error count)\n", stderr); -#endif // STAND_ALONE - return -2; // exit on error -} - -/** \ingroup xmodem_internal - * \brief Calling function for ReceiveXmodem - * - * \param pX A pointer to an 'XMODEM_BUF' with valid ser, and file members - * \return A zero value on success, negative on error, positive on cancel - * - * This is a generic 'calling function' for ReceiveXmodem that checks for - * a response to 'C' and 'NAK' characters, and sets up the XMODEM transfer - * for either CRC or CHECKSUM mode.\n - * This function will return zero on success, a negative value on error, and a - *positive - * value if the transfer was canceled by the receiver. -**/ -int XReceiveSub(XMODEM* pX) { - int i1; - - // start with CRC mode [try 8 times to get CRC] - - pX->bCRC = 1; - - for (i1 = 0; i1 < 8; i1++) { - WriteXmodemChar(pX->ser, 'C'); // start with NAK for XMODEM CRC - - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1) { - if (pX->buf.xbuf.cSOH == _SOH_) // SOH - packet is on its way - { - return ReceiveXmodem(pX); - } else if (pX->buf.xbuf.cSOH == - _EOT_) // an EOT [blank file? allow this?] - { - return 0; // for now, do this - } else if (pX->buf.xbuf.cSOH == _CAN_) // cancel - { - return 1; // canceled - } - } - } - - pX->bCRC = 0; - - // try again, this time using XMODEM CHECKSUM - for (i1 = 0; i1 < 8; i1++) { - WriteXmodemChar(pX->ser, _NAK_); // switch to NAK for XMODEM Checksum - - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1) { - if (pX->buf.xbuf.cSOH == _SOH_) // SOH - packet is on its way - { - return ReceiveXmodem(pX); - } else if (pX->buf.xbuf.cSOH == - _EOT_) // an EOT [blank file? allow this?] - { - return 0; // for now, do this - } else if (pX->buf.xbuf.cSOH == _CAN_) // cancel - { - return 1; // canceled - } - } - } - - XmodemTerminate(pX); - - return -3; // fail -} - -/** \ingroup xmodem_internal - * \brief Calling function for SendXmodem - * - * \param pX A pointer to an 'XMODEM_BUF' with valid ser, and file members - * \return A zero value on success, negative on error, positive on cancel - * - * This is a generic 'calling function' for SendXmodem that checks for polls by - *the - * receiver, and places the 'NAK' or 'C' character into the 'buf' member of the - *XMODEM - * structure so that SendXmodem can use the correct method, either CRC or - *CHECKSUM mode.\n - * This function will return zero on success, a negative value on error, and a - *positive - * value if the transfer was canceled by the receiver. -**/ -int XSendSub(XMODEM* pX) { - unsigned long ulStart; - -// waiting up to 30 seconds for transfer to start. this is part of the spec? - -#ifdef ARDUINO - ulStart = millis(); -#else // ARDUINO - ulStart = MyMillis(); -#endif // ARDUINO - - do { - if (GetXmodemBlock(pX->ser, &(pX->buf.xbuf.cSOH), 1) == 1) { - if (pX->buf.xbuf.cSOH == 'C' || // XMODEM CRC - pX->buf.xbuf.cSOH == _NAK_) // NAK - XMODEM CHECKSUM - { -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "Got %d, continuing\n", pX->buf.xbuf.cSOH); -#endif // STAND_ALONE - return SendXmodem(pX); - } else if (pX->buf.xbuf.cSOH == _CAN_) // cancel - { -#ifdef STAND_ALONE - fputs("XSendSub fail (cancel)\n", stderr); -#endif // STAND_ALONE - return 1; // canceled - } - } - } -#ifdef ARDUINO - while ((short)(millis() - ulStart) < 30000); // 30 seconds -#else // ARDUINO - while ((int)(MyMillis() - ulStart) < 30000); -#endif // ARDUINO - - XmodemTerminate(pX); - -#ifdef STAND_ALONE - fputs("XSendSub fail (timeout)\n", stderr); -#endif // STAND_ALONE - return -3; // fail -} - -// typedef struct _XMODEM_ -//{ -// SERIAL_TYPE ser; -// FILE_TYPE file; -// -// union -// { -// XMODEM_BUF xbuf; -// XMODEMC_BUF xcbuf; -// } buf; // 133 bytes -// -// unsigned char bCRC; // non-zero for CRC, zero for checksum -// -//} __attribute__((__packed__)) XMODEM; - -#ifdef ARDUINO - -short XReceive(SDClass* pSD, HardwareSerial* pSer, const char* szFilename) { - short iRval; - XMODEM xx; - - memset(&xx, 0, sizeof(xx)); - - xx.ser = pSer; - - if (pSD->exists((char*)szFilename)) { - pSD->remove((char*)szFilename); - } - - xx.file = pSD->open((char*)szFilename, FILE_WRITE); - if (!xx.file) { - return -9; // can't create file - } - - iRval = XReceiveSub(&xx); - - xx.file.close(); - - if (iRval) { - WriteXmodemChar(pSer, _CAN_); // cancel (make sure) - - pSD->remove((char*)szFilename); // delete file on error } - - return iRval; -} - -int XSend(SDClass* pSD, HardwareSerial* pSer, const char* szFilename) { - short iRval; - XMODEM xx; - - memset(&xx, 0, sizeof(xx)); - - xx.ser = pSer; - - xx.file = pSD->open(szFilename, FILE_READ); - if (!xx.file) { - return -9; // can't open file - } - - iRval = XSendSub(&xx); - - xx.file.close(); - - return iRval; -} - -#else // ARDUINO - -int XReceive(SERIAL_TYPE hSer, const char* szFilename, int nMode) { - int iRval; - XMODEM xx; -#if !defined(ARDUINO) && !defined(WIN32) - int iFlags; -#endif // !ARDUINO - -#ifdef DEBUG_CODE - szERR[0] = 0; -#endif // DEBUG_CODE - memset(&xx, 0, sizeof(xx)); - - xx.ser = hSer; - -#ifdef WIN32 - DeleteFile(szFilename); - - nMode = nMode; // to avoid unused parameter warnings - // TODO: translate 'nMode' into file attributes? for now ignore it - xx.file = CreateFile(szFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (xx.file == INVALID_HANDLE_VALUE) -#else // WIN32 - unlink(szFilename); // make sure it does not exist, first - xx.file = open(szFilename, O_CREAT | O_TRUNC | O_WRONLY, nMode); - - if (xx.file == -1) // bad file handle on POSIX systems -#endif // WIN32 - { -#ifdef STAND_ALONE - fprintf(stderr, "XReceive fail \"%s\" errno=%d\n", szFilename, errno); -#endif // STAND_ALONE - return -9; // can't create file - } - -#if !defined(ARDUINO) && !defined(WIN32) - iFlags = fcntl(hSer, F_GETFL); -#endif // !ARDUINO - - iRval = XReceiveSub(&xx); - -#if !defined(ARDUINO) && !defined(WIN32) - if (iFlags == -1 || fcntl(hSer, F_SETFL, iFlags) == -1) { - fprintf(stderr, - "Warning: 'fcntl' call to restore flags failed, errno=%d\n", - errno); - } -#endif // !ARDUINO - -#ifdef WIN32 - CloseHandle(xx.file); -#else // WIN32 - close(xx.file); -#endif // WIN32 - - if (iRval) { -#ifdef WIN32 - DeleteFile(szFilename); -#else // WIN32 - unlink(szFilename); // delete file on error -#endif // WIN32 - } - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "XReceive returns %d\n", iRval); -#endif // STAND_ALONE - return iRval; -} - -int XSend(SERIAL_TYPE hSer, const char* szFilename) { - int iRval; - XMODEM xx; -#if !defined(ARDUINO) && !defined(WIN32) - int iFlags; -#endif // !ARDUINO - -#ifdef DEBUG_CODE - szERR[0] = 0; -#endif // DEBUG_CODE - memset(&xx, 0, sizeof(xx)); - - xx.ser = hSer; - -#ifdef WIN32 - xx.file = - CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); - - if (xx.file == INVALID_HANDLE_VALUE) -#else // WIN32 - xx.file = open(szFilename, O_RDONLY, 0); - - if (xx.file == -1) // bad file handle on POSIX systems -#endif // WIN32 - { -#ifdef STAND_ALONE - fprintf(stderr, "XSend fail \"%s\" errno=%d\n", szFilename, errno); -#endif // STAND_ALONE - return -9; // can't open file - } - -#if !defined(ARDUINO) && !defined(WIN32) - iFlags = fcntl(hSer, F_GETFL); -#endif // !ARDUINO - - iRval = XSendSub(&xx); - -#if !defined(ARDUINO) && !defined(WIN32) - if (iFlags == -1 || fcntl(hSer, F_SETFL, iFlags) == -1) { - fprintf(stderr, - "Warning: 'fcntl' call to restore flags failed, errno=%d\n", - errno); - } -#endif // !ARDUINO - -#ifdef WIN32 - CloseHandle(xx.file); -#else // WIN32 - close(xx.file); -#endif // WIN32 - -#if defined(STAND_ALONE) && defined(DEBUG_CODE) - fprintf(stderr, "XSend returning %d\n", iRval); -#endif // STAND_ALONE - return iRval; -} - -#endif // ARDUINO - -#if defined(STAND_ALONE) && !defined(SFTARDCAL) - -static const char szSER[] = "/dev/ttyU0"; - -#include <termios.h> - -/** \ingroup xmodem_standalone - * \brief Terminal configuration (POSIX only) - * - * \param iFile The open file handle for the serial connection - * \param iBaud The baud rate for the connection - * \param iParity The parity, < 0 for even, > 0 for odd, 0 for none - * \param iBits The number of bits (5, 6, 7, 8) - * \param iStop The number of stop bits (1 or 2) - * - * This is a sample tty config function to CORRECTLY set up a serial connection - * to allow XMODEM transfer. The important details here are the use of the - * 'termios' structure and utility functions to DISABLE all of the things that - * would otherwise cause trouble, like CRLF translation, CTRL+C handling, etc. -**/ -void ttyconfig(int iFile, int iBaud, int iParity, int iBits, int iStop) { - int i1; - struct termios sIOS; - - i1 = fcntl(iFile, F_GETFL); - - i1 |= O_NONBLOCK; // i1 &= ~O_NONBLOCK); // turn OFF non-blocking? - - fcntl(iFile, F_SETFL, i1); - - if (!tcgetattr(iFile, &sIOS)) { - cfsetspeed(&sIOS, iBaud); - sIOS.c_cflag &= ~(CSIZE | PARENB | CS5 | CS6 | CS7 | CS8); - sIOS.c_cflag |= - iBits == 5 ? CS5 : iBits == 6 ? CS6 : iBits == 7 ? CS7 - : CS8; // 8 is default - if (iStop == 2) { - sIOS.c_cflag |= CSTOPB; - } else { - sIOS.c_cflag &= ~CSTOPB; - } - - sIOS.c_cflag &= - ~CRTSCTS; // hardware flow control _DISABLED_ (so I can do the reset) - sIOS.c_cflag |= CLOCAL; // ignore any modem status lines - - if (!iParity) { - sIOS.c_cflag &= ~(PARENB | PARODD); - } else if (iParity > 0) // odd - { - sIOS.c_cflag |= (PARENB | PARODD); - } else // even (negative) - { - sIOS.c_cflag &= PARODD; - sIOS.c_cflag |= PARENB; - } - - // sIOS.c_iflag |= IGNCR; // ignore CR - - // do not translate characters or xon/xoff and ignore break - sIOS.c_iflag &= ~(IGNBRK | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | - IMAXBEL | ISTRIP); // turn these off - -#if defined(__FreeBSD__) - sIOS.c_oflag &= ~(OPOST | ONLCR | OCRNL | TABDLY | ONOEOT | ONOCR | - ONLRET); // FreeBSD version -#else // Linux? YMMV - sIOS.c_oflag &= ~(OPOST | ONLCR | OCRNL | TABDLY | ONOCR | - ONLRET); // turn these off too (see man termios) -#endif // FBSD vs Linux - -// make sure echoing is disabled and control chars aren't translated or omitted -#if defined(__FreeBSD__) - sIOS.c_lflag &= ~(ECHO | ECHOKE | ECHOE | ECHONL | ECHOPRT | ECHOCTL | - ICANON | IEXTEN | ISIG | ALTWERASE); -#else // Linux? YMMV - sIOS.c_lflag &= ~(ECHO | ECHOKE | ECHOE | ECHONL | ECHOPRT | ECHOCTL | - ICANON | IEXTEN | ISIG); -#endif // FBSD vs Linux - sIOS.c_cc[VMIN] = 0; // ensures no 'grouping' of input - sIOS.c_cc[VTIME] = 0; // immediate return - - if (tcsetattr(iFile, TCSANOW, &sIOS)) { - fprintf(stderr, "error %d setting attributes\n", errno); - } - } else { - fprintf(stderr, "error %d getting attributes\n", errno); - } -} - -/** \ingroup xmodem_standalone - * \brief Arduino 'reset' function - * - * \param iFile The open file handle for the serial connection - * - * The Arduino serial port typically has the DTR/RTS lines configured so that - * a proper 'pulse' will cause a hardware reset of the device. This function - *will - * send that pulse to the Arduino, and wait for a short time afterwards for the - * hardware reset to take place. -**/ -void reset_arduino(int iFile) { - unsigned int sFlags; - unsigned long ulStart; - int i1; - - // toggle the RTS and DTR high, low, then high - so much easier via - // POSIX-compatible OS! - - ioctl(iFile, TIOCMGET, &sFlags); - - sFlags &= ~(TIOCM_DTR | TIOCM_RTS); // the high to low transition discharges - // the capacitor (signal is inverted on - // board) - if (ioctl(iFile, TIOCMSET, &sFlags) < 0) { - fprintf(stderr, "WARNING: ioctl() returns < 0, errno=%d (%xH)\n", errno, - errno); - } - - usleep( - 250000); // avrdude does this for 50 msecs, my change has it at 50msecs - - sFlags |= TIOCM_DTR | TIOCM_RTS; // leave it in THIS state when I'm done - if (ioctl(iFile, TIOCMSET, &sFlags) < 0) { - fprintf(stderr, "WARNING: ioctl() returns < 0, errno=%d (%xH)\n", errno, - errno); - } - - usleep(50000); // avrdude does this for 50 msecs (no change) - - ulStart = MyMillis(); - - // flush whatever is there, (5 seconds) - - while ((MyMillis() - ulStart) < 5000) { - i1 = read(iFile, &i1, 1); - if (i1 == 1) { - ulStart = MyMillis(); - } else { - usleep(1000); - } - } -} - -int main(int argc, char* argv[]) { - int hSer; - char tbuf[256]; - int i1, iSR = 0; - - if (argc < 3) { - fputs("Usage: [prog] [S|R] filename\n", stderr); - return 1; - } - - if (argv[1][0] == 'R' || argv[1][1] == 'r') { - iSR = -1; - } else if (argv[1][0] == 'S' || argv[1][1] == 's') { - iSR = 1; - } else if (argv[1][0] == 'X' || argv[1][1] == 'x') { - iSR = 0; // test function - } else { - fputs("Usage: [prog] [S|R] filename (b)\n", stderr); - return 1; - } - - hSer = open(szSER, (O_RDWR | O_NONBLOCK), 0); - if (hSer == -1) { - fprintf(stderr, "Unable to open \"%s\" errno=%d\n", szSER, errno); - return 3; - } - - fputs("TTYCONFIG\n", stderr); - ttyconfig(hSer, 9600, 0, 8, 1); - - reset_arduino(hSer); - - fprintf(stderr, "Sleeping for 10 seconds to allow reset\n"); - - // usleep(10000000); - for (i1 = 0; i1 < 10; i1++) { - XModemFlushInput(hSer); - } - - for (i1 = 0; i1 < 3; i1++) { - sprintf(tbuf, "X%c%s", argv[1][0], argv[2]); - - fprintf(stderr, "writing: \"%s\"\n", tbuf); - strcat(tbuf, "\r"); - WriteXmodemBlock(hSer, tbuf, strlen(tbuf)); - - fputs("flush input\n", stderr); - XModemFlushInput(hSer); - - // wait for an LF response - - if (iSR > 0) { - fputs("XSEND\n", stderr); - if (XSend(hSer, argv[2])) { - fputs("ERROR\n", stderr); - } else { - fputs("SUCCESS!\n", stderr); - i1 = 0; - break; - } - } else if (iSR < 0) { - fputs("XRECEIVE\n", stderr); - if (XReceive(hSer, argv[2], 0664)) { - fputs("ERROR\n", stderr); - } else { - fputs("SUCCESS!\n", stderr); - i1 = 0; - break; - } - } else { - // test function - XModemFlushInput(hSer); // continue doing this - break; // done (once only) - } - } - - fputs("EXIT\n", stderr); - close(hSer); - - return i1 ? 1 : -1; } -#endif // STAND_ALONE diff --git a/upload/xmodem.h b/upload/xmodem.h index 37bed1a..62346f1 100644 --- a/upload/xmodem.h +++ b/upload/xmodem.h @@ -1,310 +1,10 @@ #ifndef XMODEM_H #define XMODEM_H -////////////////////////////////////////////////////////////////////////////// -// // -// _ _ // -// __ __ _ __ ___ ___ __| | ___ _ __ ___ | |__ // -// \ \/ /| '_ ` _ \ / _ \ / _` | / _ \| '_ ` _ \ | '_ \ // -// > < | | | | | || (_) || (_| || __/| | | | | | _ | | | | // -// /_/\_\|_| |_| |_| \___/ \__,_| \___||_| |_| |_|(_)|_| |_| // -// // -// // -////////////////////////////////////////////////////////////////////////////// -// // -// Copyright (c) 2012 by S.F.T. Inc. - All rights reserved // -// Use, copying, and distribution of this software are licensed according // -// to the LGPLv2.1, or a BSD-like license, as appropriate (see below) // -// // -////////////////////////////////////////////////////////////////////////////// +#include "legato.h" -#ifdef ARDUINO -/** \mainpage S.F.T. XMODEM library (ARDUINO version) - * - * Copyright (c) 2012 by S.F.T. Inc. - All rights reserved\n - * - * The source files include DOXYGEN SUPPORT to properly document the library - * Please excuse the additional comments necessary to make this work. - * Instead, build the doxygen output and view the documentation, as - * well as the code itself WITHOUT all of the doxygen markup comments. - * \n - * \n - * This library was designed to work with POSIX-compliant operating systems - * such as Linux, FreeBSD, and OSX, and also on Arduino microcontrollers. - * The intent was to provide an identical code base for both ends of the - * XMODEM transfer, compilable as either C or C++ code for maximum flexibility. - * - * Normally you will only need to use one of these two functions:\n - * \n - * \ref XSend() - send a file via XMODEM\n - * \ref XReceive() - receive a file via XMODEM\n - * \n - * The rest of the documentation was provided to help you debug any problems, - * or even to write your own library (as appropriate).\n - * - * LICENSE - * - * This software is licensed under either the LGPLv2 or a BSD-like license. - * For more information, see\n - * http://opensource.org/licenses/BSD-2-Clause\n - * http://www.gnu.org/licenses/lgpl-2.1.html\n - * and the above copyright notice.\n - * \n - * In short, you may use this software anyway you like, provided that you - * do not hold S.F.T. Inc. responsible for consequential or inconsequential - * damages resulting from use, modification, abuse, or anything else done - * with this software, and you include the appropriate license (either LGPLv2 - * or a BSD-like license) and comply with the requirements of said license.\n - * So, if you use a BSD-like license, you can copy the license template at - * the abovementioned URL and sub in the copyright notice as shown above. - * Or, you may use an LGPLv2 license, and then provide source files with a - * re-distributed or derived work (including a complete re-write with this - * library as a template). A link back to the original source, of course, - * would be appreciated but is not required. -**/ -#else // ARDUINO -/** \mainpage S.F.T. XMODEM library - * - * Copyright (c) 2012 by S.F.T. Inc. - All rights reserved\n - * - * The source files include DOXYGEN SUPPORT to properly document the library - * Please excuse the additional comments necessary to make this work. - * Instead, build the doxygen output and view the documentation, as - * well as the code itself WITHOUT all of the doxygen markup comments. - * \n - * \n - * This library was designed to work with POSIX-compliant operating systems - * such as Linux, FreeBSD, and OSX, and also on Arduino microcontrollers. - * The intent was to provide an identical code base for both ends of the - * XMODEM transfer, compilable as either C or C++ code for maximum flexibility. - * - * Normally you will only need to use one of these two functions:\n - * \n - * \ref XSend() - send a file via XMODEM\n - * \ref XReceive() - receive a file via XMODEM\n - * \n - * The rest of the documentation was provided to help you debug any problems, - * or even to write your own library (as appropriate).\n - * - * LICENSE - * - * This software is licensed under either the LGPLv2 or a BSD-like license. - * For more information, see\n - * http://opensource.org/licenses/BSD-2-Clause\n - * http://www.gnu.org/licenses/lgpl-2.1.html\n - * and the above copyright notice.\n - * \n - * In short, you may use this software anyway you like, provided that you - * do not hold S.F.T. Inc. responsible for consequential or inconsequential - * damages resulting from use, modification, abuse, or anything else done - * with this software, and you include the appropriate license (either LGPLv2 - * or a BSD-like license) and comply with the requirements of said license.\n - * So, if you use a BSD-like license, you can copy the license template at - * the abovementioned URL and sub in the copyright notice as shown above. - * Or, you may use an LGPLv2 license, and then provide source files with a - * re-distributed or derived work (including a complete re-write with this - * library as a template). A link back to the original source, of course, - * would be appreciated but is not required. -**/ -#endif // ARDUINO - -/** \file xmodem.h - * \brief main header file for S.F.T. XMODEM library - * - * S.F.T. XMODEM library -**/ - -/** \defgroup xmodem_api XModem API - * high-level API functions -*/ - -/** \defgroup xmodem_internal XModem Internal - * internal support functions -*/ - -#ifdef STANDALONE -/** \defgroup xmodem_standalone XModem Stand-alone - * internal 'standalone' functions, an example for POSIX implementation -*/ -#endif // STANDALONE - -// determine if arduino build, define ARDUINO if not already done - -#if defined(__AVR__) || defined(AVR) || defined(__AVR) || defined(__AVR_ARCH__) -#ifndef ARDUINO -#define ARDUINO /* hopefully I cover all compiler variations */ -#endif // ARDUINO -#endif // __AVR__ - -#include <stdlib.h> - -// required include files -#ifdef ARDUINO -// arduino includes -#include <Arduino.h> -#include <SD.h> -#include <HardwareSerial.h> /* may already be included by 'Arduino.h' */ -#include <avr/pgmspace.h> - -#elif WIN32 -// win32 includes -#include <Windows.h> -#include <io.h> -#else // POSIX -// posix includes -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/time.h> -#include <sys/ioctl.h> // for IOCTL definitions -#include <memory.h> -#endif // OS-dependent includes - -// required per-OS definitions -#ifdef ARDUINO - -// file and serial types for Arduino -#define FILE_TYPE File -#define SERIAL_TYPE HardwareSerial* - -#elif defined(WIN32) // WINDOWS - -// file and serial types for WIN32 -#define FILE_TYPE HANDLE -#define SERIAL_TYPE HANDLE - -#else // POSIX - -// file and serial types for POSIX -#define FILE_TYPE int -#define SERIAL_TYPE int - -#endif // ARDUINO - -// common definitions - -#define SILENCE_TIMEOUT 5000 /* 5 seconds */ -#define TOTAL_ERROR_COUNT 32 -#define ACK_ERROR_COUNT 8 - -// Arduino build uses C++ so I must define functions properly - -#ifdef ARDUINO - -/** \ingroup xmodem_api - * \brief Receive a file using XMODEM protocol (ARDUINO version) - * - * \param pSD A pointer to an SDClass object, such as &SD (the default SD - *library object is 'SD') - * \param pSer A pointer to a HardwareSerial object, such as &Serial - * \param szFilename A pointer to a (const) 0-byte terminated string containing - *the file name - * \return A value of zero on success, negative on failure, positive if - *canceled - * - * Call this function to receive a file, passing the SD card's initialized - *SDClass object pointer, - * and the pointer to the 'HardwareSerial' object to be used for serial - *communication, and the - * name of the file to create from the XMODEM stream. The function will return - *a value of zero on - * success. On failure or cancelation, the file will be deleted.\n - * If the specified file exists before calling this function, it will be - *overwritten. If you do not - * want to unconditionally overwrite an existing file, you should test to see - *if it exists first - * using the SD library. - * -**/ -short XReceive(SDClass* pSD, HardwareSerial* pSer, const char* szFilename); - -/** \ingroup xmodem_api - * \brief Send a file using XMODEM protocol (ARDUINO version) - * - * \param pSD A pointer to an SDClass object, such as &SD (the default SD - *library object is 'SD') - * \param pSer A pointer to a HardwareSerial object, such as &Serial - * \param szFilename A pointer to a (const) 0-byte terminated string containing - *the file name - * \return A value of zero on success, negative on failure, positive if - *canceled - * - * Call this function to send a file, passing the SD card's initialized SDClass - *object pointer, - * and the pointer to the 'HardwareSerial' object to be used for serial - *communication, and the - * name of the file to send via the XMODEM stream. The function will return a - *value of zero on - * success. If the file does not exist, the function will return a 'failure' - *value and cancel - * the transfer. - * -**/ -int XSend(SDClass* pSD, HardwareSerial* pSer, const char* szFilename); - -#ifdef DEBUG_CODE -const char* XMGetError(void); -#endif // DEBUG_CODE - -#else // ARDUINO - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -/** \ingroup xmodem_api - * \brief Receive a file using XMODEM protocol - * - * \param hSer A 'HANDLE' for the open serial connection - * \param szFilename A pointer to a (const) 0-byte terminated string containing - *the file name - * \param nMode The file mode to be used on create (RWX bits) - * \return A value of zero on success, negative on failure, positive if - *canceled - * - * Call this function to receive a file, passing the handle to the open serial - *connection, and the - * name and mode of the file to create from the XMODEM stream. The function - *will return a value of zero on - * success. On failure or cancelation, the file will be deleted.\n - * If the specified file exists before calling this function, it will be - *overwritten. If you do not - * want to unconditionally overwrite an existing file, you should test to see - *if it exists first. - * -**/ -int XReceive(SERIAL_TYPE hSer, const char* szFilename, int nMode); - -/** \ingroup xmodem_api - * \brief Send a file using XMODEM protocol - * - * \param hSer A 'HANDLE' for the open serial connection - * \param szFilename A pointer to a (const) 0-byte terminated string containing - *the file name - * \return A value of zero on success, negative on failure, positive if - *canceled - * - * Call this function to receive a file, passing the handle to the open serial - *connection, and the - * name and mode of the file to send via the XMODEM stream. The function will - *return a value of zero on - * success. If the file does not exist, the function will return a 'failure' - *value and cancel - * the transfer. - * -**/ -int XSend(SERIAL_TYPE hSer, const char* szFilename); - -#ifdef DEBUG_CODE -const char* XMGetError(void); -#endif // DEBUG_CODE - -#ifdef __cplusplus -}; -#endif // __cplusplus - -#endif // ARDUINO +int xmodemTransmit(unsigned char* src, int srcsz); +uint8_t _inbyte(unsigned short timeout); +void _outbyte(uint8_t c); #endif |