librnode/src/librnode.c
2025-08-02 18:05:28 +01:00

1818 lines
52 KiB
C
Executable File

/* Copyright (c) 2025 - Jacob Eva (Liberated Embedded Systems)
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <md5.h>
#include <openssl/sha.h>
#include <libgen.h>
#include <time.h>
#include "librnode.h"
#include "libs/framing.h"
#include "libs/serial.h"
#include "libs/logging/log.h"
#include "libs/util.h"
#include "libs/slip_enc.h"
#include "libs/eeprom.h"
#include "libs/zip.h"
// flashing
#include "libs/flashers/nrf/dfu.h"
#include "libs/flashers/nrf/uart_drv.h"
#include "libs/flashers/esp32/esputil.h"
/* Handles incoming RNode communications from serial
* Scope: private
* Returns:
* > 0 - number of bytes read from port
* -1 - generic error
*/
int rnode_handle_resp(struct RNode* rn, bool* in_frame, uint8_t* cmd, uint8_t* cmd_buf, uint8_t* cmd_buf_l, uint16_t* frame_len) {
int len;
bool escape = false;
uint8_t sbyte;
uint8_t resp_buf[RESP_BUF_SIZE] = {0};
len = read_port(rn->fd, resp_buf, RESP_BUF_SIZE);
if (len > 0) {
log_trace("Read %d bytes", len);
for (int i = 0; i < len; i++) {
log_trace("Byte: %02x", resp_buf[i]);
if (!(*in_frame) && resp_buf[i] == FEND) {
*in_frame = true;
*cmd = CMD_UNKNOWN;
sbyte = 0;
*cmd_buf_l = 0;
} else if (*in_frame) {
if (*frame_len == 0) {
*cmd = resp_buf[i];
(*frame_len)++;
continue;
} else {
sbyte = resp_buf[i];
}
if (sbyte == FEND && *cmd == CMD_ROM_READ) {
//rn->product = cmd_buf[0];
//rn->model = cmd_buf[1];
//rn->hw_rev = cmd_buf[2];
//memcpy(rn->serial, cmd_buf+3, SERIAL_SIZE);
//memcpy(rn->made, cmd_buf+3+SERIAL_SIZE, MADE_SIZE);
//memcpy(rn->checksum, cmd_buf+3+SERIAL_SIZE+MADE_SIZE, CHECKSUM_SIZE);
//memcpy(rn->signature, cmd_buf+3+SERIAL_SIZE+MADE_SIZE, CHECKSUM_SIZE);
memcpy(rn->r_eeprom, cmd_buf, EEPROM_SIZE);
*in_frame = false;
*frame_len = 0;
*cmd = CMD_UNKNOWN;
continue;
} else if (sbyte == FEND) {
*in_frame = false;
*frame_len = 0;
*cmd = CMD_UNKNOWN;
*cmd_buf_l = 0;
continue;
} else if (*cmd == CMD_ROM_READ) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
}
} else if (*cmd == CMD_DETECT && sbyte == DETECT_RESP) {
rn->connected = true;
} else if (*cmd == CMD_PLATFORM) {
rn->platform = sbyte;
} else if (*cmd == CMD_MCU) {
rn->mcu = sbyte;
} else if (*cmd == CMD_FW_VERSION) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
if (*cmd_buf_l == 2) {
rn->fw_ver = cmd_buf[0] + cmd_buf[1] / 100.0;
*cmd_buf_l = 0;
}
}
} else if (*cmd == CMD_INTERFACES) {
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
if (*cmd_buf_l == 2) {
rn->interfaces[cmd_buf[0]] = cmd_buf[1];
*cmd_buf_l = 0;
}
} else if (*cmd == CMD_FREQUENCY) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
if (*cmd_buf_l == 4) {
rn->freq = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3];
*cmd_buf_l = 0;
}
}
} else if (*cmd == CMD_BANDWIDTH) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
if (*cmd_buf_l == 4) {
rn->bw = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3];
*cmd_buf_l = 0;
}
}
} else if (*cmd == CMD_TXPOWER) {
rn->txp = sbyte;
} else if (*cmd == CMD_SF) {
rn->sf = sbyte;
} else if (*cmd == CMD_CR) {
rn->cr = sbyte;
} else if (*cmd == CMD_ST_ALOCK) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
}
if (*cmd_buf_l == 2) {
float at = cmd_buf[0] << 8 | cmd_buf[1];
rn->st_alock = at / 100.0;
*cmd_buf_l = 0;
}
} else if (*cmd == CMD_LT_ALOCK) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
}
if (*cmd_buf_l == 2) {
float at = cmd_buf[0] << 8 | cmd_buf[1];
rn->lt_alock = at / 100.0;
*cmd_buf_l = 0;
}
} else if (*cmd == CMD_RADIO_STATE) {
rn->int_state[rn->sel_int] = sbyte;
} else if (*cmd == CMD_ROM_READ) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
}
} else if (*cmd == CMD_BT_PIN) {
if (sbyte == FESC) {
escape = true;
} else {
if (escape) {
if (sbyte == TFEND) {
sbyte = FEND;
} else if (sbyte == TFESC) {
sbyte = FESC;
}
escape = false;
}
cmd_buf[*cmd_buf_l] = sbyte;
(*cmd_buf_l)++;
}
if (*cmd_buf_l == 4) {
rn->bt_pairing_pin = cmd_buf[0] << 24 | cmd_buf[1] << 16 | cmd_buf[2] << 8 | cmd_buf[3];
*cmd_buf_l = 0;
}
}
(*frame_len)++;
}
}
}
return len;
}
/* Detects an RNode
* Scope: private
* Returns:
* 0 - success
* -1 - generic error
*/
int rnode_detect(struct RNode* rn) {
int err_code;
uint8_t tx_buf[16];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[16]){FEND, CMD_DETECT, DETECT_REQ, FEND, FEND, CMD_FW_VERSION, 0x00, FEND, FEND, CMD_PLATFORM, 0x00, FEND, FEND, CMD_MCU, 0x00, FEND}, 16*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 16);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
return (err_code == -1) ? -1 : rn->connected;
} else {
return -1;
}
}
/* Establishes communication with an RNode
* Params: port (e.g. /dev/ttyACM0), baud rate (e.g. 115200), detect (attempt
* to communicate with RNode), force_detect (fail if cannot communicate with
* RNode)
* Scope: public
* Returns:
* 0 - success
* -1 - generic error
*/
int rnode_init(struct RNode* rn, char* port, uint32_t baud, bool detect, bool force_detect) {
rn->port = port;
rn->baud = baud;
rn->fd = open_port(rn->port, baud);
if (rn->fd > 0) {
if (detect) {
if (rnode_detect(rn)) {
log_info("RNode detected successfully on port %s", port);
} else {
if (force_detect) {
log_error("RNode could not be detected on port %s", port);
return -1;
} else {
log_info("RNode could not be detected on port %s, this isn't a problem.", port);
}
}
} else {
return 0;
}
} else {
return -1;
}
return 0;
}
/* Resets an RNode
* Scope: public
* Returns:
* 0 - success
* -1 - generic error
*/
int rnode_reset(struct RNode* rn) {
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_RESET, CMD_RESET_BYTE, FEND}, 4*sizeof(uint8_t));
return write_port(rn->fd, tx_buf, 4);
}
/* Sets an RNode's platform attribute manually
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_platform(struct RNode* rn, uint8_t platform) {
rn->platform = platform;
return 0;
}
/* Enable / disable Bluetooth on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_bt(struct RNode* rn, uint8_t val) {
int err_code;
if (val == BT_ON) {
log_info("Enabling Bluetooth...");
} else if (val == BT_PAIRING) {
log_info("Starting Bluetooth pairing...");
} else {
log_info("Disabling Bluetooth...");
}
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_BT_CTRL, val, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Attempt to retrieve the BT pairing pin of an RNode. Blocking function,
* should be run in a separate thread.
* Params: timeout (in ms, minimum value 200)
* Scope: public
* Returns
* > 0 - bluetooth pairing code
* 0 - success
* -1 - generic error
* -4 - rnode did not respond
*/
int rnode_get_bt_pin(struct RNode* rn, uint32_t timeout) {
int err_code = 0;
time_t ltime;
time(&ltime);
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
unsigned int start = ltime; unsigned int current = ltime;
if (timeout < 200) {
return -1;
}
while (current <= start + timeout) {
if (err_code >= 0) {
sleep_ms(50);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
time(&ltime); current = ltime;
if (rn->bt_pairing_pin != 0) {
// If we successfully retrieved the pairing pin, return it
return rn->bt_pairing_pin;
}
} else {
return err_code;
}
}
return -4;
}
/* Display related functions */
/* Set display intensity on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_disp_int(struct RNode* rn, uint8_t disp_int) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_INT, disp_int, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Set display timeout on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_disp_timeout(struct RNode* rn, uint8_t timeout) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_INT, timeout, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Set display address on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_disp_addr(struct RNode* rn, uint8_t addr) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_ADDR, addr, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Set display rotation on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_disp_rot(struct RNode* rn, uint8_t rot) {
int err_code = 0;
if (rot < 0) rot = 0;
else if (rot > 3) rot = 3;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_ROT, rot, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Start display reconditioning on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_start_disp_recon(struct RNode* rn) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DISP_RCND, 1, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Set neopixel intensity on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_np_int(struct RNode* rn, uint8_t np_int) {
int err_code;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_NP_INT, np_int, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Get available interfaces on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_interfaces(struct RNode* rn) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_INTERFACES, 0x00, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
}
if (err_code > 0) {
return 0;
} else {
return err_code;
}
}
/* Radio configuration functions */
/* Select interface on an RNode (in preparation for running rnode_set_freq, etc)
* Scope: public
* Returns
* 0 - success
* -1 - generic error
* -13 - chosen interface does not exist (did you run rnode_get_interfaces)?
*/
int rnode_select_interface(struct RNode* rn, uint8_t interface) {
int err_code;
uint8_t tx_buf[4];
if (rn->interfaces[interface] != 0) {
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SEL_INT, interface, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
} else {
return err_code;
}
if (!err_code) {
rn->sel_int = interface;
}
return err_code;
}
/* Set frequency on an RNode (in preparation for TNC mode)
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_freq(struct RNode* rn, uint32_t freq) {
uint8_t tx_buf[11];
uint8_t escaped_freq[8];
uint32_t escaped_freq_size;
uint8_t freq_a[4];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(freq_a, (uint8_t[4]){freq >> 24, freq >> 16 & 0xFF, freq >> 8 & 0xFF, freq & 0xFF}, 4*sizeof(uint8_t));
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_FREQUENCY}, 2*sizeof(uint8_t));
encode_slip(escaped_freq, &escaped_freq_size, freq_a, 4, false);
memcpy(tx_buf + 2, escaped_freq, escaped_freq_size*sizeof(uint8_t));
tx_buf[2 + escaped_freq_size] = FEND;
err_code = write_port(rn->fd, tx_buf, 2 + escaped_freq_size + 1);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->freq;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get frequency on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_freq(struct RNode* rn) {
uint8_t tx_buf[7];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[7]){FEND, CMD_FREQUENCY, 0, 0, 0, 0, FEND}, 7*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 7);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->freq;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set bandwidth on an RNode (in preparation for TNC mode)
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_bw(struct RNode* rn, uint32_t bw) {
int err_code;
uint8_t tx_buf[11];
uint8_t escaped_bw[8];
uint32_t escaped_bw_size;
uint8_t bw_a[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(bw_a, (uint8_t[4]){bw >> 24, bw >> 16 & 0xFF, bw >> 8 & 0xFF, bw & 0xFF}, 4*sizeof(uint8_t));
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_BANDWIDTH}, 2*sizeof(uint8_t));
encode_slip(escaped_bw, &escaped_bw_size, bw_a, 4, false);
memcpy(tx_buf + 2, escaped_bw, escaped_bw_size*sizeof(uint8_t));
tx_buf[2 + escaped_bw_size] = FEND;
err_code = write_port(rn->fd, tx_buf, 2 + escaped_bw_size + 1);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->bw;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get bandwidth on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_bw(struct RNode* rn) {
uint8_t tx_buf[7];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[7]){FEND, CMD_BANDWIDTH, 0, 0, 0, 0, FEND}, 7*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 7);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->bw;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set transmission power on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_txp(struct RNode* rn, uint8_t txp) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_TXPOWER, txp, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return txp == rn->txp ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get transmission power on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_txp(struct RNode* rn) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_TXPOWER, 0xFF, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->txp;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set spreading factor on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_sf(struct RNode* rn, uint8_t sf) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SF, sf, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return sf == rn->sf ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get spreading factor on an RNode
* Scope: public
* Returns
* >=5 - spreading factor
* 0 - success
* -1 - generic error
*/
int rnode_get_sf(struct RNode* rn) {
uint8_t tx_buf[11];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_SF, 0xFF, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (!err_code) {
return rn->sf;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set coding rate on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_cr(struct RNode* rn, uint8_t cr) {
uint8_t tx_buf[4];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CR, cr, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return cr == rn->cr ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get coding rate on an RNode
* Scope: public
* Returns
* >=5 - coding rate
* 0 - success
* -1 - generic error
*/
int rnode_get_cr(struct RNode* rn) {
uint8_t tx_buf[4];
int err_code;
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CR, 0xFF, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return rn->cr;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set short term airtime limit on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
float rnode_set_st_alock(struct RNode* rn, float at_l) {
int err_code;
int at = at_l * 100;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_ST_ALOCK}, 2*sizeof(uint8_t));
memcpy(tx_buf + 2, (uint8_t[2]){(at >> 8 & 0xFF), (at & 0xFF)}, 1*sizeof(uint8_t));
tx_buf[2 + 1] = FEND;
err_code = write_port(rn->fd, tx_buf, 2 + 1 + 1);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return at_l == rn->st_alock ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Set long term airtime limit on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
float rnode_set_lt_alock(struct RNode* rn, float at_l) {
int err_code;
int at = at_l * 100;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_LT_ALOCK}, 2*sizeof(uint8_t));
memcpy(tx_buf + 2, (uint8_t[2]){(at >> 8 & 0xFF), (at & 0xFF)}, 1*sizeof(uint8_t));
tx_buf[2 + 1] = FEND;
err_code = write_port(rn->fd, tx_buf, 2 + 1 + 1);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return at_l == rn->lt_alock ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Start or stop selected interface on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_interface_state(struct RNode* rn, bool state) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[10] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_RADIO_STATE, state, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
if (err_code > 0) {
return state == rn->int_state[rn->sel_int] ? 0 : -1;
} else {
return err_code;
}
} else {
return err_code;
}
}
/* Get selected interface state on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_interface_state(struct RNode* rn) {
return rn->int_state[rn->sel_int];
}
/* Mode selection functions */
/* Enable the host-controlled (normal) mode of operation on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_normal_mode(struct RNode* rn) {
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CONF_DELETE, 0x00, FEND}, 4*sizeof(uint8_t));
return write_port(rn->fd, tx_buf, 4);
}
/* Enable the serial TNC mode of operation on an RNode
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_tnc_mode(struct RNode* rn) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_CONF_SAVE, 0x00, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* Misc functions */
int rnode_set_int_avoid(struct RNode* rn, bool avoid) {
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_DIS_IA, !avoid, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
return err_code;
}
/* EEPROM related functions */
/* Sets the firmware hash on an RNode.
* Scope: public
* Returns:
* 0 - success
* -1 - generic error
*/
int rnode_set_fw_hash(struct RNode* rn, uint8_t* hash) {
uint8_t tx_buf[2 + (FW_HASH_SIZE * 2) + 1];
uint8_t escaped_hash[FW_HASH_SIZE*2];
uint32_t escaped_hash_size;
unsigned char hash_str[FW_HASH_SIZE * 2 + 1] = "";
log_debug("Setting firmware hash...");
for (int i = 0; i < FW_HASH_SIZE; i++) {
sprintf(hash_str+i*2, "%02x", hash[i]);
}
log_trace("Firmware hash is %s", hash_str);
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_FW_HASH}, 2*sizeof(uint8_t));
encode_slip(escaped_hash, &escaped_hash_size, hash, FW_HASH_SIZE, false);
memcpy(tx_buf + 2, escaped_hash, escaped_hash_size*sizeof(uint8_t));
tx_buf[2 + escaped_hash_size] = FEND;
return write_port(rn->fd, tx_buf, 2 + escaped_hash_size + 1);
}
/* Write to an arbitrary address in an RNode's EEPROM
* Scope: private
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_write_eeprom(struct RNode* rn, uint8_t address, uint8_t value) {
int err_code = 0;
uint8_t tx_buf[3 + 2*2];
uint8_t data[2];
uint8_t escaped_data[2*2];
uint32_t escaped_data_size;
data[0] = address;
data[1] = value;
memcpy(tx_buf, (uint8_t[2]){FEND, CMD_ROM_WRITE}, 2*sizeof(uint8_t));
encode_slip(escaped_data, &escaped_data_size, data, 2, false);
memcpy(tx_buf+2, escaped_data, escaped_data_size*sizeof(uint8_t));
tx_buf[2 + escaped_data_size] = FEND;
err_code = write_port(rn->fd, tx_buf, 2 + escaped_data_size + 1);
if (!err_code) {
sleep_ms(6);
}
return err_code;
}
/* Sets an RNode's product in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_product(struct RNode* rn, uint8_t product) {
rn->product = product;
return rnode_write_eeprom(rn, ADDR_PRODUCT, product);
}
/* Sets an RNode's model in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_model(struct RNode* rn, uint8_t model) {
rn->model = model;
return rnode_write_eeprom(rn, ADDR_MODEL, model);
}
/* Sets an RNode's hardware revision in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_hw_rev(struct RNode* rn, uint8_t hw_rev) {
rn->hw_rev = hw_rev;
return rnode_write_eeprom(rn, ADDR_HW_REV, hw_rev);
}
/* Sets an RNode's serial in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_serial(struct RNode* rn, uint8_t* serial) {
int err_code = 0;
for (int i = 0; i < 4; i++) {
err_code = rnode_write_eeprom(rn, ADDR_SERIAL+i, serial[i]);
rn->serial[i] = serial[i];
if (err_code) {
break;
}
}
return err_code;
}
/* Sets an RNode's manufacture time in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_made_time(struct RNode* rn, uint32_t time) {
int err_code = 0;
uint8_t data[4];
data[3] = time & 0x000000FF;
data[2] = time >> 8 & 0x000000FF;
data[1] = time >> 16 & 0x000000FF;
data[0] = time >> 24 & 0x000000FF;
for (int i = 0; i < 4; i++) {
err_code = rnode_write_eeprom(rn, ADDR_MADE+i, data[i]);
rn->made[i] = data[i];
if (err_code) {
break;
}
}
return err_code;
}
/* Calculates an RNode's checksum for its EEPROM. Must ONLY be called AFTER
* rnode_set_product, rnode_set_model, rnode_set_hw_rev, rnode_set_serial and
* rnode_set_made_time are all called. OR alternatively, after rnode_get_eeprom.
* Scope: public
* Returns
* 0 - success
* -1 - generic error
* -3 - provided array was too small
*/
int rnode_calculate_checksum(struct RNode* rn, uint8_t* checksum) {
uint8_t data[11];
int temp_val;
unsigned char checksum_s[CHECKSUM_SIZE * 2];
data[0] = rn->product;
data[1] = rn->model;
data[2] = rn->hw_rev;
data[3] = rn->serial[0];
data[4] = rn->serial[1];
data[5] = rn->serial[2];
data[6] = rn->serial[3];
data[7] = rn->made[0];
data[8] = rn->made[1];
data[9] = rn->made[2];
data[10] = rn->made[3];
MD5Data(data, 11, checksum_s);
for (int i = 0; i < CHECKSUM_SIZE && sscanf(checksum_s + i * 2, "%2x", &temp_val); i++) {
checksum[i] = temp_val;
}
return 0;
}
/* Sets an RNode's checksum in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_checksum(struct RNode* rn, uint8_t* checksum) {
int err_code = 0;
for (int i = 0; i < CHECKSUM_SIZE; i++) {
err_code = rnode_write_eeprom(rn, ADDR_CHKSUM+i, checksum[i]);
if (err_code) {
break;
}
}
memcpy(rn->checksum, checksum, CHECKSUM_SIZE);
return err_code;
}
/* Generate's an RNode's EEPROM signature from the checksum
* THE RETURNED POINTER MUST BE OPENSSL_free()'D!
* Scope: public
* Returns
* 0 - success
* -1 - generic error
* -3 - malloc failure
*/
void* rnode_generate_signature(struct RNode* rn, EVP_PKEY *signing_key) {
EVP_PKEY_CTX *ctx;
size_t hash_len = 256 / 8, sig_len;
uint8_t hash[256 / 8];
void* signature;
/*
* NB: assumes signing_key and md are set up before the next
* step. signing_key must be an RSA private key and md must
* point to the SHA-256 digest to be signed.
*/
ctx = EVP_PKEY_CTX_new(signing_key, NULL /* no engine */);
if (ctx == NULL) return NULL;
if (EVP_PKEY_sign_init(ctx) <= 0) return NULL;
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) return NULL;
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0) return NULL;
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_MAX) <= 0) return NULL;
if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) <= 0) return NULL;
SHA256(rn->checksum, CHECKSUM_SIZE, hash);
/* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, &sig_len, hash, hash_len) <= 0) return NULL;
// todo need to free signature
signature = OPENSSL_malloc(sig_len);
if (signature == NULL) return NULL;
if (EVP_PKEY_sign(ctx, signature, &sig_len, hash, hash_len) <= 0) return NULL;
/* Signature is siglen bytes written to buffer sig */
EVP_PKEY_CTX_free(ctx);
return signature;
}
/* Sets an RNode's signature in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_signature(struct RNode* rn, uint8_t* data) {
int err_code = 0;
for (int i = 0; i < SIGNATURE_SIZE; i++) {
err_code = rnode_write_eeprom(rn, ADDR_SIGNATURE+i, data[i]);
if (err_code) {
break;
}
}
return err_code;
}
/* Sets an RNode's lock byte in its EEPROM
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_set_lock(struct RNode* rn) {
return rnode_write_eeprom(rn, ADDR_INFO_LOCK, INFO_LOCK_BYTE);
}
/* Retrieve an RNode's EEPROM values into its struct
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_get_eeprom(struct RNode* rn) {
int err_code = rnode_dump_eeprom(rn);
// Populate remote EEPROM values
memcpy(&rn->product, rn->r_eeprom, 1);
memcpy(&rn->model, rn->r_eeprom+1, 1);
memcpy(&rn->hw_rev, rn->r_eeprom+2, 1);
memcpy(&rn->serial, rn->r_eeprom+3, 4);
memcpy(&rn->made, rn->r_eeprom+7, 4);
memcpy(&rn->checksum, rn->r_eeprom+11, 16);
memcpy(&rn->signature, rn->r_eeprom+27, 128);
memcpy(&rn->lock_byte, rn->r_eeprom+155, 1);
memcpy(&rn->sf, rn->r_eeprom+156, 1);
memcpy(&rn->cr, rn->r_eeprom+157, 1);
memcpy(&rn->txp, rn->r_eeprom+158, 1);
memcpy(&rn->bw, rn->r_eeprom+159, 4);
memcpy(&rn->freq, rn->r_eeprom+163, 4);
memcpy(&rn->cfg_ok, rn->r_eeprom+167, 4);
memcpy(&rn->bt, rn->r_eeprom+168, 1);
memcpy(&rn->disp_set, rn->r_eeprom+169, 1);
memcpy(&rn->disp_int, rn->r_eeprom+170, 1);
memcpy(&rn->disp_addr, rn->r_eeprom+171, 1);
return err_code;
}
/* Dump an RNode's entire EEPROM to r_eeprom
* Note: This will populate r_eeprom but not the cached EEPROM values. If you
* want to update the cached values, call rnode_get_eeprom instead.
* Scope: public
* Returns
* 0 - success
* -1 - generic error
*/
int rnode_dump_eeprom(struct RNode* rn) {
int err_code;
uint8_t tx_buf[4];
bool in_frame = false;
uint8_t cmd = CMD_UNKNOWN;
uint8_t cmd_buf[256] = {0};
uint8_t cmd_buf_l = 0;
uint16_t frame_len = 0;
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_ROM_READ, 0x00, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
if (!err_code) {
sleep_ms(100);
err_code = rnode_handle_resp(rn, &in_frame, &cmd, cmd_buf, &cmd_buf_l, &frame_len);
}
return err_code > 0 ? 0 : -1;
}
/* Verify an RNode's EEPROM matches the expected values
* Scope: public
* Returns
* 0 - success
* value > 0 or value < 0 - EEPROM invalid
*/
int rnode_verify_eeprom(struct RNode* rn) {
int err_code;
err_code = rnode_dump_eeprom(rn);
if (!err_code) {
err_code = memcmp(&rn->product, rn->r_eeprom, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->model, rn->r_eeprom+1, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->hw_rev, rn->r_eeprom+2, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->serial, rn->r_eeprom+3, sizeof(4));
}
if (!err_code) {
err_code = memcmp(&rn->made, rn->r_eeprom+7, sizeof(4));
}
if (!err_code) {
err_code = memcmp(&rn->checksum, rn->r_eeprom+11, sizeof(16));
}
if (!err_code) {
err_code = memcmp(&rn->signature, rn->r_eeprom+27, sizeof(128));
}
if (!err_code) {
err_code = memcmp(&rn->lock_byte, rn->r_eeprom+155, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->sf, rn->r_eeprom+156, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->cr, rn->r_eeprom+157, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->txp, rn->r_eeprom+158, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->bw, rn->r_eeprom+159, sizeof(4));
}
if (!err_code) {
err_code = memcmp(&rn->freq, rn->r_eeprom+163, sizeof(4));
}
if (!err_code) {
err_code = memcmp(&rn->cfg_ok, rn->r_eeprom+167, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->bt, rn->r_eeprom+168, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->disp_set, rn->r_eeprom+169, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->disp_int, rn->r_eeprom+170, sizeof(1));
}
if (!err_code) {
err_code = memcmp(&rn->disp_addr, rn->r_eeprom+171, sizeof(1));
}
return err_code;
}
/* Wipes an RNode's EEPROM.
* Scope: public
* Returns:
* 0 - success
* -1 - generic error
* -2 - rnode not supported (firmware too old, please update)
*/
int rnode_wipe_eeprom(struct RNode* rn) {
log_info("Erasing EEPROM, please switch off your device IMMEDIATELY if you did not intend to do this!");
int err_code = 0;
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_UNLOCK_ROM, 0xF8, FEND}, 4*sizeof(uint8_t));
if (rn->fw_ver != 0 && rn->fw_ver < MIN_FW_VER) {
log_trace("RNode firmware version too low!");
return -2;
} else {
err_code = write_port(rn->fd, tx_buf, 4);
}
if (err_code) {
return err_code;
}
err_code = close_port(rn->fd);
if (!err_code) {
sleep_ms(13000);
if (rn->platform == PLATFORM_NRF52) {
// Due to the current janky emulated EEPROM implementation for the
// RAK4631, extra time must be given to allow for writing.
sleep_ms(10000);
}
} else {
return err_code;
}
rn->fd = open_port(rn->port, rn->baud);
return (err_code == 0) ? 0 : -1;
}
int rnode_flash_progress_cb(struct RNode* rn, void (*ptr)(uint8_t)) {
rn->prog_cb = ptr;
}
/* Flashes an RNode.
* Scope: public
* Returns:
* 0 - success
* -1 - generic error
* -9 - ESP32 image invalid, SHA256 digest incorrect
*/
int rnode_flash(struct RNode* rn, char* zip_path, bool update, uint8_t* serial, EVP_PKEY* priv_key, bool touch) {
int err_code = 0;
uint8_t hash[FW_HASH_SIZE];
if (update) {
// show update logo on RNode display
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_FW_UPD, 0x01, FEND}, 4*sizeof(uint8_t));
err_code = write_port(rn->fd, tx_buf, 4);
}
// Close port to prepare for flashing
if (!err_code) {
err_code = close_port(rn->fd);
}
if (!err_code) {
switch (rn->platform) {
case PLATFORM_ESP32:
uint8_t img_hash[32];
struct zip_t *zip_pkg;
char* bin_ext = ".bin";
char* bootloader_ext = ".bootloader";
char* boot_app0_ext = ".boot_app0";
char* partitions_ext = ".partitions";
char* console_image_name = "console_image.bin";
char *bin, *bootloader, *boot_app0, *partitions, *console_image = 0, *bin_path, *bootloader_path, *boot_app0_path, *partitions_path, *console_image_path, *filename, *filename_no_ext, *dir, *zip_path_cpy;
size_t bin_size, bootloader_size, boot_app0_size, partitions_size, console_image_size = 0, bin_path_size, bootloader_path_size, boot_app0_path_size, partitions_path_size, console_image_path_size, filename_no_ext_size;
zip_pkg = zip_open(zip_path, 0, 'r');
if (zip_pkg == NULL)
{
log_error("Cannot open ZIP firmware package file!");
return -1;
}
else
{
zip_path_cpy = strdup(zip_path);
filename = basename(zip_path_cpy);
for (int i = 0; i < strlen(filename); i++) {
if (filename[i] == '.') {
filename_no_ext_size = i;
break;
}
}
filename_no_ext = malloc(filename_no_ext_size+2);
memcpy(filename_no_ext, filename, filename_no_ext_size);
filename_no_ext[filename_no_ext_size+1] = '\0';
// Retrieve bin file
bin_path_size = filename_no_ext_size+strlen(bin_ext);
bin_path = malloc(bin_path_size+1);
strcpy(bin_path, filename_no_ext);
strcat(bin_path, bin_ext);
if (zip_entry_open(zip_pkg, bin_path)) {
return -1;
} else {
bin = malloc(FILE_MAX_SIZE);
if (zip_entry_read(zip_pkg, (void**)&bin, &bin_size)) {
return -1;
} else {
if (zip_entry_close(zip_pkg)) {
return -1;
}
}
}
// Retrieve bootloader
bootloader_path_size = filename_no_ext_size+strlen(bootloader_ext);
bootloader_path = malloc(bootloader_path_size+1);
strcpy(bootloader_path, filename_no_ext);
strcat(bootloader_path, bootloader_ext);
if (zip_entry_open(zip_pkg, bootloader_path)) {
return -1;
} else {
bootloader = malloc(FILE_MAX_SIZE);
if (zip_entry_read(zip_pkg, (void**)&bootloader, &bootloader_size)) {
return -1;
} else {
if (zip_entry_close(zip_pkg)) {
return -1;
}
}
}
// Retrieve boot_app0
boot_app0_path_size = filename_no_ext_size+strlen(boot_app0_ext);
boot_app0_path = malloc(boot_app0_path_size+1);
strcpy(boot_app0_path, filename_no_ext);
strcat(boot_app0_path, boot_app0_ext);
if (zip_entry_open(zip_pkg, boot_app0_path)) {
return -1;
} else {
boot_app0 = malloc(FILE_MAX_SIZE);
if (zip_entry_read(zip_pkg, (void**)&boot_app0, &boot_app0_size)) {
return -1;
} else {
if (zip_entry_close(zip_pkg)) {
return -1;
}
}
}
// Retrieve partitions
partitions_path_size = filename_no_ext_size+strlen(partitions_ext);
partitions_path = malloc(partitions_path_size+1);
strcpy(partitions_path, filename_no_ext);
strcat(partitions_path, partitions_ext);
if (zip_entry_open(zip_pkg, partitions_path)) {
return -1;
} else {
partitions = malloc(FILE_MAX_SIZE);
if (zip_entry_read(zip_pkg, (void**)&partitions, &partitions_size)) {
return -1;
} else {
if (zip_entry_close(zip_pkg)) {
return -1;
}
}
}
// Retrieve console image
if (zip_entry_open(zip_pkg, console_image_name)) {
// No console image on this build, skipping
} else {
console_image = malloc(FILE_MAX_SIZE);
if (zip_entry_read(zip_pkg, (void**)&console_image, &console_image_size)) {
return -1;
} else {
if (zip_entry_close(zip_pkg)) {
return -1;
}
}
}
// Check firmware hash
memcpy(img_hash, (bin + bin_size) - 32, 32);
SHA256(bin, bin_size-32, hash);
if (memcmp(hash, img_hash, 32*sizeof(uint8_t) != 0)) {
return -9;
}
flash_full(rn, boot_app0, boot_app0_size, bootloader, bootloader_size, bin, bin_size, partitions, partitions_size, console_image, console_image_size);
free(console_image);
free(partitions);
free(partitions_path);
free(boot_app0);
free(boot_app0_path);
free(bootloader);
free(bootloader_path);
free(bin);
free(bin_path);
free(filename_no_ext);
free(zip_path_cpy);
}
break;
case PLATFORM_NRF52:
{
// No space for console image on flash on this target currently
dfu_param_t dfu_param;
uart_drv_t uart_drv;
uart_drv.p_PortName = rn->port;
err_code = uart_slip_open(&uart_drv, touch);
if (!err_code) {
dfu_param.p_uart = &uart_drv;
dfu_param.p_pkg_file = zip_path;
err_code = dfu_send_package(&dfu_param, rn, hash);
} else {
return -1;
}
err_code = uart_slip_close(&uart_drv);
}
}
}
// Wait for reset and boot. This value may have to be adjusted in the
// future if firmware becomes more complex.
sleep_ms(15000);
rn->fd = open_port(rn->port, rn->baud);
if (rn->fd > 0) {
if (!update) {
uint8_t checksum[CHECKSUM_SIZE];
void* signature;
log_info("Bootstrapping RNode EEPROM...");
// Provision EEPROM
err_code = rnode_set_product(rn, rn->product);
if (!err_code) {
err_code = rnode_set_model(rn, rn->model);
}
if (!err_code) {
err_code = rnode_set_hw_rev(rn, rn->hw_rev);
}
if (!err_code) {
err_code = rnode_set_serial(rn, serial);
}
if (!err_code) {
time_t current = time(NULL);
err_code = rnode_set_made_time(rn, current);
}
if (!err_code) {
err_code = rnode_calculate_checksum(rn, checksum);
}
if (!err_code) {
err_code = rnode_set_checksum(rn, checksum);
}
if (!err_code) {
signature = rnode_generate_signature(rn, priv_key);
}
if (signature != NULL) {
err_code = rnode_set_signature(rn, signature);
}
if (!err_code) {
OPENSSL_free(signature);
err_code = rnode_set_lock(rn);
}
}
} else {
err_code = -1;
}
if (!err_code) {
sleep_ms(750);
err_code = rnode_set_fw_hash(rn, hash);
} else {
return err_code;
}
if (!err_code) {
log_info("Waiting 10 seconds for RNode to come online...");
sleep_ms(10000);
if (rn->platform == PLATFORM_NRF52) {
err_code = close_port(rn->fd);
// Due to the current janky emulated EEPROM implementation for the
// RAK4631, extra time must be given to allow for writing.
log_info("Waiting an extra 15 seconds for RNode to come online...");
sleep_ms(15000);
} else if (rn->platform == PLATFORM_ESP32) {
rnode_reset(rn);
err_code = close_port(rn->fd);
log_info("Waiting for ESP32 reset...");
sleep_ms(7000);
}
err_code = rnode_init(rn, rn->port, rn->baud, true, true);
} else {
return err_code;
}
if (!err_code) {
err_code = rnode_verify_eeprom(rn);
}
return err_code;
}
int rnode_disconnect(struct RNode* rn) {
uint8_t tx_buf[4];
memcpy(tx_buf, (uint8_t[4]){FEND, CMD_LEAVE, 0xFF, FEND}, 4*sizeof(uint8_t));
return write_port(rn->fd, tx_buf, 4);
}
int rnode_cleanup(struct RNode* rn) {
close_port(rn->fd);
}