diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-05-02 19:12:26 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-05-02 19:12:26 +1000 |
| commit | 1573a8c4cde1cd9528b422b2dcc598e37ffe94a7 (patch) | |
| tree | d162822b8fd7054f81bace0c7a65ab4d5e6f93ef /src/system_fsm | |
| parent | a231fd1c8afedbeb14b0bc77d76bad61db986059 (diff) | |
| download | tangara-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.txt | 11 | ||||
| -rw-r--r-- | src/system_fsm/booting.cpp | 140 | ||||
| -rw-r--r-- | src/system_fsm/idle.cpp | 108 | ||||
| -rw-r--r-- | src/system_fsm/include/service_locator.hpp | 153 | ||||
| -rw-r--r-- | src/system_fsm/include/system_events.hpp | 89 | ||||
| -rw-r--r-- | src/system_fsm/include/system_fsm.hpp | 146 | ||||
| -rw-r--r-- | src/system_fsm/running.cpp | 197 | ||||
| -rw-r--r-- | src/system_fsm/service_locator.cpp | 19 | ||||
| -rw-r--r-- | src/system_fsm/system_fsm.cpp | 102 |
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) |
