summaryrefslogtreecommitdiff
path: root/src/system_fsm
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-05-02 19:12:26 +1000
committerjacqueline <me@jacqueline.id.au>2024-05-02 19:12:26 +1000
commit1573a8c4cde1cd9528b422b2dcc598e37ffe94a7 (patch)
treed162822b8fd7054f81bace0c7a65ab4d5e6f93ef /src/system_fsm
parenta231fd1c8afedbeb14b0bc77d76bad61db986059 (diff)
downloadtangara-fw-1573a8c4cde1cd9528b422b2dcc598e37ffe94a7.tar.gz
WIP merge cyclically dependent components into one big component
Diffstat (limited to 'src/system_fsm')
-rw-r--r--src/system_fsm/CMakeLists.txt11
-rw-r--r--src/system_fsm/booting.cpp140
-rw-r--r--src/system_fsm/idle.cpp108
-rw-r--r--src/system_fsm/include/service_locator.hpp153
-rw-r--r--src/system_fsm/include/system_events.hpp89
-rw-r--r--src/system_fsm/include/system_fsm.hpp146
-rw-r--r--src/system_fsm/running.cpp197
-rw-r--r--src/system_fsm/service_locator.cpp19
-rw-r--r--src/system_fsm/system_fsm.cpp102
9 files changed, 0 insertions, 965 deletions
diff --git a/src/system_fsm/CMakeLists.txt b/src/system_fsm/CMakeLists.txt
deleted file mode 100644
index e98d4653..00000000
--- a/src/system_fsm/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2023 jacqueline <me@jacqueline.id.au>
-#
-# SPDX-License-Identifier: GPL-3.0-only
-
-idf_component_register(
- SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp"
- "service_locator.cpp"
- INCLUDE_DIRS "include"
- REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio"
- "app_console" "battery" "locale")
-target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp
deleted file mode 100644
index 44700cc4..00000000
--- a/src/system_fsm/booting.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "collation.hpp"
-#include "haptics.hpp"
-#include "spiffs.hpp"
-#include "system_fsm.hpp"
-
-#include <stdint.h>
-#include <memory>
-
-#include "assert.h"
-#include "esp_err.h"
-#include "esp_log.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "freertos/timers.h"
-
-#include "adc.hpp"
-#include "audio_fsm.hpp"
-#include "battery.hpp"
-#include "bluetooth.hpp"
-#include "bluetooth_types.hpp"
-#include "display_init.hpp"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "i2c.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "spi.hpp"
-#include "system_events.hpp"
-#include "tag_parser.hpp"
-#include "tasks.hpp"
-#include "touchwheel.hpp"
-#include "track_queue.hpp"
-#include "ui_fsm.hpp"
-
-namespace system_fsm {
-namespace states {
-
-[[maybe_unused]] static const char kTag[] = "BOOT";
-
-static auto bt_event_cb(drivers::bluetooth::Event ev) -> void {
- events::Ui().Dispatch(BluetoothEvent{.event = ev});
- events::Audio().Dispatch(BluetoothEvent{.event = ev});
-}
-
-static const TickType_t kInterruptCheckPeriod = pdMS_TO_TICKS(100);
-
-auto Booting::entry() -> void {
- ESP_LOGI(kTag, "beginning tangara boot");
- sServices.reset(new ServiceLocator());
-
- ESP_LOGI(kTag, "installing early drivers");
- // NVS is needed first because it contains information about what specific
- // hardware configuration we're running on.
- sServices->nvs(
- std::unique_ptr<drivers::NvsStorage>(drivers::NvsStorage::OpenSync()));
-
- // HACK: tell the unit that it has an ERM motor (we will likely default to
- // LRAs in future, but all the current units in the field use ERMs.)
- sServices->nvs().HapticMotorIsErm(true);
-
- // HACK: fix up the switch polarity on newer dev units
- // sServices->nvs().LockPolarity(false);
-
- // I2C and SPI are both always needed. We can't even power down or show an
- // error without these.
- ESP_ERROR_CHECK(drivers::init_spi());
- sServices->gpios(std::unique_ptr<drivers::Gpios>(
- drivers::Gpios::Create(sServices->nvs().LockPolarity())));
-
- ESP_LOGI(kTag, "starting ui");
- if (!ui::UiState::InitBootSplash(sServices->gpios(), sServices->nvs())) {
- events::System().Dispatch(FatalError{});
- return;
- }
-
- ESP_LOGI(kTag, "starting bg worker");
- sServices->bg_worker(std::make_unique<tasks::WorkerPool>());
-
- ESP_LOGI(kTag, "installing remaining drivers");
- drivers::spiffs_mount();
- sServices->samd(std::unique_ptr<drivers::Samd>(drivers::Samd::Create()));
- sServices->touchwheel(
- std::unique_ptr<drivers::TouchWheel>{drivers::TouchWheel::Create()});
- sServices->haptics(std::make_unique<drivers::Haptics>(
- sServices->nvs().HapticMotorIsErm()
- ? std::variant<drivers::ErmMotor, drivers::LraMotor>(
- drivers::ErmMotor())
- : drivers::LraMotor()));
-
- auto adc = drivers::AdcBattery::Create();
- sServices->battery(std::make_unique<battery::Battery>(
- sServices->samd(), std::unique_ptr<drivers::AdcBattery>(adc)));
-
- sServices->track_queue(
- std::make_unique<audio::TrackQueue>(sServices->bg_worker()));
- sServices->tag_parser(std::make_unique<database::TagParserImpl>());
- sServices->collator(locale::CreateCollator());
-
- ESP_LOGI(kTag, "init bluetooth");
- sServices->bluetooth(std::make_unique<drivers::Bluetooth>(
- sServices->nvs(), sServices->bg_worker()));
- sServices->bluetooth().SetEventHandler(bt_event_cb);
-
- BootComplete ev{.services = sServices};
- events::Audio().Dispatch(ev);
- events::Ui().Dispatch(ev);
- events::System().Dispatch(ev);
-}
-
-auto Booting::exit() -> void {
- // TODO(jacqueline): Gate this on something. Debug flag? Flashing mode?
- sAppConsole = new console::AppConsole();
- sAppConsole->sServices = sServices;
- sAppConsole->Launch();
-
- TimerHandle_t timer = xTimerCreate("INTERRUPTS", kInterruptCheckPeriod, true,
- NULL, check_interrupts_cb);
- xTimerStart(timer, portMAX_DELAY);
-}
-
-auto Booting::react(const BootComplete& ev) -> void {
- ESP_LOGI(kTag, "bootup completely successfully");
-
- if (sServices->gpios().IsLocked()) {
- transit<Idle>();
- } else {
- transit<Running>();
- }
-}
-
-} // namespace states
-} // namespace system_fsm
diff --git a/src/system_fsm/idle.cpp b/src/system_fsm/idle.cpp
deleted file mode 100644
index e28864b3..00000000
--- a/src/system_fsm/idle.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "app_console.hpp"
-#include "file_gatherer.hpp"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "gpios.hpp"
-#include "result.hpp"
-
-#include "audio_fsm.hpp"
-#include "event_queue.hpp"
-#include "samd.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
-
-namespace system_fsm {
-namespace states {
-
-[[maybe_unused]] static const char kTag[] = "IDLE";
-static const TickType_t kTicksBeforeSleep = pdMS_TO_TICKS(10000);
-
-static void timer_callback(TimerHandle_t timer) {
- events::System().Dispatch(internal::IdleTimeout{});
-}
-
-/*
- * Ensure the storage and database are both available. If either of these fails
- * to open, then we assume it's an issue with the underlying SD card.
- */
-void Idle::entry() {
- ESP_LOGI(kTag, "system became idle");
-
- sServices->nvs().Write();
-
- events::Audio().Dispatch(OnIdle{});
- events::Ui().Dispatch(OnIdle{});
-
- sIdleTimeout = xTimerCreate("idle_timeout", kTicksBeforeSleep, true, NULL,
- timer_callback);
- xTimerStart(sIdleTimeout, portMAX_DELAY);
-}
-
-void Idle::exit() {
- xTimerStop(sIdleTimeout, portMAX_DELAY);
- xTimerDelete(sIdleTimeout, portMAX_DELAY);
- ESP_LOGI(kTag, "system left idle");
-}
-
-void Idle::react(const KeyLockChanged& ev) {
- if (!ev.locking) {
- transit<Running>();
- }
-}
-
-void Idle::react(const internal::IdleTimeout& ev) {
- if (!IdleCondition()) {
- // Defensively ensure that we didn't miss an idle-ending event.
- transit<Running>();
- return;
- }
- if (sServices->samd().GetChargeStatus() !=
- drivers::Samd::ChargeStatus::kDischarging) {
- // Stay powered on if we're plugged in, in order to charge faster, sync
- // files, flash updates, etc.
- return;
- }
- ESP_LOGI(kTag, "system shutting down");
-
- // FIXME: It would be neater to just free a bunch of our pointers, deinit the
- // other state machines, etc.
- auto touchwheel = sServices->touchwheel();
- if (touchwheel) {
- touchwheel.value()->PowerDown();
- }
-
- auto& gpios = sServices->gpios();
- // Pull down to turn things off
- gpios.WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false);
- gpios.WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false);
- gpios.WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false);
-
- // Leave up to match the external pullups
- gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true);
- gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true);
-
- // Pull down to prevent sourcing current uselessly from input pins.
- gpios.WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false);
- gpios.WriteBuffered(drivers::IGpios::Pin::kKeyUp, false);
- gpios.WriteBuffered(drivers::IGpios::Pin::kKeyDown, false);
-
- gpios.Flush();
-
- // Retry shutting down in case of a transient failure with the SAMD. e.g. i2c
- // timeouts. This guards against a buggy SAMD firmware preventing idle.
- for (;;) {
- sServices->samd().PowerDown();
- vTaskDelay(pdMS_TO_TICKS(1000));
- }
-}
-
-} // namespace states
-} // namespace system_fsm
diff --git a/src/system_fsm/include/service_locator.hpp b/src/system_fsm/include/service_locator.hpp
deleted file mode 100644
index 5978578c..00000000
--- a/src/system_fsm/include/service_locator.hpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <memory>
-
-#include "battery.hpp"
-#include "bluetooth.hpp"
-#include "collation.hpp"
-#include "database.hpp"
-#include "gpios.hpp"
-#include "haptics.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "storage.hpp"
-#include "tag_parser.hpp"
-#include "tasks.hpp"
-#include "touchwheel.hpp"
-#include "track_queue.hpp"
-
-namespace system_fsm {
-
-class ServiceLocator {
- public:
- ServiceLocator();
-
- auto gpios() -> drivers::Gpios& {
- assert(gpios_ != nullptr);
- return *gpios_;
- }
-
- auto gpios(std::unique_ptr<drivers::Gpios> i) { gpios_ = std::move(i); }
-
- auto samd() -> drivers::Samd& {
- assert(samd_ != nullptr);
- return *samd_;
- }
-
- auto samd(std::unique_ptr<drivers::Samd> i) { samd_ = std::move(i); }
-
- auto nvs() -> drivers::NvsStorage& {
- assert(nvs_ != nullptr);
- return *nvs_;
- }
-
- auto nvs(std::unique_ptr<drivers::NvsStorage> i) { nvs_ = std::move(i); }
-
- auto sd() -> drivers::SdState& { return sd_; }
-
- auto sd(drivers::SdState s) { sd_ = s; }
-
- auto bluetooth() -> drivers::Bluetooth& {
- assert(bluetooth_ != nullptr);
- return *bluetooth_;
- }
-
- auto bluetooth(std::unique_ptr<drivers::Bluetooth> i) {
- bluetooth_ = std::move(i);
- }
-
- auto battery() -> battery::Battery& {
- assert(battery_ != nullptr);
- return *battery_;
- }
-
- auto battery(std::unique_ptr<battery::Battery> i) { battery_ = std::move(i); }
-
- auto touchwheel() -> std::optional<drivers::TouchWheel*> {
- if (!touchwheel_) {
- return {};
- }
- return touchwheel_.get();
- }
-
- auto touchwheel(std::unique_ptr<drivers::TouchWheel> i) {
- touchwheel_ = std::move(i);
- }
-
- auto haptics() -> drivers::Haptics& { return *haptics_; }
-
- auto haptics(std::unique_ptr<drivers::Haptics> i) { haptics_ = std::move(i); }
-
- auto database() -> std::weak_ptr<database::Database> { return database_; }
-
- auto database(std::unique_ptr<database::Database> i) {
- database_ = std::move(i);
- }
-
- auto tag_parser() -> database::ITagParser& {
- assert(tag_parser_ != nullptr);
- return *tag_parser_;
- }
-
- auto tag_parser(std::unique_ptr<database::ITagParser> i) {
- tag_parser_ = std::move(i);
- }
-
- auto track_queue() -> audio::TrackQueue& {
- assert(queue_ != nullptr);
- return *queue_;
- }
-
- auto track_queue(std::unique_ptr<audio::TrackQueue> i) {
- queue_ = std::move(i);
- }
-
- auto collator() -> locale::ICollator& {
- assert(collator_ != nullptr);
- return *collator_;
- }
-
- auto collator(std::unique_ptr<locale::ICollator> i) {
- collator_ = std::move(i);
- }
-
- auto bg_worker() -> tasks::WorkerPool& {
- assert(bg_worker_ != nullptr);
- return *bg_worker_;
- }
-
- auto bg_worker(std::unique_ptr<tasks::WorkerPool> w) -> void {
- bg_worker_ = std::move(w);
- }
-
- // Not copyable or movable.
- ServiceLocator(const ServiceLocator&) = delete;
- ServiceLocator& operator=(const ServiceLocator&) = delete;
-
- private:
- std::unique_ptr<drivers::Gpios> gpios_;
- std::unique_ptr<drivers::Samd> samd_;
- std::unique_ptr<drivers::NvsStorage> nvs_;
- std::unique_ptr<drivers::TouchWheel> touchwheel_;
- std::unique_ptr<drivers::Haptics> haptics_;
- std::unique_ptr<drivers::Bluetooth> bluetooth_;
-
- std::unique_ptr<audio::TrackQueue> queue_;
- std::unique_ptr<battery::Battery> battery_;
-
- std::shared_ptr<database::Database> database_;
- std::unique_ptr<database::ITagParser> tag_parser_;
- std::unique_ptr<locale::ICollator> collator_;
-
- std::unique_ptr<tasks::WorkerPool> bg_worker_;
-
- drivers::SdState sd_;
-};
-
-} // namespace system_fsm
diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp
deleted file mode 100644
index 22e3b6bd..00000000
--- a/src/system_fsm/include/system_events.hpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <memory>
-
-#include "battery.hpp"
-#include "bluetooth_types.hpp"
-#include "database.hpp"
-#include "ff.h"
-#include "haptics.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "tinyfsm.hpp"
-
-namespace system_fsm {
-
-struct DisplayReady : tinyfsm::Event {};
-
-/*
- * Sent by SysState when the system has finished with its boot and self-test,
- * and is now ready to run normally.
- */
-struct BootComplete : tinyfsm::Event {
- std::shared_ptr<ServiceLocator> services;
-};
-
-/*
- * May be sent by any component to indicate that the system has experienced an
- * unrecoverable error. This should be used sparingly, as it essentially brings
- * down the device.
- */
-struct FatalError : tinyfsm::Event {};
-
-struct OnIdle : tinyfsm::Event {};
-
-/*
- * Sent by SysState when the system storage has been successfully mounted.
- */
-struct StorageMounted : tinyfsm::Event {};
-
-struct StorageError : tinyfsm::Event {
- FRESULT error;
-};
-
-struct KeyLockChanged : tinyfsm::Event {
- bool locking;
-};
-struct HasPhonesChanged : tinyfsm::Event {
- bool has_headphones;
-};
-struct SdDetectChanged : tinyfsm::Event {
- bool has_sd_card;
-};
-
-struct SamdUsbMscChanged : tinyfsm::Event {
- bool en;
-};
-struct SamdUsbStatusChanged : tinyfsm::Event {
- drivers::Samd::UsbStatus new_status;
-};
-
-struct BatteryStateChanged : tinyfsm::Event {
- battery::Battery::BatteryState new_state;
-};
-
-struct BluetoothEvent : tinyfsm::Event {
- drivers::bluetooth::Event event;
-};
-
-struct HapticTrigger : tinyfsm::Event {
- drivers::Haptics::Effect effect;
-};
-
-namespace internal {
-
-struct GpioInterrupt : tinyfsm::Event {};
-struct SamdInterrupt : tinyfsm::Event {};
-
-struct IdleTimeout : tinyfsm::Event {};
-struct UnmountTimeout : tinyfsm::Event {};
-
-} // namespace internal
-
-} // namespace system_fsm
diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp
deleted file mode 100644
index f01afb3f..00000000
--- a/src/system_fsm/include/system_fsm.hpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#pragma once
-
-#include <memory>
-
-#include "app_console.hpp"
-#include "audio_events.hpp"
-#include "battery.hpp"
-#include "bluetooth.hpp"
-#include "database.hpp"
-#include "db_events.hpp"
-#include "display.hpp"
-#include "gpios.hpp"
-#include "nvs.hpp"
-#include "samd.hpp"
-#include "service_locator.hpp"
-#include "storage.hpp"
-#include "tag_parser.hpp"
-#include "tinyfsm.hpp"
-#include "touchwheel.hpp"
-
-#include "freertos/FreeRTOS.h"
-#include "freertos/timers.h"
-
-#include "system_events.hpp"
-#include "track_queue.hpp"
-
-namespace system_fsm {
-
-void check_interrupts_cb(TimerHandle_t timer);
-
-/*
- * State machine for the overall system state. Responsible for managing
- * peripherals, and bringing the rest of the system up and down.
- */
-class SystemState : public tinyfsm::Fsm<SystemState> {
- public:
- virtual ~SystemState() {}
-
- virtual void entry() {}
- virtual void exit() {}
-
- /* Fallback event handler. Does nothing. */
- void react(const tinyfsm::Event& ev) {}
-
- void react(const FatalError&);
- void react(const internal::GpioInterrupt&);
- void react(const internal::SamdInterrupt&);
-
- void react(const HapticTrigger&);
-
- virtual void react(const DisplayReady&) {}
- virtual void react(const BootComplete&) {}
- virtual void react(const StorageMounted&) {}
- virtual void react(const StorageError&) {}
- virtual void react(const KeyLockChanged&) {}
- virtual void react(const SdDetectChanged&) {}
- virtual void react(const SamdUsbMscChanged&) {}
- virtual void react(const database::event::UpdateFinished&) {}
- virtual void react(const audio::PlaybackUpdate&) {}
- virtual void react(const internal::IdleTimeout&) {}
- virtual void react(const internal::UnmountTimeout&) {}
-
- protected:
- auto IdleCondition() -> bool;
-
- static std::shared_ptr<ServiceLocator> sServices;
- static std::unique_ptr<drivers::SdStorage> sStorage;
-
- static console::AppConsole* sAppConsole;
-};
-
-namespace states {
-
-/*
- * Initial state. Initialises peripherals, starts up lvgl, checks everything
- * looks good.
- */
-class Booting : public SystemState {
- public:
- void entry() override;
- void exit() override;
-
- void react(const BootComplete&) override;
- using SystemState::react;
-};
-
-/*
- * Most common state. Everything is going full bore!
- */
-class Running : public SystemState {
- public:
- void entry() override;
- void exit() override;
-
- void react(const KeyLockChanged&) override;
- void react(const SdDetectChanged&) override;
- void react(const audio::PlaybackUpdate&) override;
- void react(const database::event::UpdateFinished&) override;
- void react(const SamdUsbMscChanged&) override;
- void react(const internal::UnmountTimeout&) override;
- void react(const StorageError&) override;
-
- using SystemState::react;
-
- private:
- auto checkIdle() -> void;
-
- auto mountStorage() -> bool;
- auto unmountStorage() -> void;
-
- bool storage_mounted_;
-};
-
-/**
- * State for when the screen is off, controls locked, and music paused. Prelude
- * to shutting off power completely.
- */
-class Idle : public SystemState {
- public:
- void entry() override;
- void exit() override;
-
- void react(const KeyLockChanged&) override;
- void react(const internal::IdleTimeout&) override;
-
- using SystemState::react;
-
- private:
- TimerHandle_t sIdleTimeout;
-};
-
-/*
- * Something unrecoverably bad went wrong. Shows an error (if possible), awaits
- * reboot.
- */
-class Error : public SystemState {};
-
-} // namespace states
-
-} // namespace system_fsm
diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp
deleted file mode 100644
index 796c96dc..00000000
--- a/src/system_fsm/running.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "app_console.hpp"
-#include "audio_events.hpp"
-#include "database.hpp"
-#include "db_events.hpp"
-#include "ff.h"
-#include "file_gatherer.hpp"
-#include "freertos/portmacro.h"
-#include "freertos/projdefs.h"
-#include "gpios.hpp"
-#include "result.hpp"
-
-#include "audio_fsm.hpp"
-#include "event_queue.hpp"
-#include "storage.hpp"
-#include "system_events.hpp"
-#include "system_fsm.hpp"
-#include "ui_fsm.hpp"
-
-namespace system_fsm {
-namespace states {
-
-[[maybe_unused]] static const char kTag[] = "RUN";
-
-static const TickType_t kTicksBeforeUnmount = pdMS_TO_TICKS(10000);
-
-static TimerHandle_t sUnmountTimer = nullptr;
-
-static void timer_callback(TimerHandle_t timer) {
- events::System().Dispatch(internal::UnmountTimeout{});
-}
-
-static database::IFileGatherer* sFileGatherer;
-
-void Running::entry() {
- if (!sUnmountTimer) {
- sUnmountTimer = xTimerCreate("unmount_timeout", kTicksBeforeUnmount, false,
- NULL, timer_callback);
- }
- // Only mount our storage immediately if we know it's not currently in use
- // by the SAMD.
- if (!sServices->samd().UsbMassStorage()) {
- mountStorage();
- }
-}
-
-void Running::exit() {
- unmountStorage();
-}
-
-void Running::react(const KeyLockChanged& ev) {
- checkIdle();
-}
-
-void Running::react(const audio::PlaybackUpdate& ev) {
- checkIdle();
-}
-
-void Running::react(const database::event::UpdateFinished&) {
- checkIdle();
-}
-
-void Running::react(const internal::UnmountTimeout&) {
- if (IdleCondition()) {
- transit<Idle>();
- }
-}
-
-void Running::react(const SdDetectChanged& ev) {
- if (sServices->samd().UsbMassStorage()) {
- // We don't currently control the sd card, so don't mess with it.
- return;
- }
-
- if (ev.has_sd_card && !sStorage) {
- mountStorage();
- }
- // Don't automatically unmount, since this event seems to occasionally happen
- // supriously. FIXME: Why?
- // (It doesn't matter too much; by the time we get this event the SD card has
- // already been disconnected electrically.)
-}
-
-void Running::react(const SamdUsbMscChanged& ev) {
- if (ev.en) {
- // Stop using the sd card, and power it off.
- unmountStorage();
-
- // Set up the SD card for usage by the samd21.
- auto& gpios = sServices->gpios();
- gpios.WriteSync(drivers::IGpios::Pin::kSdPowerEnable, 1);
- gpios.WriteSync(drivers::IGpios::Pin::kSdMuxSwitch,
- drivers::IGpios::SD_MUX_SAMD);
- gpios.WriteSync(drivers::IGpios::Pin::kSdMuxDisable, 0);
-
- // Off you go!
- sServices->samd().UsbMassStorage(true);
- } else {
- // Make sure the samd knows that its access is going away, and give it time
- // to finish up any remaining work.
- sServices->samd().UsbMassStorage(false);
- vTaskDelay(pdMS_TO_TICKS(250));
-
- auto& gpios = sServices->gpios();
- // No more writing, please!
- gpios.WriteSync(drivers::IGpios::Pin::kSdMuxDisable, 1);
- vTaskDelay(pdMS_TO_TICKS(100));
-
- // Reboot the SD card so that it comes up in a consistent state.
- // TODO: can we avoid doing this?
- gpios.WriteSync(drivers::IGpios::Pin::kSdPowerEnable, 0);
-
- // Now it's ready for us.
- mountStorage();
- }
-}
-
-void Running::react(const StorageError& ev) {
- ESP_LOGE(kTag, "storage error %u", ev.error);
- if (ev.error == FR_DISK_ERR || ev.error == FR_INVALID_OBJECT) {
- unmountStorage();
- }
-}
-
-auto Running::checkIdle() -> void {
- xTimerStop(sUnmountTimer, portMAX_DELAY);
- if (IdleCondition()) {
- xTimerStart(sUnmountTimer, portMAX_DELAY);
- }
-}
-
-auto Running::mountStorage() -> bool {
- ESP_LOGI(kTag, "mounting sd card");
- auto storage_res = drivers::SdStorage::Create(sServices->gpios());
- if (storage_res.has_error()) {
- ESP_LOGW(kTag, "failed to mount!");
- switch (storage_res.error()) {
- case drivers::SdStorage::FAILED_TO_MOUNT:
- sServices->sd(drivers::SdState::kNotFormatted);
- break;
- case drivers::SdStorage::FAILED_TO_READ:
- default:
- sServices->sd(drivers::SdState::kNotPresent);
- break;
- }
- return false;
- }
-
- sStorage.reset(storage_res.value());
- sServices->sd(drivers::SdState::kMounted);
-
- ESP_LOGI(kTag, "opening database");
- sFileGatherer = new database::FileGathererImpl();
- auto database_res =
- database::Database::Open(*sFileGatherer, sServices->tag_parser(),
- sServices->collator(), sServices->bg_worker());
- if (database_res.has_error()) {
- unmountStorage();
- return false;
- }
-
- sServices->database(
- std::unique_ptr<database::Database>{database_res.value()});
-
- ESP_LOGI(kTag, "storage loaded okay");
- events::Ui().Dispatch(StorageMounted{});
- events::Audio().Dispatch(StorageMounted{});
- events::System().Dispatch(StorageMounted{});
-
- // Tell the database to refresh so that we pick up any changes from the newly
- // mounted card.
- if (sServices->nvs().DbAutoIndex()) {
- sServices->bg_worker().Dispatch<void>([&]() {
- auto db = sServices->database().lock();
- if (!db) {
- return;
- }
- db->updateIndexes();
- });
- }
-
- return true;
-}
-
-auto Running::unmountStorage() -> void {
- ESP_LOGW(kTag, "unmounting storage");
- sServices->database({});
- sStorage.reset();
-}
-
-} // namespace states
-} // namespace system_fsm
diff --git a/src/system_fsm/service_locator.cpp b/src/system_fsm/service_locator.cpp
deleted file mode 100644
index d8dcf44a..00000000
--- a/src/system_fsm/service_locator.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "service_locator.hpp"
-
-#include <memory>
-
-#include "nvs.hpp"
-#include "storage.hpp"
-#include "touchwheel.hpp"
-
-namespace system_fsm {
-
-ServiceLocator::ServiceLocator() : sd_(drivers::SdState::kNotPresent) {}
-
-} // namespace system_fsm
diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp
deleted file mode 100644
index 59d41c73..00000000
--- a/src/system_fsm/system_fsm.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "system_fsm.hpp"
-#include "audio_fsm.hpp"
-#include "driver/gpio.h"
-#include "event_queue.hpp"
-#include "gpios.hpp"
-#include "service_locator.hpp"
-#include "system_events.hpp"
-#include "tag_parser.hpp"
-#include "track_queue.hpp"
-
-[[maybe_unused]] static const char kTag[] = "system";
-
-namespace system_fsm {
-
-std::shared_ptr<ServiceLocator> SystemState::sServices;
-std::unique_ptr<drivers::SdStorage> SystemState::sStorage;
-
-console::AppConsole* SystemState::sAppConsole;
-
-void check_interrupts_cb(TimerHandle_t timer) {
- if (!gpio_get_level(GPIO_NUM_34)) {
- events::System().Dispatch(internal::GpioInterrupt{});
- }
- if (!gpio_get_level(GPIO_NUM_35)) {
- events::System().Dispatch(internal::SamdInterrupt{});
- }
-}
-
-void SystemState::react(const FatalError& err) {
- if (!is_in_state<states::Error>()) {
- transit<states::Error>();
- }
-}
-
-void SystemState::react(const HapticTrigger& trigger) {
- auto& haptics = sServices->haptics();
- haptics.PlayWaveformEffect(trigger.effect);
-}
-
-void SystemState::react(const internal::GpioInterrupt&) {
- auto& gpios = sServices->gpios();
- bool prev_key_lock = gpios.IsLocked();
- bool prev_has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
- bool prev_has_sd = gpios.Get(drivers::Gpios::Pin::kSdCardDetect);
-
- gpios.Read();
-
- bool key_lock = gpios.IsLocked();
- bool has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
- bool has_sd = gpios.Get(drivers::Gpios::Pin::kSdCardDetect);
-
- if (key_lock != prev_key_lock) {
- KeyLockChanged ev{.locking = key_lock};
- events::System().Dispatch(ev);
- events::Audio().Dispatch(ev);
- events::Ui().Dispatch(ev);
- }
- if (has_headphones != prev_has_headphones) {
- HasPhonesChanged ev{.has_headphones = has_headphones};
- events::Audio().Dispatch(ev);
- }
- if (has_sd != prev_has_sd) {
- SdDetectChanged ev{.has_sd_card = !has_sd};
- events::System().Dispatch(ev);
- events::Ui().Dispatch(ev);
- }
-}
-
-void SystemState::react(const internal::SamdInterrupt&) {
- auto& samd = sServices->samd();
- auto prev_charge_status = samd.GetChargeStatus();
- auto prev_usb_status = samd.GetUsbStatus();
-
- samd.UpdateChargeStatus();
- samd.UpdateUsbStatus();
-
- auto charge_status = samd.GetChargeStatus();
- auto usb_status = samd.GetUsbStatus();
-
- if (charge_status != prev_charge_status && sServices) {
- sServices->battery().Update();
- }
- if (usb_status != prev_usb_status) {
- events::Ui().Dispatch(SamdUsbStatusChanged{.new_status = usb_status});
- }
-}
-
-auto SystemState::IdleCondition() -> bool {
- auto db = sServices->database().lock();
- return sServices->gpios().IsLocked() && !(db && db->isUpdating()) &&
- audio::AudioState::is_in_state<audio::states::Standby>();
-}
-
-} // namespace system_fsm
-
-FSM_INITIAL_STATE(system_fsm::SystemState, system_fsm::states::Booting)