#pragma once #include #include #include #include #include "sha256.h" namespace roro { // One file sent over the USB serial console (scripts/sd_put.py), to put an Update File on the SD // card without taking the card out. The sender writes `sd put `, then the raw // bytes, one chunk at a time, and waits for each chunk to be written before sending the next: the // serial driver drops bytes once its receive buffer is full. The file lands as `.part` and is // renamed to `` only once every byte has arrived and the checksum matches. class FileReceiver { public: static constexpr size_t kChunk = 1024; // must stay below the serial receive buffer static constexpr uint32_t kTimeoutMs = 5000; // silence, or a card job that never completes static constexpr uint32_t kMaxBytes = 8u << 20; // larger than any app partition enum class State { Idle, Receiving, // waiting for bytes of the current chunk Writing, // chunk() is complete: write it to partPath(), then call chunkWritten() Finishing, // everything written and checked: rename partPath() to path(), then finished() Done, Failed, // error() says why; partPath() should be removed }; // Parses " ". Returns "" when the transfer starts, or what's wrong. std::string begin(const std::string& args, uint32_t nowMs); // Takes bytes for the current chunk; returns how many were used (none while a chunk waits). size_t feed(const uint8_t* data, size_t len, uint32_t nowMs); void chunkWritten(bool ok, uint32_t nowMs); // While Finishing: the SHA-256 of the file as read back from the card. The checksum on the // received bytes doesn't prove the card kept them (a failed write can lose buffered data). void cardChecked(const uint8_t digest[32]); void finished(bool ok); void tick(uint32_t nowMs); void reset() { *this = FileReceiver(); } State state() const { return state_; } bool active() const { return state_ != State::Idle; } // Bytes still missing from the current chunk: read no more than this from the serial port. size_t wanted() const; const std::vector& chunk() const { return chunk_; } const std::string& path() const { return path_; } std::string partPath() const { return path_ + ".part"; } uint32_t size() const { return size_; } uint32_t received() const { return received_; } const std::string& error() const { return error_; } private: void fail(const char* why); State state_ = State::Idle; std::string path_; uint32_t size_ = 0; uint32_t received_ = 0; // bytes in chunks already written uint8_t expected_[32] = {}; Sha256 sha_; std::vector chunk_; uint32_t lastActivityMs_ = 0; std::string error_; }; } // namespace roro