104 lines
3.7 KiB
C++
104 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
namespace mesh {
|
|
namespace peripherals {
|
|
|
|
// RAK4631 only: append this checked policy after every previously published
|
|
// preference byte. Missing historical policy means GPS-only; an unreadable or
|
|
// damaged new policy must retain the shared supply instead of removing power.
|
|
static constexpr size_t FooterSize = 8;
|
|
static constexpr size_t CommonLegacySize = 877;
|
|
static constexpr size_t LegacyRs232TailSize = 10;
|
|
|
|
inline uint16_t checksum(const uint8_t* bytes, size_t length) {
|
|
uint16_t crc = 0xFFFF;
|
|
for (size_t i = 0; i < length; ++i) {
|
|
crc ^= static_cast<uint16_t>(bytes[i]) << 8;
|
|
for (unsigned bit = 0; bit < 8; ++bit)
|
|
crc = (crc & 0x8000) ? static_cast<uint16_t>((crc << 1) ^ 0x1021)
|
|
: static_cast<uint16_t>(crc << 1);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
inline void encodeFooter(uint8_t out[FooterSize], uint8_t shared) {
|
|
out[0] = 'P'; out[1] = 'M'; out[2] = 'O'; out[3] = 1;
|
|
out[4] = shared == 0 ? 0 : 1;
|
|
out[5] = 0;
|
|
const uint16_t crc = checksum(out, 6);
|
|
out[6] = static_cast<uint8_t>(crc >> 8);
|
|
out[7] = static_cast<uint8_t>(crc);
|
|
}
|
|
|
|
inline bool decodeFooter(const uint8_t* in, size_t size, uint8_t& shared) {
|
|
if (!in || size != FooterSize || in[0] != 'P' || in[1] != 'M'
|
|
|| in[2] != 'O' || in[3] != 1 || in[4] > 1 || in[5] != 0
|
|
|| checksum(in, 6) != static_cast<uint16_t>((in[6] << 8) | in[7]))
|
|
return false;
|
|
shared = in[4];
|
|
return true;
|
|
}
|
|
|
|
template <typename File> uint8_t readCommonMode(File& file) {
|
|
const size_t size = file.size();
|
|
if (size <= CommonLegacySize) return 0;
|
|
// A historical RS232-only image has no peripheral policy. This check is
|
|
// independent of the current profile: UART -> no-UART upgrades must work.
|
|
if (size == CommonLegacySize + LegacyRs232TailSize) {
|
|
uint8_t header[4] = {};
|
|
if (!file.seek(CommonLegacySize) || file.read(header, sizeof(header)) != sizeof(header))
|
|
return 1;
|
|
if (header[0] == 'R' && header[1] == 'S' && header[2] == 'P' && header[3] == 1)
|
|
return 0;
|
|
}
|
|
uint8_t footer[FooterSize] = {};
|
|
uint8_t shared = 1;
|
|
if (size < FooterSize || !file.seek(size - FooterSize)
|
|
|| file.read(footer, sizeof(footer)) != sizeof(footer)
|
|
|| !decodeFooter(footer, sizeof(footer), shared)) return 1;
|
|
return shared;
|
|
}
|
|
|
|
template <typename Save>
|
|
bool handleCommand(uint8_t& shared, const char* command, char* reply,
|
|
size_t capacity, bool supported, Save save) {
|
|
if (!command || !reply || capacity == 0) return false;
|
|
const bool query = strcmp(command, "get peripherals.mode") == 0;
|
|
static const char prefix[] = "set peripherals.mode";
|
|
const bool setter = strncmp(command, prefix, sizeof(prefix) - 1) == 0
|
|
&& (command[sizeof(prefix) - 1] == 0 || command[sizeof(prefix) - 1] == ' '
|
|
|| command[sizeof(prefix) - 1] == '\t');
|
|
if (!query && !setter) return false;
|
|
if (!supported) {
|
|
snprintf(reply, capacity, "Error: peripheral mode unsupported");
|
|
return true;
|
|
}
|
|
if (query) {
|
|
snprintf(reply, capacity, "> %s (saved; applied at boot)", shared ? "shared" : "gps-only");
|
|
return true;
|
|
}
|
|
const char* value = command + sizeof(prefix) - 1;
|
|
while (*value == ' ' || *value == '\t') ++value;
|
|
if (strcmp(value, "gps-only") != 0 && strcmp(value, "shared") != 0) {
|
|
snprintf(reply, capacity, "Error: use set peripherals.mode gps-only|shared");
|
|
return true;
|
|
}
|
|
const uint8_t previous = shared;
|
|
shared = strcmp(value, "shared") == 0 ? 1 : 0;
|
|
if (!save()) {
|
|
shared = previous;
|
|
snprintf(reply, capacity, "Error: save unconfirmed; reboot may differ");
|
|
} else {
|
|
snprintf(reply, capacity, "OK - %s saved; reboot to apply", value);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace peripherals
|
|
} // namespace mesh
|