1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
/*
* 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 "file_gatherer.hpp"
#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 database::IFileGatherer* sFileGatherer;
void Running::entry() {
if (mountStorage()) {
events::Ui().Dispatch(StorageMounted{});
}
}
void Running::exit() {
unmountStorage();
}
void Running::react(const KeyLockChanged& ev) {
if (IdleCondition()) {
transit<Idle>();
}
}
void Running::react(const audio::PlaybackFinished& ev) {
if (IdleCondition()) {
transit<Idle>();
}
}
void Running::react(const database::event::UpdateFinished&) {
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) {
if (!sStorage && mountStorage()) {
events::Ui().Dispatch(StorageMounted{});
}
}
// 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.
if (mountStorage()) {
events::Ui().Dispatch(StorageMounted{});
}
}
}
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");
return true;
}
auto Running::unmountStorage() -> void {
ESP_LOGW(kTag, "unmounting storage");
sServices->database({});
sStorage.reset();
}
} // namespace states
} // namespace system_fsm
|