diff options
| author | Nick Van Doorn <vandoorn.nick@gmail.com> | 2018-09-10 23:23:21 -0700 | 
|---|---|---|
| committer | Nick Van Doorn <vandoorn.nick@gmail.com> | 2018-09-10 23:23:21 -0700 | 
| commit | 83639bdffb6b9355a29d2b2e55807aa3b6837bbd (patch) | |
| tree | e5858b146a3901880e5babaa89aacbe0979f0552 | |
Initial commit
| -rw-r--r-- | mtkSetup.adef | 27 | ||||
| -rw-r--r-- | upload/Component.cdef | 22 | ||||
| -rw-r--r-- | upload/crc16.c | 69 | ||||
| -rw-r--r-- | upload/crc16.h | 33 | ||||
| -rw-r--r-- | upload/upload.c | 161 | ||||
| -rw-r--r-- | upload/xmodem.c | 1879 | ||||
| -rw-r--r-- | upload/xmodem.h | 310 | 
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 | 
