mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-25 22:20:27 +00:00
Merge pull request #2112 from meshtastic/xmodem-proto
Add Chunked File Transfer to API
This commit is contained in:
commit
d35b619063
@ -6,6 +6,7 @@
|
|||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#include "xmodem.h"
|
||||||
|
|
||||||
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||||
#error FromRadio is too big
|
#error FromRadio is too big
|
||||||
@ -31,6 +32,7 @@ void PhoneAPI::handleStartConfig()
|
|||||||
if (!isConnected()) {
|
if (!isConnected()) {
|
||||||
onConnectionChanged(true);
|
onConnectionChanged(true);
|
||||||
observe(&service.fromNumChanged);
|
observe(&service.fromNumChanged);
|
||||||
|
observe(&xModem.packetReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
// even if we were already connected - restart our state machine
|
// even if we were already connected - restart our state machine
|
||||||
@ -48,6 +50,7 @@ void PhoneAPI::close()
|
|||||||
state = STATE_SEND_NOTHING;
|
state = STATE_SEND_NOTHING;
|
||||||
|
|
||||||
unobserve(&service.fromNumChanged);
|
unobserve(&service.fromNumChanged);
|
||||||
|
unobserve(&xModem.packetReady);
|
||||||
releasePhonePacket(); // Don't leak phone packets on shutdown
|
releasePhonePacket(); // Don't leak phone packets on shutdown
|
||||||
releaseQueueStatusPhonePacket();
|
releaseQueueStatusPhonePacket();
|
||||||
|
|
||||||
@ -90,6 +93,10 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
LOG_INFO("Disconnecting from phone\n");
|
LOG_INFO("Disconnecting from phone\n");
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
|
case ToRadio_xmodemPacket_tag:
|
||||||
|
LOG_INFO("Got xmodem packet\n");
|
||||||
|
xModem.handlePacket(toRadioScratch.xmodemPacket);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore nop messages
|
// Ignore nop messages
|
||||||
// LOG_DEBUG("Error: unexpected ToRadio variant\n");
|
// LOG_DEBUG("Error: unexpected ToRadio variant\n");
|
||||||
@ -287,10 +294,14 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
// Do we have a message from the mesh?
|
// Do we have a message from the mesh?
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
||||||
if (queueStatusPacketForPhone) {
|
if (queueStatusPacketForPhone) {
|
||||||
|
|
||||||
fromRadioScratch.which_payload_variant = FromRadio_queueStatus_tag;
|
fromRadioScratch.which_payload_variant = FromRadio_queueStatus_tag;
|
||||||
fromRadioScratch.queueStatus = *queueStatusPacketForPhone;
|
fromRadioScratch.queueStatus = *queueStatusPacketForPhone;
|
||||||
releaseQueueStatusPhonePacket();
|
releaseQueueStatusPhonePacket();
|
||||||
|
} else if (xmodemPacketForPhone) {
|
||||||
|
fromRadioScratch.which_payload_variant = FromRadio_xmodemPacket_tag;
|
||||||
|
fromRadioScratch.xmodemPacket = *xmodemPacketForPhone;
|
||||||
|
free(xmodemPacketForPhone);
|
||||||
|
xmodemPacketForPhone = NULL;
|
||||||
} else if (packetForPhone) {
|
} else if (packetForPhone) {
|
||||||
printPacket("phone downloaded packet", packetForPhone);
|
printPacket("phone downloaded packet", packetForPhone);
|
||||||
|
|
||||||
@ -353,6 +364,7 @@ bool PhoneAPI::available()
|
|||||||
case STATE_SEND_MODULECONFIG:
|
case STATE_SEND_MODULECONFIG:
|
||||||
case STATE_SEND_COMPLETE_ID:
|
case STATE_SEND_COMPLETE_ID:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_SEND_NODEINFO:
|
case STATE_SEND_NODEINFO:
|
||||||
if (!nodeInfoForPhone)
|
if (!nodeInfoForPhone)
|
||||||
nodeInfoForPhone = nodeDB.readNextInfo();
|
nodeInfoForPhone = nodeDB.readNextInfo();
|
||||||
@ -365,6 +377,12 @@ bool PhoneAPI::available()
|
|||||||
if (hasPacket)
|
if (hasPacket)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!xmodemPacketForPhone)
|
||||||
|
xmodemPacketForPhone = xModem.getForPhone();
|
||||||
|
hasPacket = !!packetForPhone;
|
||||||
|
if (hasPacket)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (!packetForPhone)
|
if (!packetForPhone)
|
||||||
packetForPhone = service.getForPhone();
|
packetForPhone = service.getForPhone();
|
||||||
hasPacket = !!packetForPhone;
|
hasPacket = !!packetForPhone;
|
||||||
|
@ -42,6 +42,9 @@ class PhoneAPI : public Observer<uint32_t> // FIXME, we shouldn't be inheriting
|
|||||||
/// downloads it
|
/// downloads it
|
||||||
MeshPacket *packetForPhone = NULL;
|
MeshPacket *packetForPhone = NULL;
|
||||||
|
|
||||||
|
// file transfer packets destined for phone. Push it to the queue then free it.
|
||||||
|
XModem *xmodemPacketForPhone = NULL;
|
||||||
|
|
||||||
// Keep QueueStatus packet just as packetForPhone
|
// Keep QueueStatus packet just as packetForPhone
|
||||||
QueueStatus *queueStatusPacketForPhone = NULL;
|
QueueStatus *queueStatusPacketForPhone = NULL;
|
||||||
|
|
||||||
|
@ -700,6 +700,7 @@ typedef struct _ToRadio {
|
|||||||
This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link.
|
This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link.
|
||||||
(Sending this message is optional for clients) */
|
(Sending this message is optional for clients) */
|
||||||
bool disconnect;
|
bool disconnect;
|
||||||
|
/* File Transfer Chunk */
|
||||||
XModem xmodemPacket;
|
XModem xmodemPacket;
|
||||||
};
|
};
|
||||||
} ToRadio;
|
} ToRadio;
|
||||||
|
219
src/xmodem.cpp
Normal file
219
src/xmodem.cpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
/***********************************************************************************************************************
|
||||||
|
* based on XMODEM implementation by Georges Menie (www.menie.org)
|
||||||
|
***********************************************************************************************************************
|
||||||
|
* Copyright 2001-2019 Georges Menie (www.menie.org)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Adapted for protobuf encapsulation. this is not really Xmodem any more.
|
||||||
|
*
|
||||||
|
* 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 "xmodem.h"
|
||||||
|
|
||||||
|
XModemAdapter xModem;
|
||||||
|
|
||||||
|
XModemAdapter::XModemAdapter()
|
||||||
|
{
|
||||||
|
xmodemStore = (XModem*)malloc(XModem_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short XModemAdapter::crc16_ccitt(const pb_byte_t *buffer, int length)
|
||||||
|
{
|
||||||
|
unsigned short crc16 = 0;
|
||||||
|
while(length != 0) {
|
||||||
|
crc16 = (unsigned char)(crc16 >> 8) | (crc16 << 8);
|
||||||
|
crc16 ^= *buffer;
|
||||||
|
crc16 ^= (unsigned char)(crc16 & 0xff) >> 4;
|
||||||
|
crc16 ^= (crc16 << 8) << 4;
|
||||||
|
crc16 ^= ((crc16 & 0xff) << 4) << 1;
|
||||||
|
buffer++;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc16;
|
||||||
|
}
|
||||||
|
|
||||||
|
int XModemAdapter::check(const pb_byte_t *buf, int sz, unsigned short tcrc)
|
||||||
|
{
|
||||||
|
unsigned short crc = crc16_ccitt(buf, sz);
|
||||||
|
if (crc == tcrc)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XModemAdapter::sendControl(XModem_Control c) {
|
||||||
|
memset(xmodemStore, 0, XModem_size);
|
||||||
|
xmodemStore->control = c;
|
||||||
|
LOG_DEBUG("XModem: Notify Sending control %d.\n", c);
|
||||||
|
packetReady.notifyObservers(packetno);
|
||||||
|
}
|
||||||
|
|
||||||
|
XModem *XModemAdapter::getForPhone()
|
||||||
|
{
|
||||||
|
if(xmodemStore) {
|
||||||
|
return xmodemStore;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XModemAdapter::handlePacket(XModem xmodemPacket)
|
||||||
|
{
|
||||||
|
switch(xmodemPacket.control) {
|
||||||
|
case XModem_Control_SOH:
|
||||||
|
case XModem_Control_STX:
|
||||||
|
if ((xmodemPacket.seq == 0) && !isReceiving && !isTransmitting) {
|
||||||
|
// NULL packet has the destination filename
|
||||||
|
memcpy(filename, &xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
|
||||||
|
if(xmodemPacket.control == XModem_Control_SOH) { // Receive this file and put to Flash
|
||||||
|
file = FSCom.open(filename, FILE_O_WRITE);
|
||||||
|
if(file) {
|
||||||
|
sendControl(XModem_Control_ACK);
|
||||||
|
isReceiving = true;
|
||||||
|
packetno = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendControl(XModem_Control_NAK);
|
||||||
|
isReceiving = false;
|
||||||
|
break;
|
||||||
|
} else { // Transmit this file from Flash
|
||||||
|
LOG_INFO("XModem: Transmitting file %s\n", filename);
|
||||||
|
file = FSCom.open(filename, FILE_O_READ);
|
||||||
|
if (file) {
|
||||||
|
packetno = 1;
|
||||||
|
isTransmitting = true;
|
||||||
|
memset(xmodemStore, 0, XModem_size);
|
||||||
|
xmodemStore->control = XModem_Control_SOH;
|
||||||
|
xmodemStore->seq = packetno;
|
||||||
|
xmodemStore->buffer.size = file.read(xmodemStore->buffer.bytes, sizeof(XModem_buffer_t::bytes));
|
||||||
|
xmodemStore->crc16 = crc16_ccitt(xmodemStore->buffer.bytes, xmodemStore->buffer.size);
|
||||||
|
LOG_DEBUG("XModem: STX Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore->buffer.size);
|
||||||
|
if (xmodemStore->buffer.size < sizeof(XModem_buffer_t::bytes)) {
|
||||||
|
isEOT = true;
|
||||||
|
// send EOT on next Ack
|
||||||
|
}
|
||||||
|
packetReady.notifyObservers(packetno);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sendControl(XModem_Control_NAK);
|
||||||
|
isTransmitting = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isReceiving) {
|
||||||
|
// normal file data packet
|
||||||
|
if ((xmodemPacket.seq == packetno) && check(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size, xmodemPacket.crc16)) {
|
||||||
|
// valid packet
|
||||||
|
file.write(xmodemPacket.buffer.bytes, xmodemPacket.buffer.size);
|
||||||
|
sendControl(XModem_Control_ACK);
|
||||||
|
packetno++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// invalid packet
|
||||||
|
sendControl(XModem_Control_NAK);
|
||||||
|
break;
|
||||||
|
} else if (isTransmitting) {
|
||||||
|
// just received something weird.
|
||||||
|
sendControl(XModem_Control_CAN);
|
||||||
|
isTransmitting = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XModem_Control_EOT:
|
||||||
|
// End of transmission
|
||||||
|
sendControl(XModem_Control_ACK);
|
||||||
|
file.close();
|
||||||
|
isReceiving = false;
|
||||||
|
break;
|
||||||
|
case XModem_Control_CAN:
|
||||||
|
// Cancel transmission and remove file
|
||||||
|
sendControl(XModem_Control_ACK);
|
||||||
|
file.close();
|
||||||
|
FSCom.remove(filename);
|
||||||
|
isReceiving = false;
|
||||||
|
break;
|
||||||
|
case XModem_Control_ACK:
|
||||||
|
// Acknowledge Send the next packet
|
||||||
|
if (isTransmitting) {
|
||||||
|
if (isEOT) {
|
||||||
|
sendControl(XModem_Control_EOT);
|
||||||
|
file.close();
|
||||||
|
LOG_INFO("XModem: Finished sending file %s\n", filename);
|
||||||
|
isTransmitting = false;
|
||||||
|
isEOT = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
retrans = MAXRETRANS; // reset retransmit counter
|
||||||
|
packetno++;
|
||||||
|
memset(xmodemStore, 0, XModem_size);
|
||||||
|
xmodemStore->control = XModem_Control_SOH;
|
||||||
|
xmodemStore->seq = packetno;
|
||||||
|
xmodemStore->buffer.size = file.read(xmodemStore->buffer.bytes, sizeof(XModem_buffer_t::bytes));
|
||||||
|
xmodemStore->crc16 = crc16_ccitt(xmodemStore->buffer.bytes, xmodemStore->buffer.size);
|
||||||
|
LOG_DEBUG("XModem: ACK Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore->buffer.size);
|
||||||
|
if (xmodemStore->buffer.size < sizeof(XModem_buffer_t::bytes)) {
|
||||||
|
isEOT = true;
|
||||||
|
// send EOT on next Ack
|
||||||
|
}
|
||||||
|
packetReady.notifyObservers(packetno);
|
||||||
|
} else {
|
||||||
|
// just received something weird.
|
||||||
|
sendControl(XModem_Control_CAN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XModem_Control_NAK:
|
||||||
|
// Negative acknowledge. Send the same buffer again
|
||||||
|
if (isTransmitting) {
|
||||||
|
if (--retrans <= 0) {
|
||||||
|
sendControl(XModem_Control_CAN);
|
||||||
|
file.close();
|
||||||
|
LOG_INFO("XModem: Retransmit timeout, cancelling file %s\n", filename);
|
||||||
|
isTransmitting = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memset(xmodemStore, 0, XModem_size);
|
||||||
|
xmodemStore->control = XModem_Control_SOH;
|
||||||
|
xmodemStore->seq = packetno;
|
||||||
|
file.seek((packetno-1) * sizeof(XModem_buffer_t::bytes));
|
||||||
|
xmodemStore->buffer.size = file.read(xmodemStore->buffer.bytes, sizeof(XModem_buffer_t::bytes));
|
||||||
|
xmodemStore->crc16 = crc16_ccitt(xmodemStore->buffer.bytes, xmodemStore->buffer.size);
|
||||||
|
LOG_DEBUG("XModem: NAK Notify Sending packet %d, %d Bytes.\n", packetno, xmodemStore->buffer.size);
|
||||||
|
if (xmodemStore->buffer.size < sizeof(XModem_buffer_t::bytes)) {
|
||||||
|
isEOT = true;
|
||||||
|
// send EOT on next Ack
|
||||||
|
}
|
||||||
|
packetReady.notifyObservers(packetno);
|
||||||
|
} else {
|
||||||
|
// just received something weird.
|
||||||
|
sendControl(XModem_Control_CAN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unknown control character
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
76
src/xmodem.h
Normal file
76
src/xmodem.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/***********************************************************************************************************************
|
||||||
|
* based on XMODEM implementation by Georges Menie (www.menie.org)
|
||||||
|
***********************************************************************************************************************
|
||||||
|
* Copyright 2001-2019 Georges Menie (www.menie.org)
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Adapted for protobuf encapsulation. this is not really Xmodem any more.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
**********************************************************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
#include "mesh/generated/xmodem.pb.h"
|
||||||
|
#include "FSCommon.h"
|
||||||
|
|
||||||
|
#define MAXRETRANS 25
|
||||||
|
|
||||||
|
class XModemAdapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Called when we put a fragment in the outgoing memory
|
||||||
|
Observable<uint32_t> packetReady;
|
||||||
|
|
||||||
|
XModemAdapter();
|
||||||
|
|
||||||
|
void handlePacket(XModem xmodemPacket);
|
||||||
|
XModem *getForPhone();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isReceiving = false;
|
||||||
|
bool isTransmitting = false;
|
||||||
|
bool isEOT = false;
|
||||||
|
|
||||||
|
int retrans = MAXRETRANS;
|
||||||
|
|
||||||
|
uint16_t packetno = 0;
|
||||||
|
|
||||||
|
#ifdef ARCH_NRF52
|
||||||
|
File file = File(FSCom);
|
||||||
|
#else
|
||||||
|
File file;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char filename[sizeof(XModem_buffer_t::bytes)] = {0};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
XModem *xmodemStore = NULL;
|
||||||
|
unsigned short crc16_ccitt(const pb_byte_t *buffer, int length);
|
||||||
|
int check(const pb_byte_t *buf, int sz, unsigned short tcrc);
|
||||||
|
void sendControl(XModem_Control c);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern XModemAdapter xModem;
|
Loading…
Reference in New Issue
Block a user