mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 10:21:59 +00:00
fix(l2): no remount while the card is a USB drive, vector packs locked across cores, time zone list, noise sweep restore
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -280,7 +280,9 @@ static void noiseSweep(Sweep& sw, const NodePrefs* p) {
|
||||
sw.v[i] = (int16_t)lroundf(v[K / 2]);
|
||||
}
|
||||
radio.standby();
|
||||
if (p) radio_driver.setParams(p->freq, p->bw, p->sf, p->cr);
|
||||
// Back to what the mesh uses: the repeater's own profile while relaying on
|
||||
// one, not always the chat settings.
|
||||
if (p) the_mesh.applyRepeaterRadio();
|
||||
radio.startReceive();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -564,7 +564,8 @@ void UITask::buildHomeClock(lv_obj_t* box) {
|
||||
lv_obj_set_style_pad_top(row, 12, 0);
|
||||
char opts[200];
|
||||
int o = 0;
|
||||
for (int k = 0; k < telemetry::COUNT; k++) o += snprintf(opts + o, sizeof(opts) - o, "%s%s", k ? "\n" : "", telemetry::NAME[k]);
|
||||
for (int k = 0; k < telemetry::COUNT && o < (int)sizeof(opts) - 24; k++) // 137 bytes today; the list only grows
|
||||
o += snprintf(opts + o, sizeof(opts) - o, "%s%s", k ? "\n" : "", telemetry::NAME[k]);
|
||||
for (int i = 0; i < FIELDS; i++) {
|
||||
uint8_t f = _prefs ? _prefs->dashboard_fields[i] : telemetry::NONE;
|
||||
if (f >= telemetry::COUNT) f = telemetry::NONE;
|
||||
|
||||
@@ -287,7 +287,12 @@ static void backlightFadeIn() { display.fadeIn(); }
|
||||
// is picked up.
|
||||
static bool s_sd_mounted = false;
|
||||
static uint32_t s_sd_last_try = 0;
|
||||
// Lent to a computer as a USB drive (usbDriveStart()): its driver owns the
|
||||
// card's host. Mounting it again here as well -- the map, the history and
|
||||
// Settings > About all ask -- would put two drivers on one card.
|
||||
static bool s_sd_lent = false;
|
||||
static bool mountStorage() {
|
||||
if (s_sd_lent) return false;
|
||||
if (s_sd_mounted) return true;
|
||||
if (s_sd_last_try && millis() - s_sd_last_try < 5000) return false;
|
||||
s_sd_last_try = millis() | 1;
|
||||
@@ -373,6 +378,7 @@ static bool usbDriveStart() {
|
||||
if (s_usb_drive) return true;
|
||||
SD_MMC.end();
|
||||
s_sd_mounted = false;
|
||||
s_sd_lent = true;
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.flags = SDMMC_HOST_FLAG_1BIT;
|
||||
sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
@@ -386,7 +392,7 @@ static bool usbDriveStart() {
|
||||
sdmmc_host_deinit();
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) { s_sd_last_try = 0; mountStorage(); return false; }
|
||||
if (!ok) { s_sd_lent = false; s_sd_last_try = 0; mountStorage(); return false; }
|
||||
s_msc.vendorID("MeshCore");
|
||||
s_msc.productID("Wio L2 SD card");
|
||||
s_msc.productRevision("1.0");
|
||||
@@ -408,6 +414,7 @@ static void usbDriveStop() {
|
||||
delay(100);
|
||||
sdmmc_host_deinit();
|
||||
s_usb_drive = false;
|
||||
s_sd_lent = false; // the UI restarts next (restartScreen): the card is mounted afresh
|
||||
}
|
||||
#else
|
||||
static bool usbHost() { return false; }
|
||||
|
||||
@@ -83,10 +83,9 @@ void UITask::mapRegionAction(uint8_t act) {
|
||||
}
|
||||
case A_DELETE: {
|
||||
if (!tapConfirmed(s_del_lbl, "Delete?")) return;
|
||||
char path[80];
|
||||
snprintf(path, sizeof(path), "%s/%s", mapview::VECTOR_ROOT, p.file);
|
||||
mapview::s_vpacks.closeOpen();
|
||||
remove(path);
|
||||
char file[sizeof(p.file)];
|
||||
snprintf(file, sizeof(file), "%s", p.file); // p is the list's: read again by removePack()
|
||||
mapview::s_vpacks.removePack(file);
|
||||
mapview::s_vector.available(); // lists the packs again, forgets the data read
|
||||
mapview::labels::flush();
|
||||
mapview::s_cache.invalidate();
|
||||
|
||||
@@ -200,7 +200,7 @@ void UITask::scopeAction(uint8_t act) {
|
||||
char t[40];
|
||||
snprintf(t, sizeof(t), "Delete %s?", sl.name((uint8_t)s_scope));
|
||||
confirmBody(navPopupPanel(t, false),
|
||||
"Channels using it go back to no scope. If it is the default, * becomes the default.",
|
||||
"Channels using it follow the default again. If it is the default, * becomes the default.",
|
||||
LV_SYMBOL_TRASH " Delete", onScopeAction, S_DELETE_GO);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -487,7 +487,9 @@ void UITask::schemaRow(lv_obj_t* card, int i) {
|
||||
lv_obj_add_event_cb(sw, onSchemaSwitch, LV_EVENT_VALUE_CHANGED, (void*)(uintptr_t)i);
|
||||
return;
|
||||
}
|
||||
char opts[160];
|
||||
// The longest list, the time zone, is 27 options and 167 bytes: at 160
|
||||
// it lost UTC+8 .. +14, and a zone set from the app showed as UTC-12.
|
||||
char opts[320];
|
||||
int o = 0;
|
||||
for (uint8_t k = 0; k < st.count && o < (int)sizeof(opts) - 16; k++) {
|
||||
if (k) opts[o++] = '\n';
|
||||
|
||||
@@ -414,6 +414,7 @@ void UITask::storageFormatTap(bool go) {
|
||||
lv_refr_now(NULL); // shown before the loop blocks
|
||||
stopWalk();
|
||||
if (mapview::s_dl.active()) mapDownloadStop();
|
||||
mapview::s_vpacks.closeOpen();
|
||||
bool ok = lvport::formatCard();
|
||||
if (!ok) {
|
||||
if (s_fmt_overlay) lv_obj_delete_async(s_fmt_overlay);
|
||||
@@ -504,6 +505,7 @@ void UITask::usbTap(bool drive) {
|
||||
if (!drive || locked()) return;
|
||||
storeview::stopWalk();
|
||||
if (mapview::s_dl.active()) mapDownloadStop();
|
||||
mapview::s_vpacks.closeOpen(); // a pack file kept open between reads: not across the unmount
|
||||
if (!lvport::usbDriveStart()) { showToast("No SD card to share", 3000); return; }
|
||||
usbPanel("USB drive",
|
||||
"The computer has the SD card now. Eject it there (or unplug the cable) when you're done - "
|
||||
|
||||
@@ -951,6 +951,7 @@ void UITask::fillStart(int n, int first, void (UITask::*row)(int)) {
|
||||
void UITask::fillStep() {
|
||||
FillJob& j = s_fill[0];
|
||||
(this->*j.fn)(j.next++); // may queue more, behind
|
||||
if (!s_fill_n) return; // the piece built a new screen: the queue went with the old one
|
||||
if (j.next >= j.end) memmove(s_fill, s_fill + 1, --s_fill_n * sizeof(FillJob));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,25 +35,75 @@ public:
|
||||
// start), then done() -- a pack's file stays open for the next read.
|
||||
struct Src { FILE* f = nullptr; uint32_t len = 0; bool owned = false; };
|
||||
|
||||
// The loop (labels, the region list, a scan) and the tile task on the other
|
||||
// core both read packs: one shared FILE* and index. A recursive lock spans
|
||||
// find() to done() and every change to the list, so one never closes or
|
||||
// seeks the file under the other.
|
||||
#if defined(ESP32)
|
||||
VectorPacks() : _mx(xSemaphoreCreateRecursiveMutex()) {}
|
||||
void lock() { if (_mx) xSemaphoreTakeRecursive(_mx, portMAX_DELAY); }
|
||||
void unlock() { if (_mx) xSemaphoreGiveRecursive(_mx); }
|
||||
#else
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
#endif
|
||||
|
||||
// Lists the packs again (the map opening, a download finished).
|
||||
void scan() {
|
||||
lock();
|
||||
closeOpen();
|
||||
for (int i = 0; i < _n; i++) free(_p[i].index);
|
||||
_n = 0;
|
||||
DIR* d = opendir(VECTOR_ROOT);
|
||||
if (!d) return;
|
||||
if (!d) { unlock(); return; }
|
||||
while (struct dirent* e = readdir(d)) {
|
||||
size_t l = strlen(e->d_name);
|
||||
if (_n >= MAX || l < 5 || l >= sizeof(_p[0].file) || strcasecmp(e->d_name + l - 4, ".vpk") != 0) continue;
|
||||
if (open(_p[_n], e->d_name)) _n++;
|
||||
}
|
||||
closedir(d);
|
||||
unlock();
|
||||
}
|
||||
|
||||
int count() const { return _n; }
|
||||
const Pack& at(int i) const { return _p[i]; }
|
||||
|
||||
// True: `s` is ready to read and the lock is held until done(s).
|
||||
bool find(uint8_t kind, int dz, int x, int y, Src& s) {
|
||||
lock();
|
||||
if (findLocked(kind, dz, x, y, s)) return true;
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
void done(Src& s) {
|
||||
if (s.owned && s.f) fclose(s.f);
|
||||
s.f = nullptr;
|
||||
unlock();
|
||||
}
|
||||
|
||||
// Before a pack file is replaced or deleted.
|
||||
void closeOpen() {
|
||||
lock();
|
||||
if (_open_f) fclose(_open_f);
|
||||
_open_f = nullptr;
|
||||
_open_i = -1;
|
||||
unlock();
|
||||
}
|
||||
|
||||
// Deletes a pack: closed and removed under the lock (the tile task can't
|
||||
// reopen it in between), then the list read again.
|
||||
void removePack(const char* file) {
|
||||
lock();
|
||||
closeOpen();
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "%s/%s", VECTOR_ROOT, file);
|
||||
remove(path);
|
||||
scan();
|
||||
unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
bool findLocked(uint8_t kind, int dz, int x, int y, Src& s) {
|
||||
uint64_t key = keyOf(kind, dz, x, y);
|
||||
for (int i = 0; i < _n; i++) {
|
||||
Pack& p = _p[i];
|
||||
@@ -75,23 +125,14 @@ public:
|
||||
s.f = f; s.len = sz > 0 ? (uint32_t)sz : 0; s.owned = true;
|
||||
return true;
|
||||
}
|
||||
void done(Src& s) {
|
||||
if (s.owned && s.f) fclose(s.f);
|
||||
s.f = nullptr;
|
||||
}
|
||||
|
||||
// Before a pack file is replaced or deleted.
|
||||
void closeOpen() {
|
||||
if (_open_f) fclose(_open_f);
|
||||
_open_f = nullptr;
|
||||
_open_i = -1;
|
||||
}
|
||||
|
||||
private:
|
||||
Pack _p[MAX];
|
||||
int _n = 0;
|
||||
FILE* _open_f = nullptr; // the pack read last
|
||||
int _open_i = -1;
|
||||
#if defined(ESP32)
|
||||
SemaphoreHandle_t _mx;
|
||||
#endif
|
||||
static const uint32_t INDEX_IN_RAM_MAX = 2u * 1024 * 1024;
|
||||
|
||||
static uint64_t keyOf(uint8_t kind, int dz, int x, int y) {
|
||||
|
||||
Reference in New Issue
Block a user