Public Access
The card "refused" a write about once in 2,000 multi-block writes: three 1.7 MB uploads in ten. Measured with a driver that records where it gives up: every time, all blocks were accepted, and the status check after Stop Tran came back as 0xFF or 0x1F. The driver tests for ready with the first byte after selecting the card, which reads 0xFF before the card has signalled busy, so CMD13 went out mid-programming. A dummy byte first, as in ChaN's reference driver, and one after Stop Tran. 30 uploads in a row since, each read back by SHA-256, ten with the radio listening: no fault. 10 MHz made no difference; the card stays at 20 MHz. `info` shows the driver's write faults; `put` prints the step and the card's answer when one happens. ADR 0007. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
96 lines
3.7 KiB
C++
96 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/semphr.h>
|
|
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "cleanup_plan.h"
|
|
#include "service.h"
|
|
#include "storage_monitor.h"
|
|
|
|
namespace roro {
|
|
|
|
// Owns the microSD card. All card access happens on this Service's own task (the SD driver isn't
|
|
// safe across tasks, and a failed mount blocks for seconds): it mounts and watches usage, writes
|
|
// queued Log lines in batches, lists files for Storage Clean-up, deletes, and formats.
|
|
class StorageService : public Service {
|
|
public:
|
|
// The lock exists from the start, so a Service that's never started (Safe Mode) can still be asked.
|
|
explicit StorageService(EventBus& bus) : monitor_(bus), bus_(bus), lock_(xSemaphoreCreateMutex()) {}
|
|
const char* name() const override { return "storage"; }
|
|
void start() override;
|
|
void stop() override;
|
|
|
|
StorageState state() const;
|
|
|
|
// Queues one line for a Log file (created with its folders as needed). Dropped while Logs are
|
|
// paused (over 90 % full, or no card) or if too much is already waiting. A Capture (or a Track,
|
|
// something the user started) keeps going past 90 %, until the card is full.
|
|
void appendLine(const std::string& path, const std::string& line, bool capture = false);
|
|
// The same, for binary files (a pcap Capture): the bytes as they are, no newline.
|
|
void appendBytes(const std::string& path, const std::string& bytes, bool capture = false);
|
|
uint32_t droppedLines() const { return dropped_; }
|
|
|
|
// Storage Clean-up: list every file of every category, then delete a selection.
|
|
void requestListing();
|
|
bool listingReady() const;
|
|
std::vector<std::vector<StoredFile>> listing() const; // one vector per kCleanupCategories entry
|
|
void requestDelete(std::vector<std::string> paths);
|
|
|
|
// Runs `job` on the storage task, where card access is safe (e.g. reading an Update File).
|
|
void runJob(std::function<void()> job);
|
|
// The same, and waits for it (from another task, never the storage task itself). False if it
|
|
// never started within 5 s: no card mounted, and then it never will run.
|
|
bool runAndWait(std::function<void()> job);
|
|
|
|
// Erases the whole card: one partition spanning the card, formatted FAT32.
|
|
bool requestFormat();
|
|
bool formatting() const { return formatRequested_; }
|
|
|
|
private:
|
|
static constexpr uint32_t kWakeMs = 1000;
|
|
static constexpr uint32_t kPollEvery = 15; // wake-ups between usage checks
|
|
static constexpr size_t kMaxPendingBytes = 16 * 1024;
|
|
static constexpr uint32_t kSdHz = 20000000; // the card's SPI clock; 10 MHz changed nothing (ADR 0007)
|
|
|
|
static void taskEntry(void* self);
|
|
void loop();
|
|
void poll();
|
|
bool mount();
|
|
void format();
|
|
void writePending();
|
|
void list();
|
|
void remove(const std::vector<std::string>& paths);
|
|
void lock() const { xSemaphoreTake(lock_, portMAX_DELAY); }
|
|
void unlock() const { xSemaphoreGive(lock_); }
|
|
|
|
StorageMonitor monitor_;
|
|
EventBus& bus_;
|
|
bool mounted_ = false;
|
|
TaskHandle_t task_ = nullptr;
|
|
mutable SemaphoreHandle_t lock_ = nullptr;
|
|
|
|
// Shared with other tasks, guarded by lock_.
|
|
struct Pending {
|
|
std::string path, data;
|
|
bool newline;
|
|
};
|
|
std::deque<Pending> pending_;
|
|
void append(const std::string& path, const std::string& data, bool capture, bool newline);
|
|
size_t pendingBytes_ = 0;
|
|
uint32_t dropped_ = 0;
|
|
bool listingRequested_ = false;
|
|
bool listingReady_ = false;
|
|
std::vector<std::vector<StoredFile>> listing_;
|
|
std::vector<std::string> toDelete_;
|
|
std::vector<std::function<void()>> jobs_;
|
|
volatile bool formatRequested_ = false;
|
|
};
|
|
|
|
} // namespace roro
|