From 0e7797f3bbcf06b86aa097e11d565670b04b8d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 8 Jan 2023 18:15:51 +0100 Subject: [PATCH] Adding to generated for now, protobuf PR will come when it works ... :-) --- src/mesh/PhoneAPI.cpp | 18 ++- src/mesh/PhoneAPI.h | 3 + src/mesh/generated/mesh.pb.h | 14 ++- src/mesh/generated/xmodem.pb.c | 13 +++ src/mesh/generated/xmodem.pb.h | 77 +++++++++++++ src/xmodem.cpp | 201 +++++++++++++++++++++++++++++++++ src/xmodem.h | 71 ++++++++++++ 7 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 src/mesh/generated/xmodem.pb.c create mode 100644 src/mesh/generated/xmodem.pb.h create mode 100644 src/xmodem.cpp create mode 100644 src/xmodem.h diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index c1d56b7ec..90c340cce 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -6,6 +6,7 @@ #include "PowerFSM.h" #include "RadioInterface.h" #include "configuration.h" +#include "xmodem.h" #include #if FromRadio_size > MAX_TO_FROM_RADIO_SIZE @@ -91,6 +92,10 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) LOG_INFO("Disconnecting from phone\n"); close(); break; + case ToRadio_xmodemPacket_tag: + LOG_INFO("Got xmodem packet\n"); + xModem.handlePacket(toRadioScratch.xmodemPacket); + break; default: // Ignore nop messages // LOG_DEBUG("Error: unexpected ToRadio variant\n"); @@ -284,10 +289,14 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Do we have a message from the mesh? LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n"); if (queueStatusPacketForPhone) { - fromRadioScratch.which_payload_variant = FromRadio_queueStatus_tag; fromRadioScratch.queueStatus = *queueStatusPacketForPhone; releaseQueueStatusPhonePacket(); + } else if (xmodemPacketForPhone) { + fromRadioScratch.which_payload_variant = FromRadio_xmodemPacket_tag; + fromRadioScratch.xmodemPacket = *xmodemPacketForPhone; + free(xmodemPacketForPhone); + xmodemPacketForPhone = NULL; } else if (packetForPhone) { printPacket("phone downloaded packet", packetForPhone); @@ -350,6 +359,7 @@ bool PhoneAPI::available() case STATE_SEND_MODULECONFIG: case STATE_SEND_COMPLETE_ID: return true; + case STATE_SEND_NODEINFO: if (!nodeInfoForPhone) nodeInfoForPhone = nodeDB.readNextInfo(); @@ -362,6 +372,12 @@ bool PhoneAPI::available() if (hasPacket) return true; + if (!xmodemPacketForPhone) + xmodemPacketForPhone = xModem.getForPhone(); + hasPacket = !!packetForPhone; + if (hasPacket) + return true; + if (!packetForPhone) packetForPhone = service.getForPhone(); hasPacket = !!packetForPhone; diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h index 2f2695807..8e28efcbf 100644 --- a/src/mesh/PhoneAPI.h +++ b/src/mesh/PhoneAPI.h @@ -42,6 +42,9 @@ class PhoneAPI : public Observer // FIXME, we shouldn't be inheriting /// downloads it 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 QueueStatus *queueStatusPacketForPhone = NULL; diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index d1f3e5bf5..064b9cdcb 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -9,6 +9,7 @@ #include "module_config.pb.h" #include "portnums.pb.h" #include "telemetry.pb.h" +#include "xmodem.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -670,6 +671,8 @@ typedef struct _FromRadio { Channel channel; /* Queue status info */ QueueStatus queueStatus; + /* File Transfer Chunk */ + XModem xmodemPacket; }; } FromRadio; @@ -693,6 +696,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. (Sending this message is optional for clients) */ bool disconnect; + XModem xmodemPacket; }; } ToRadio; @@ -904,9 +908,11 @@ extern "C" { #define FromRadio_moduleConfig_tag 9 #define FromRadio_channel_tag 10 #define FromRadio_queueStatus_tag 11 +#define FromRadio_xmodemPacket_tag 12 #define ToRadio_packet_tag 1 #define ToRadio_want_config_id_tag 3 #define ToRadio_disconnect_tag 4 +#define ToRadio_xmodemPacket_tag 5 #define Compressed_portnum_tag 1 #define Compressed_data_tag 2 @@ -1062,7 +1068,8 @@ X(a, STATIC, ONEOF, UINT32, (payload_variant,config_complete_id,config_co X(a, STATIC, ONEOF, BOOL, (payload_variant,rebooted,rebooted), 8) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,moduleConfig,moduleConfig), 9) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \ -X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) #define FromRadio_CALLBACK NULL #define FromRadio_DEFAULT NULL #define FromRadio_payload_variant_packet_MSGTYPE MeshPacket @@ -1073,14 +1080,17 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 1 #define FromRadio_payload_variant_moduleConfig_MSGTYPE ModuleConfig #define FromRadio_payload_variant_channel_MSGTYPE Channel #define FromRadio_payload_variant_queueStatus_MSGTYPE QueueStatus +#define FromRadio_payload_variant_xmodemPacket_MSGTYPE XModem #define ToRadio_FIELDLIST(X, a) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \ X(a, STATIC, ONEOF, UINT32, (payload_variant,want_config_id,want_config_id), 3) \ -X(a, STATIC, ONEOF, BOOL, (payload_variant,disconnect,disconnect), 4) +X(a, STATIC, ONEOF, BOOL, (payload_variant,disconnect,disconnect), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 5) #define ToRadio_CALLBACK NULL #define ToRadio_DEFAULT NULL #define ToRadio_payload_variant_packet_MSGTYPE MeshPacket +#define ToRadio_payload_variant_xmodemPacket_MSGTYPE XModem #define Compressed_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, portnum, 1) \ diff --git a/src/mesh/generated/xmodem.pb.c b/src/mesh/generated/xmodem.pb.c new file mode 100644 index 000000000..f210380fe --- /dev/null +++ b/src/mesh/generated/xmodem.pb.c @@ -0,0 +1,13 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.7 */ + +#include "xmodem.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(XModem, XModem, AUTO) + + + + diff --git a/src/mesh/generated/xmodem.pb.h b/src/mesh/generated/xmodem.pb.h new file mode 100644 index 000000000..e5fd33234 --- /dev/null +++ b/src/mesh/generated/xmodem.pb.h @@ -0,0 +1,77 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.7 */ + +#ifndef PB_XMODEM_PB_H_INCLUDED +#define PB_XMODEM_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _XModem_Control { + XModem_Control_NUL = 0, + XModem_Control_SOH = 1, + XModem_Control_STX = 2, + XModem_Control_EOT = 4, + XModem_Control_ACK = 6, + XModem_Control_NAK = 21, + XModem_Control_CAN = 24, + XModem_Control_CTRLZ = 26 +} XModem_Control; + +/* Struct definitions */ +typedef PB_BYTES_ARRAY_T(128) XModem_buffer_t; +typedef struct _XModem { + XModem_Control control; + uint8_t seq; + uint16_t crc16; + XModem_buffer_t buffer; +} XModem; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _XModem_Control_MIN XModem_Control_NUL +#define _XModem_Control_MAX XModem_Control_CTRLZ +#define _XModem_Control_ARRAYSIZE ((XModem_Control)(XModem_Control_CTRLZ+1)) + +#define XModem_control_ENUMTYPE XModem_Control + + +/* Initializer values for message structs */ +#define XModem_init_default {_XModem_Control_MIN, 0, 0, {0, {0}}} +#define XModem_init_zero {_XModem_Control_MIN, 0, 0, {0, {0}}} + +/* Field tags (for use in manual encoding/decoding) */ +#define XModem_control_tag 1 +#define XModem_seq_tag 2 +#define XModem_crc16_tag 3 +#define XModem_buffer_tag 4 + +/* Struct field encoding specification for nanopb */ +#define XModem_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, control, 1) \ +X(a, STATIC, SINGULAR, UINT32, seq, 2) \ +X(a, STATIC, SINGULAR, UINT32, crc16, 3) \ +X(a, STATIC, SINGULAR, BYTES, buffer, 4) +#define XModem_CALLBACK NULL +#define XModem_DEFAULT NULL + +extern const pb_msgdesc_t XModem_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define XModem_fields &XModem_msg + +/* Maximum encoded size of messages (where known) */ +#define XModem_size 140 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/xmodem.cpp b/src/xmodem.cpp new file mode 100644 index 000000000..bbbf751e1 --- /dev/null +++ b/src/xmodem.cpp @@ -0,0 +1,201 @@ +/*********************************************************************************************************************** + * XMODEM implementation with YMODEM support + *********************************************************************************************************************** + * Copyright 2001-2019 Georges Menie (www.menie.org) + * Modified by Thuffir in 2019 + * 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 "xmodem.h" + +XModemAdapter xModem; + +XModemAdapter::XModemAdapter() +{ +} + +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; +} + +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 + 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); + 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(); + 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); + if (xmodemStore->buffer.size < sizeof(XModem_buffer_t::bytes)) { + isEOT = true; + // send EOT on next Ack + } + } 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(); + 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); + if (xmodemStore->buffer.size < sizeof(XModem_buffer_t::bytes)) { + isEOT = true; + // send EOT on next Ack + } + } else { + // just received something weird. + sendControl(XModem_Control_CAN); + } + break; + default: + // Unknown control character + break; + } +} diff --git a/src/xmodem.h b/src/xmodem.h new file mode 100644 index 000000000..819ab970b --- /dev/null +++ b/src/xmodem.h @@ -0,0 +1,71 @@ +/*********************************************************************************************************************** + * XMODEM implementation with YMODEM support + *********************************************************************************************************************** + * Copyright 2001-2019 Georges Menie (www.menie.org) + * Modified by Thuffir in 2019 + * 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. + **********************************************************************************************************************/ + +#pragma once + +#include "configuration.h" +#include "mesh/generated/xmodem.pb.h" +#include "FSCommon.h" + +#define MAXRETRANS 25 + +class XModemAdapter +{ + public: + XModemAdapter(); + + void handlePacket(XModem xmodemPacket); + XModem *getForPhone(); + + private: + bool isReceiving = false; + bool isTransmitting = false; + bool isEOT = false; + + int retrans = MAXRETRANS; + + unsigned char 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;