Skip to content
68 changes: 59 additions & 9 deletions src/xmodem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@

#include "xmodem.h"
#include "SPILock.h"
#include <cstdio>

#ifdef FSCom

Expand Down Expand Up @@ -119,18 +120,42 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
case meshtastic_XModem_Control_STX:
if ((xmodemPacket.seq == 0) && !isReceiving && !isTransmitting) {
// NULL packet has the destination filename
memcpy(filename, &xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
memset(filename, 0, sizeof(filename));
size_t filenameLen = xmodemPacket.buffer.size;
if (filenameLen >= sizeof(filename)) filenameLen = sizeof(filename) - 1;
memcpy(filename, xmodemPacket.buffer.bytes, filenameLen);
filename[filenameLen] = '\0';
Comment thread
benallfree marked this conversation as resolved.

if (xmodemPacket.control == meshtastic_XModem_Control_SOH) { // Receive this file and put to Flash
spiLock->lock();
file = FSCom.open(filename, FILE_O_WRITE);
if (recvCommitPending) {
FSCom.remove(recvTmpPath);
recvCommitPending = false;
}
int plen = snprintf(recvTmpPath, sizeof(recvTmpPath), "%s.tmp", filename);
if (plen < 0 || (size_t)plen >= sizeof(recvTmpPath)) {
spiLock->unlock();
LOG_WARN("XModem: Receive path too long for .tmp suffix");
sendControl(meshtastic_XModem_Control_NAK);
isReceiving = false;
break;
}
if (FSCom.exists(recvTmpPath) && !FSCom.remove(recvTmpPath)) {
spiLock->unlock();
LOG_WARN("XModem: Failed to remove existing temp before receive: %s", recvTmpPath);
sendControl(meshtastic_XModem_Control_NAK);
isReceiving = false;
break;
}
file = FSCom.open(recvTmpPath, FILE_O_WRITE);
spiLock->unlock();
if (file) {
sendControl(meshtastic_XModem_Control_ACK);
isReceiving = true;
packetno = 1;
break;
}
LOG_WARN("XModem: Failed to open temp for receive: %s", recvTmpPath);
sendControl(meshtastic_XModem_Control_NAK);
isReceiving = false;
break;
Expand Down Expand Up @@ -186,24 +211,49 @@ void XModemAdapter::handlePacket(meshtastic_XModem xmodemPacket)
}
break;
case meshtastic_XModem_Control_EOT:
// End of transmission
sendControl(meshtastic_XModem_Control_ACK);
// End of transmission (receive): flush temp, rename to final, ACK only after commit
if (!isReceiving && !recvCommitPending)
break;
if (recvCommitPending) {
if (!renameFile(recvTmpPath, filename)) {
LOG_WARN("XModem: rename temp to final failed (retry): %s -> %s", recvTmpPath, filename);
sendControl(meshtastic_XModem_Control_NAK);
break;
}
sendControl(meshtastic_XModem_Control_ACK);
recvCommitPending = false;
memset(recvTmpPath, 0, sizeof(recvTmpPath));
break;
}
spiLock->lock();
file.flush();
file.close();
spiLock->unlock();
isReceiving = false;
if (!renameFile(recvTmpPath, filename)) {
LOG_WARN("XModem: rename temp to final failed: %s -> %s", recvTmpPath, filename);
sendControl(meshtastic_XModem_Control_NAK);
recvCommitPending = true;
break;
}
sendControl(meshtastic_XModem_Control_ACK);
memset(recvTmpPath, 0, sizeof(recvTmpPath));
break;
case meshtastic_XModem_Control_CAN:
// Cancel transmission and remove file
// Cancel receive: drop partial temp only (final path unchanged)
if (!isReceiving && !recvCommitPending)
break;
sendControl(meshtastic_XModem_Control_ACK);
spiLock->lock();
file.flush();
file.close();

FSCom.remove(filename);
if (isReceiving) {
file.flush();
file.close();
}
FSCom.remove(recvTmpPath);
spiLock->unlock();
isReceiving = false;
recvCommitPending = false;
memset(recvTmpPath, 0, sizeof(recvTmpPath));
break;
case meshtastic_XModem_Control_ACK:
// Acknowledge Send the next packet
Expand Down
3 changes: 3 additions & 0 deletions src/xmodem.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class XModemAdapter

private:
bool isReceiving = false;
bool recvCommitPending = false;
bool isTransmitting = false;
bool isEOT = false;

Expand All @@ -68,6 +69,8 @@ class XModemAdapter
#endif

char filename[sizeof(meshtastic_XModem_buffer_t::bytes)] = {0};
/** Receive scratch path (`filename` + `.tmp`); commit via renameFile on EOT. */
char recvTmpPath[sizeof(filename) + 5] = {0};

protected:
meshtastic_XModem xmodemStore = meshtastic_XModem_init_zero;
Expand Down
Loading