#include "FolderMotaStore.h" #include "MotaSeederProto.h" #include "OtaByteIO.h" #include namespace mesh { namespace ota { bool FolderMotaStore::readByteT(uint8_t& b, uint32_t started) const { while ((uint32_t)(millis() - started) < _to) { int c = _io.read(); if (c >= 0) { if ((uint32_t)(millis() - started) >= _to) return false; b = (uint8_t)c; return true; } delay(1); // let BLE/WiFi callbacks deliver the response } return false; } bool FolderMotaStore::readExact(uint8_t* b, uint16_t n, uint32_t started) const { for (uint16_t i = 0; i < n; i++) if (!readByteT(b[i], started)) return false; return true; } // Resync-safe request/response (same framing as SerialMotaSource): drain stale input, frame op+args with an // XOR checksum, scan for the response magic, validate op+status+checksum, deliver payload. bool FolderMotaStore::txn(uint8_t op, const uint8_t* args, uint16_t arglen, uint8_t* payload, uint16_t payload_len) const { // Snapshot the currently queued stale bytes. A continuously sending TCP // client must not trap the mesh loop before a request has even been sent. int pending = _io.available(); while (pending-- > 0) if (_io.read() < 0) break; uint8_t xs = op; for (uint16_t i = 0; i < arglen; i++) xs ^= args[i]; // Keep one contiguous write for TCP (and fewer calls on UART). The largest // request is OP_WRITE's fixed header plus one bounded data chunk. if (arglen > 10 + MOTA_SEEDER_WRITE_MAX) return false; uint8_t frame[4 + 10 + MOTA_SEEDER_WRITE_MAX]; uint16_t frame_len = 0; frame[frame_len++] = MOTA_SEEDER_REQ_MAGIC0; frame[frame_len++] = MOTA_SEEDER_REQ_MAGIC1; frame[frame_len++] = op; if (arglen) { memcpy(frame + frame_len, args, arglen); frame_len += arglen; } frame[frame_len++] = xs; if (_io.write(frame, frame_len) != frame_len) return false; if (_write_policy == MotaStreamWritePolicy::FlushTransmit) _io.flush(); uint32_t t0 = millis(); bool got = false; uint8_t prev = 0; while ((millis() - t0) < _to) { // scan for response magic 'm' 's' (tolerate noise) int c = _io.read(); if (c < 0) { delay(1); continue; } if (prev == MOTA_SEEDER_RSP_MAGIC0 && (uint8_t)c == MOTA_SEEDER_RSP_MAGIC1) { got = true; break; } prev = (uint8_t)c; } if (!got) return false; uint8_t hdr[2]; if (!readExact(hdr, 2, t0)) return false; // op, status if (hdr[0] != op) return false; uint8_t rxs = (uint8_t)(MOTA_SEEDER_RSP_MAGIC0 ^ MOTA_SEEDER_RSP_MAGIC1) ^ hdr[0] ^ hdr[1]; bool ok = (hdr[1] == MS_STATUS_OK); if (ok && payload_len) { if (!readExact(payload, payload_len, t0)) return false; for (uint16_t i = 0; i < payload_len; i++) rxs ^= payload[i]; } uint8_t xsum; if (!readByteT(xsum, t0)) return false; if (xsum != rxs) return false; // corrupt frame -> caller retries return ok; } bool FolderMotaStore::begin(uint32_t total_size) { uint8_t args[8]; memcpy(args, _mid, 4); wr_u32le(args + 4, total_size); if (!txn(MS_OP_BEGIN, args, 8, nullptr, 0)) return false; _total = total_size; return true; } bool FolderMotaStore::write(uint32_t off, const uint8_t* data, uint32_t len) { uint8_t req[10 + MOTA_SEEDER_WRITE_MAX]; memcpy(req, _mid, 4); uint32_t done = 0; while (done < len) { uint16_t chunk = (len - done > MOTA_SEEDER_WRITE_MAX) ? MOTA_SEEDER_WRITE_MAX : (uint16_t)(len - done); wr_u32le(req + 4, off + done); req[8] = (uint8_t)(chunk & 0xFF); req[9] = (uint8_t)(chunk >> 8); memcpy(req + 10, data + done, chunk); if (!txn(MS_OP_WRITE, req, (uint16_t)(10 + chunk), nullptr, 0)) return false; done += chunk; } return true; } bool FolderMotaStore::read(uint32_t off, uint8_t* buf, uint32_t len) const { uint8_t args[10]; memcpy(args, _mid, 4); uint32_t done = 0; while (done < len) { uint16_t chunk = (len - done > MOTA_SEEDER_WRITE_MAX) ? MOTA_SEEDER_WRITE_MAX : (uint16_t)(len - done); wr_u32le(args + 4, off + done); args[8] = (uint8_t)(chunk & 0xFF); args[9] = (uint8_t)(chunk >> 8); if (!txn(MS_OP_SREAD, args, 10, buf + done, chunk)) return false; done += chunk; } return true; } bool FolderMotaStore::finalize() { return txn(MS_OP_FIN, _mid, 4, nullptr, 0); // publish .mota.part as .mota } bool FolderMotaStore::reopen() { _reopen_io_error = false; uint8_t pl[5]; if (!txn(MS_OP_STAT, _mid, 4, pl, 5)) { _reopen_io_error = true; return false; } if (pl[0] == 0) return false; // host has no partial/complete file -> start fresh uint32_t total = rd_u32le(pl + 1); if (total < 13) return false; // implausible container _total = total; return true; } } // namespace ota } // namespace mesh