Issue #20 - TeraTerm XModem file transfers with W command.

This commit is contained in:
Tom Nisbet 2020-11-14 13:26:08 -05:00
parent ec4686bfa0
commit e4703f4a88
6 changed files with 88 additions and 42 deletions

View File

@ -71,7 +71,10 @@ void PromDevice::resetDebugStats() {
debugLastAddress = 0; debugLastAddress = 0;
debugLastExpected = 0; debugLastExpected = 0;
debugLastReadback = 0; debugLastReadback = 0;
debugStartChar = 0; debugRxDuplicates = 0;
debugExtraChars = 0;
debugRxStarts = 0;
debugRxSyncErrors = 0;
} }
void PromDevice::printDebugStats() { void PromDevice::printDebugStats() {
Serial.print(F("debugBlockWrites: ")); Serial.print(F("debugBlockWrites: "));
@ -82,8 +85,14 @@ void PromDevice::printDebugStats() {
Serial.println(debugLastExpected, HEX); Serial.println(debugLastExpected, HEX);
Serial.print(F("debugLastReadback: ")); Serial.print(F("debugLastReadback: "));
Serial.println(debugLastReadback, HEX); Serial.println(debugLastReadback, HEX);
Serial.print(F("debugStartChar: ")); Serial.print(F("debugRxDuplicates: "));
Serial.println(debugStartChar, HEX); Serial.println(debugRxDuplicates);
Serial.print(F("debugExtraChars: "));
Serial.println(debugExtraChars);
Serial.print(F("debugRxStarts: "));
Serial.println(debugRxStarts);
Serial.print(F("debugRxSyncErrors: "));
Serial.println(debugRxSyncErrors);
} }
// BEGIN PRIVATE METHODS // BEGIN PRIVATE METHODS

View File

@ -32,7 +32,10 @@ class PromDevice
uint32_t debugLastAddress; // Last address with an issue uint32_t debugLastAddress; // Last address with an issue
uint8_t debugLastExpected; // Last expected readback value uint8_t debugLastExpected; // Last expected readback value
uint8_t debugLastReadback; // Last actual readback value uint8_t debugLastReadback; // Last actual readback value
uint8_t debugStartChar; // XModem start char sent or received uint32_t debugRxDuplicates; // XModem received packet duplicates
uint32_t debugExtraChars; // XModem received bytes beyond first packet
uint8_t debugRxStarts; // XModem start transfer requests
uint8_t debugRxSyncErrors; // XModem unexpected characters after packet
protected: protected:
uint32_t mSize; // Size of the device, in bytes uint32_t mSize; // Size of the device, in bytes

View File

@ -19,7 +19,7 @@
#include "XModem.h" #include "XModem.h"
static const char * MY_VERSION = "2.6"; static const char * MY_VERSION = "2.7";
// Global status // Global status

View File

@ -2,17 +2,13 @@
#include "XModem.h" #include "XModem.h"
#include "CmdStatus.h" #include "CmdStatus.h"
// The original TommyPROM code used XModem CRC protocol but some Linux communication // The original TommyPROM code used XModem CRC protocol but this requires parameters to be
// programs do not seem to support this correctly. The default is now to use basic XModem // changed for the host communication program. The default is now to use basic XModem
// with the 8-bit checksum instead of the 16-bit CRC error checking. This shouldn't // with the 8-bit checksum instead of the 16-bit CRC error checking. This shouldn't
// matter because communication errors aren't likely to happen on a 3 foot USB cable like // matter because communication errors aren't likely to happen on a 3 foot USB cable like
// they did over the long distance dail-up lines that XModem was designed for. Uncomment // they did over the long distance dail-up lines that XModem was designed for. Uncomment
// the XMODEM_CRC_PROTOCOL line below to restore the original XMODEM CRC support. // the XMODEM_CRC_PROTOCOL line below to restore the original XMODEM CRC support.
//#define XMODEM_CRC_PROTOCOL
// Update!!! - Teraterm does not seem to like sending files to TommyPROM with plain
// XModem, so the default is back to CRC. For Linux, comment out the line below to use
// checksum.
#define XMODEM_CRC_PROTOCOL
enum enum
{ {
@ -37,6 +33,7 @@ enum
PKTLEN = 128 PKTLEN = 128
}; };
enum { RX_OK, RX_ERROR, RX_IGNORE };
uint32_t XModem::ReceiveFile(uint32_t address) uint32_t XModem::ReceiveFile(uint32_t address)
{ {
@ -45,6 +42,7 @@ uint32_t XModem::ReceiveFile(uint32_t address)
uint8_t seq = 1; uint8_t seq = 1;
uint32_t numBytes = 0; uint32_t numBytes = 0;
bool complete = false; bool complete = false;
int status;
#ifdef XMODEM_CRC_PROTOCOL #ifdef XMODEM_CRC_PROTOCOL
Serial.println(F("Send the image file using XMODEM CRC")); Serial.println(F("Send the image file using XMODEM CRC"));
@ -70,15 +68,16 @@ uint32_t XModem::ReceiveFile(uint32_t address)
{ {
case XMDM_SOH: case XMDM_SOH:
// Start of a packet // Start of a packet
if (ReceivePacket(buffer, PKTLEN, seq++, address)) status = ReceivePacket(buffer, PKTLEN, seq, address);
{ if (status == RX_OK) {
// Good packet - advance the buffer
numBytes += PKTLEN; numBytes += PKTLEN;
address += PKTLEN; address += PKTLEN;
} ++seq;
else } else if (status == RX_ERROR) {
{
return 0; return 0;
} }
// else ignore the duplicate packet
break; break;
case XMDM_EOT: case XMDM_EOT:
@ -129,7 +128,6 @@ bool XModem::SendFile(uint32_t address, uint32_t fileSize)
{ {
rxChar = GetChar(); rxChar = GetChar();
} }
promDevice.debugStartChar = rxChar;
if (rxChar != XMDM_TRANSFER_START) if (rxChar != XMDM_TRANSFER_START)
{ {
#ifdef XMODEM_CRC_PROTOCOL #ifdef XMODEM_CRC_PROTOCOL
@ -182,13 +180,14 @@ void XModem::Cancel()
// Private functions // Private functions
int XModem::GetChar(int msWaitTime) int XModem::GetChar(int msWaitTime)
{ {
msWaitTime *= 4;
do do
{ {
if (Serial.available() > 0) if (Serial.available() > 0)
{ {
return Serial.read(); return Serial.read();
} }
delay(1); delayMicroseconds(250);
} while (msWaitTime--); } while (msWaitTime--);
return -1; return -1;
@ -219,21 +218,22 @@ uint16_t XModem::UpdateCrc(uint16_t crc, uint8_t data)
bool XModem::StartReceive() bool XModem::StartReceive()
{ {
for (int retries = 30; (retries); --retries) delay(2000);
for (int retries = 20; (retries); --retries)
{ {
// Send the XMDM_TRANSFER_START until the sender of the file responds with // Send the XMDM_TRANSFER_START until the sender of the file responds with
// something. The start character will be sent once a second for a number of // something. The start character will be sent once a second for a number of
// seconds. If nothing is received in that time then return false to indicate // seconds. If nothing is received in that time then return false to indicate
// that the transfer did not start. // that the transfer did not start.
++promDevice.debugRxStarts;
Serial.write(XMDM_TRANSFER_START); Serial.write(XMDM_TRANSFER_START);
promDevice.debugStartChar = XMDM_TRANSFER_START; for (unsigned ms = 30000; (ms); --ms)
for (int ms = 1000; (ms); --ms)
{ {
delayMicroseconds(100);
if (Serial.available() > 0) if (Serial.available() > 0)
{ {
return true; return true;
} }
delay(1);
} }
} }
@ -241,7 +241,7 @@ bool XModem::StartReceive()
} }
bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr) int XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr)
{ {
int c; int c;
uint8_t rxSeq1, rxSeq2; uint8_t rxSeq1, rxSeq2;
@ -263,7 +263,7 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
cmdStatus.error("Timeout waiting for next packet char."); cmdStatus.error("Timeout waiting for next packet char.");
cmdStatus.setValueDec(0, "seq", seq); cmdStatus.setValueDec(0, "seq", seq);
Serial.write(XMDM_CAN); Serial.write(XMDM_CAN);
return false; return RX_ERROR;
} }
buffer[ix] = (uint8_t) c; buffer[ix] = (uint8_t) c;
calcCrc = UpdateCrc(calcCrc, buffer[ix]); calcCrc = UpdateCrc(calcCrc, buffer[ix]);
@ -275,7 +275,52 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
#else #else
rxCrc = GetChar(); rxCrc = GetChar();
#endif #endif
if ((calcCrc != rxCrc) || (rxSeq1 != seq) || ((rxSeq1 ^ rxSeq2) != 0xff))
// At this point in the code, there should not be anything in the receive buffer
// because the sender has just sent a complete packet and is waiting on a response. If
// the XModem transfer is not started quickly on the host side, TeraTerm seems to
// buffer the NAK character that starts the transfer and use it as an indication that
// the first packet should be sent again. That can cause the Arduino's 64 byte buffer
// to overflow and gets the transfer out of sync. The normal processing will detect
// the first packet and will process it correctly, but the partial packet in the
// buffer will cause the processing of the next packet to fail.
delay(10);
if (Serial.available()) {
++promDevice.debugRxSyncErrors;
if (seq == 1) {
// If any characters are in the buffer after the first packet, quietly eat
// them to get back on a packet boundary and send a NAK to cause a retransmit
// of the packet.
while (Serial.available() > 0) {
delay(1);
Serial.read();
++promDevice.debugExtraChars;
}
delay(1);
Serial.write(XMDM_NAK);
return RX_IGNORE;
} else {
// Sync issues shouldn't happen at any other time so fail the transfer.
cmdStatus.error("RX sync error.");
cmdStatus.setValueDec(0, "seq", seq);
cmdStatus.setValueDec(1, "rxSeq1", rxSeq1);
cmdStatus.setValueDec(2, "rxSeq2", rxSeq2);
cmdStatus.setValueHex(3, "calcCrc", calcCrc);
cmdStatus.setValueHex(4, "rxCrc", rxCrc);
Serial.write(XMDM_CAN);
return RX_ERROR;
}
}
if ((calcCrc == rxCrc) && (rxSeq1 == seq - 1) && ((rxSeq1 ^ rxSeq2) == 0xff))
{
// Resend of previously processed packet.
delay(10);
++promDevice.debugRxDuplicates;
Serial.write(XMDM_ACK);
return RX_IGNORE;
}
else if ((calcCrc != rxCrc) || (rxSeq1 != seq) || ((rxSeq1 ^ rxSeq2) != 0xff))
{ {
// Fail if the CRC or sequence number is not correct or if the two received // Fail if the CRC or sequence number is not correct or if the two received
// sequence numbers are not the complement of one another. // sequence numbers are not the complement of one another.
@ -286,7 +331,7 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
cmdStatus.setValueHex(3, "calcCrc", calcCrc); cmdStatus.setValueHex(3, "calcCrc", calcCrc);
cmdStatus.setValueHex(4, "rxCrc", rxCrc); cmdStatus.setValueHex(4, "rxCrc", rxCrc);
Serial.write(XMDM_CAN); Serial.write(XMDM_CAN);
return false; return RX_ERROR;
} }
else else
{ {
@ -295,12 +340,12 @@ bool XModem::ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, u
{ {
cmdStatus.error("Write failed"); cmdStatus.error("Write failed");
cmdStatus.setValueHex(0, "address", destAddr); cmdStatus.setValueHex(0, "address", destAddr);
return false; return RX_ERROR;
} }
Serial.write(XMDM_ACK); Serial.write(XMDM_ACK);
} }
return true; return RX_OK;
} }

View File

@ -33,7 +33,7 @@ class XModem
int GetChar(int msWaitTime = 3000); int GetChar(int msWaitTime = 3000);
uint16_t UpdateCrc(uint16_t crc, uint8_t data); uint16_t UpdateCrc(uint16_t crc, uint8_t data);
bool StartReceive(); bool StartReceive();
bool ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr); int ReceivePacket(uint8_t buffer[], unsigned bufferSize, uint8_t seq, uint32_t destAddr);
void SendPacket(uint32_t address, uint8_t seq); void SendPacket(uint32_t address, uint8_t seq);
}; };

View File

@ -94,22 +94,11 @@ received file size.
Once the READ or WRITE command is issued to the programmer, the transfer must Once the READ or WRITE command is issued to the programmer, the transfer must
be started on the host program. be started on the host program.
~~Note that previous versions of TommyPROM used the XMODEM-CRC protocol to complete the file Note that previous versions of TommyPROM used the XMODEM-CRC protocol to complete the file
transfers for the READ and WRITE commands. This did not work well with minicom and other transfers for the READ and WRITE commands. This did not work well with minicom and other
Linux programs that rely on the sz/rz commands. Versions 2.5 and later of TommyPROM now Linux programs that rely on the sz/rz commands. Versions 2.5 and later of TommyPROM now
use basic XModem with the 8-bit checksum. The XModem-CRC support is still available as a use basic XModem with the 8-bit checksum. The XModem-CRC support is still available as a
compile-time option. See [issue #19](https://github.com/TomNisbet/TommyPROM/issues/19) compile-time option. See [issue #19](https://github.com/TomNisbet/TommyPROM/issues/19)
for details.~~
**UPDATE: XModem troubles continue!**
It seems that TeraTerm does not like to send files to TommyPROM in XModem checksum mode.
The default transfer has been restored to XModem-CRC mode.
* For TeraTerm, be sure to select the XModem-CRC option in the dialog box when receiving files.
* For Linux/minicom, comment out the XMODEM_CRC_PROTOCOL in XModem.cpp and use checksum mode.
Given that there are now problems with both minicomm and TeraTerm, it seems likely that the
actual problem lies somewhere in the TommyPROM code. See [issue #20](https://github.com/TomNisbet/TommyPROM/issues/20)
for details. for details.
The files used for READ and WRITE are simple binary images. This can be created directly The files used for READ and WRITE are simple binary images. This can be created directly