summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Van Doorn <vandoorn.nick@gmail.com>2018-09-10 23:23:21 -0700
committerNick Van Doorn <vandoorn.nick@gmail.com>2018-09-10 23:23:21 -0700
commit83639bdffb6b9355a29d2b2e55807aa3b6837bbd (patch)
treee5858b146a3901880e5babaa89aacbe0979f0552
Initial commit
-rw-r--r--mtkSetup.adef27
-rw-r--r--upload/Component.cdef22
-rw-r--r--upload/crc16.c69
-rw-r--r--upload/crc16.h33
-rw-r--r--upload/upload.c161
-rw-r--r--upload/xmodem.c1879
-rw-r--r--upload/xmodem.h310
7 files changed, 2501 insertions, 0 deletions
diff --git a/mtkSetup.adef b/mtkSetup.adef
new file mode 100644
index 0000000..c376de4
--- /dev/null
+++ b/mtkSetup.adef
@@ -0,0 +1,27 @@
+sandboxed: false
+start: manual
+version: 2.14.0
+
+executables:
+{
+ mtkSetup = ( upload )
+}
+
+processes:
+{
+ run:
+ {
+ ( mtkSetup )
+ }
+ faultAction: restartApp
+ envVars:
+ {
+ LE_LOG_LEVEL = DEBUG
+ }
+}
+
+bindings:
+{
+ mtkSetup.upload.mtRst -> gpioService.le_gpioPin2
+ mtkSetup.upload.mtBootstrap -> gpioService.le_gpioPin24
+}
diff --git a/upload/Component.cdef b/upload/Component.cdef
new file mode 100644
index 0000000..b7d57eb
--- /dev/null
+++ b/upload/Component.cdef
@@ -0,0 +1,22 @@
+cflags:
+{
+ -std=gnu99
+ -I$BRNKL_ROOT/apps/util
+ -Wno-unused-but-set-variable
+}
+
+requires:
+{
+ api:
+ {
+ mtRst = le_gpio.api
+ mtBootstrap = le_gpio.api
+ }
+}
+
+sources:
+{
+ upload.c
+ xmodem.c
+ $BRNKL_ROOT/apps/util/util.c
+}
diff --git a/upload/crc16.c b/upload/crc16.c
new file mode 100644
index 0000000..3e27ecd
--- /dev/null
+++ b/upload/crc16.c
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include "crc16.h"
+
+/* CRC16 implementation acording to CCITT standards */
+
+static const unsigned short crc16tab[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108,
+ 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
+ 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b,
+ 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
+ 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee,
+ 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
+ 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d,
+ 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5,
+ 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
+ 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4,
+ 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
+ 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13,
+ 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
+ 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e,
+ 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1,
+ 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
+ 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0,
+ 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
+ 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657,
+ 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
+ 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882,
+ 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e,
+ 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
+ 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d,
+ 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
+ 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};
+
+unsigned short crc16_ccitt(const void* buf, int len) {
+ register int counter;
+ register unsigned short crc = 0;
+ for (counter = 0; counter < len; counter++)
+ crc = (crc << 8) ^ crc16tab[((crc >> 8) ^ *(char*)buf++) & 0x00FF];
+ return crc;
+}
diff --git a/upload/crc16.h b/upload/crc16.h
new file mode 100644
index 0000000..3140de5
--- /dev/null
+++ b/upload/crc16.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _CRC16_H_
+#define _CRC16_H_
+
+unsigned short crc16_ccitt(const void* buf, int len);
+
+#endif /* _CRC16_H_ */
diff --git a/upload/upload.c b/upload/upload.c
new file mode 100644
index 0000000..a18244e
--- /dev/null
+++ b/upload/upload.c
@@ -0,0 +1,161 @@
+#include "legato.h"
+#include "interfaces.h"
+#include "xmodem.h"
+#include "util.h"
+
+#define MTK7697_BAUD 115200
+#define MTK7697_BAUDX 115200 * 8
+static const char* SERIAL_PORT_PATH = "/dev/ttyHS0";
+static const char* LDR_BIN_PATH = "/home/root/mtfiles/mt7697_bootloader.bin";
+static const char* N9_BIN_PATH =
+ "/home/root/mtfiles/WIFI_RAM_CODE_MT76X7_in_flash.bin";
+static const char* CM4_BIN_PATH = "/home/root/mtfiles/ble_smart_connect.bin";
+static const char* DA_PATH = "/home/root/mtfiles/da97.bin";
+
+typedef enum { LDR = 1, N9 = 3, CM4 = 2 } MtkMemorySegment;
+
+typedef struct {
+ int serialPort;
+ int errorCount;
+ int cCount;
+ int retry;
+ int startTime;
+} FlashState;
+
+FlashState state = {0, 0, 0, 0};
+
+void configureSerialPort(FlashState* s, int baud) {
+ close(s->serialPort);
+ s->serialPort = fd_openSerial(SERIAL_PORT_PATH, baud);
+}
+
+bool flashDa(FlashState* s) {
+ configureSerialPort(&state, MTK7697_BAUD);
+ LE_INFO("Sending DA over xmodem");
+ bool r = XSend(s->serialPort, DA_PATH) == 0;
+ LE_INFO("DA success: %d", r);
+ return r;
+}
+
+void putIntoBootloader() {
+ // make sure the bootstrap pin is on
+ mtBootstrap_Activate();
+ // reset state && wait
+ mtRst_Deactivate();
+ sleep(1);
+ // pull out of reset
+ mtRst_Activate();
+}
+
+void retryInitSequence(FlashState* s) {
+ s->retry++;
+ s->cCount = 0;
+ s->errorCount = 0;
+ s->startTime = util_getUnixDatetime();
+ close(s->serialPort);
+ putIntoBootloader();
+ configureSerialPort(s, MTK7697_BAUD);
+}
+
+bool verifyInitSequence(FlashState* s) {
+ uint8_t data;
+ bool initDone = false;
+ s->startTime = util_getUnixDatetime();
+ while (!initDone) {
+ data = fd_getChar(s->serialPort);
+ fd_flush(s->serialPort);
+ if (data == 'C') {
+ s->cCount++;
+ LE_INFO("Got a C");
+ } else if (data != 0) {
+ s->errorCount++;
+ LE_INFO("Got an error");
+ }
+ if (s->cCount > 1) {
+ initDone = true;
+ LE_INFO("Init done");
+ break;
+ }
+ if (s->errorCount > 3 || (util_getUnixDatetime() - s->startTime > 3)) {
+ LE_INFO("Retrying...");
+ retryInitSequence(s);
+ }
+ if (s->retry > 3) {
+ LE_INFO("Aborting");
+ break;
+ }
+ }
+ return initDone;
+}
+
+bool flashBinary(FlashState* s, MtkMemorySegment segment, const char* binPath) {
+ putIntoBootloader();
+ configureSerialPort(s, MTK7697_BAUD);
+ bool init = verifyInitSequence(s);
+ LE_INFO("Init verified: %d", init);
+ flashDa(s);
+ configureSerialPort(s, MTK7697_BAUDX);
+ sleep(1);
+ bool initDone = false;
+ int data = '0';
+ switch (segment) {
+ case LDR:
+ fd_puts(s->serialPort, "1\r");
+ break;
+ case N9:
+ fd_puts(s->serialPort, "3\r");
+ break;
+ case CM4:
+ fd_puts(s->serialPort, "2\r");
+ break;
+ }
+ while (!initDone) {
+ LE_INFO("here %c", data);
+ data = fd_getChar(s->serialPort);
+ if (data == 'C') {
+ initDone = true;
+ break;
+ }
+ }
+ fd_flush(s->serialPort);
+ LE_INFO("Sending %s over xmodem", binPath);
+ bool r = XSend(s->serialPort, binPath) == 0;
+ LE_INFO("%s success: %d", binPath, r);
+ sleep(1);
+ fd_puts(s->serialPort, "C\r");
+ fd_flush(s->serialPort);
+ return true;
+}
+
+bool flashLdr(FlashState* s) {
+ LE_INFO("Flashing LDR segment");
+ return flashBinary(s, LDR, LDR_BIN_PATH);
+}
+
+bool flashN9(FlashState* s) {
+ LE_INFO("Flashing N9 segment");
+ return flashBinary(s, N9, N9_BIN_PATH);
+}
+
+bool flashCm4(FlashState* s) {
+ LE_INFO("Flashing CM4");
+ return flashBinary(s, CM4, CM4_BIN_PATH);
+}
+
+void resetPins() {
+ mtBootstrap_Deactivate();
+}
+
+void configureGpio() {
+ mtRst_SetPushPullOutput(MTRST_ACTIVE_HIGH, true);
+ mtBootstrap_SetPushPullOutput(MTBOOTSTRAP_ACTIVE_HIGH, true);
+}
+
+COMPONENT_INIT {
+ configureGpio();
+ flashLdr(&state);
+ flashN9(&state);
+ flashCm4(&state);
+ resetPins();
+ close(state.serialPort);
+}
diff --git a/upload/xmodem.c b/upload/xmodem.c
new file mode 100644
index 0000000..dd471d7
--- /dev/null
+++ b/upload/xmodem.c
@@ -0,0 +1,1879 @@
+//////////////////////////////////////////////////////////////////////////////
+// //
+// _ //
+// __ __ _ __ ___ ___ __| | ___ _ __ ___ ___ //
+// \ \/ /| '_ ` _ \ / _ \ / _` | / _ \| '_ ` _ \ / __| //
+// > < | | | | | || (_) || (_| || __/| | | | | | _| (__ //
+// /_/\_\|_| |_| |_| \___/ \__,_| \___||_| |_| |_|(_)\___| //
+// //
+// //
+//////////////////////////////////////////////////////////////////////////////
+// //
+// 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
+
+#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++;
+ }
+ }
+
+ fputc('\n', stderr);
+ fflush(stderr);
+}
+#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);
+}
+
+/** \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;
+ }
+ }
+ }
+
+ 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) {
+ 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;
+ }
+ }
+ }
+
+ // 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;
+ }
+#endif // STAND_ALONE, DEBUG_CODE
+ ulStart = MyMillis();
+ } else {
+ usleep(1000);
+ }
+ }
+
+#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
+ }
+#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
+ } 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
+ }
+
+ ec2++;
+ }
+ } 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;
+ }
+ }
+
+ 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;
+ } 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++;
+ }
+ } else {
+ ecount++; // increase total error count, then loop back and re-send
+ // packet
+ break;
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..37bed1a
--- /dev/null
+++ b/upload/xmodem.h
@@ -0,0 +1,310 @@
+#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) //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#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
+
+#endif