CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
5
6#include <stdio.h>
7#include <time.h>
8#include <sys/time.h>
9#include "freertos/FreeRTOS.h"
10#include "freertos/task.h"
11#include "nvs.h"
12#include "nvs_flash.h"
13#include "esp_log.h"
14#include "cdc_log.h"
15
20#include "cdc_ui/I18n.h"
21#include "cdc_core/EventBus.h"
23#include "cdc_core/SystemLock.h"
27#include "cdc_core/PinManager.h"
29#include "modules_init.gen.h" // Auto-generated module registrations
30#include "usb_badge/usb_cdc.h"
32#include "cdc_hal/IDisplay.h"
33#include "cdc_hal/II2cBus.h"
34#include "cdc_hal/IKeypad.h"
40#include "cdc_hal/hw_config.h"
41#include "cdc_hal/IRtc.h"
42#include "cdc_os_ui/AppUi.h"
44#include "cdc_msg/MessageTransfer.h"
45#include "driver/rtc_io.h"
46#include "esp_sleep.h"
47#include "esp_system.h"
48#include "esp_timer.h"
49
50static const char* TAG = "BOOT";
51
52using namespace cdc::core;
53
55static cdc::hal::II2cBus* s_i2cBus = nullptr;
56static cdc::hal::IKeypad* s_keypad = nullptr;
60static cdc::hal::IDisplay* s_display = nullptr;
62static esp_sleep_wakeup_cause_t s_wakeupCause = ESP_SLEEP_WAKEUP_UNDEFINED;
63
70 const char* detail) {
71 const char* reasonText;
72 switch (reason) {
74 reasonText = "Secure element alarm"; break;
76 reasonText = "Secure element offline"; break;
78 reasonText = "Secure element init failed"; break;
80 reasonText = "Non-volatile storage unreadable"; break;
81 default:
82 reasonText = "Unknown failure"; break;
83 }
84
85 LOG_E(TAG, "SYSTEM LOCKDOWN: %s%s%s", reasonText,
86 detail ? " - " : "", detail ? detail : "");
87
88 if (!s_display) {
89 return;
90 }
91
92 const int16_t w = static_cast<int16_t>(s_display->getWidth());
93 const int16_t h = static_cast<int16_t>(s_display->getHeight());
94 constexpr int16_t kMargin = 8;
95 const int16_t modalX = kMargin;
96 const int16_t modalY = kMargin;
97 const int16_t modalW = static_cast<int16_t>(w - 2 * kMargin);
98 const int16_t modalH = static_cast<int16_t>(h - 2 * kMargin);
99
100 s_display->clear();
101 s_display->fillRect(modalX, modalY, modalW, modalH, 0xFFFF);
102 s_display->drawRect(modalX, modalY, modalW, modalH, 0x0000);
103 s_display->drawRect(modalX + 1, modalY + 1,
104 static_cast<int16_t>(modalW - 2),
105 static_cast<int16_t>(modalH - 2), 0x0000);
106
107 constexpr int16_t kHeaderHeight = 18;
108 s_display->fillRect(static_cast<int16_t>(modalX + 2),
109 static_cast<int16_t>(modalY + 2),
110 static_cast<int16_t>(modalW - 4),
111 kHeaderHeight, 0x0000);
112 s_display->setTextColor(0xFFFF);
113 s_display->setTextSize(2);
114 s_display->setCursor(static_cast<int16_t>(modalX + 12),
115 static_cast<int16_t>(modalY + 4));
116 s_display->print("SYSTEM LOCKED");
117
118 s_display->setTextColor(0x0000);
119 s_display->setTextSize(1);
120
121 int16_t cursorY = static_cast<int16_t>(modalY + kHeaderHeight + 12);
122 s_display->setCursor(static_cast<int16_t>(modalX + 10), cursorY);
123 s_display->print(reasonText);
124
125 if (detail && *detail) {
126 cursorY = static_cast<int16_t>(cursorY + 14);
127 s_display->setCursor(static_cast<int16_t>(modalX + 10), cursorY);
128 s_display->printf("Detail: %s", detail);
129 }
130
131 s_display->setCursor(static_cast<int16_t>(modalX + 10),
132 static_cast<int16_t>(modalY + modalH - 14));
133 s_display->print("Power cycle to recover");
134
136}
137
143static bool initNvs() {
144 esp_err_t ret = nvs_flash_init();
145 if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
146 ESP_ERROR_CHECK(nvs_flash_erase());
147 ret = nvs_flash_init();
148 }
149 ESP_ERROR_CHECK(ret);
150 return ret == ESP_OK;
151}
152
161static void seedBuildProfile() {
162 nvs_handle_t handle = 0;
163 if (nvs_open(kBootProfileNs, NVS_READWRITE, &handle) == ESP_OK) {
164 nvs_set_u8(handle, kBootProfileKey, BUILD_PROFILE_BYTE);
165 nvs_commit(handle);
166 nvs_close(handle);
167 }
168}
169
178 constexpr uint8_t expected = BUILD_PROFILE_BYTE;
179 constexpr uint8_t kMaxValid = 0x03;
180
181 nvs_handle_t handle = 0;
182 uint8_t stored = 0;
183 esp_err_t openErr = nvs_open(kBootProfileNs, NVS_READONLY, &handle);
184 esp_err_t readErr = ESP_ERR_NVS_NOT_FOUND;
185 if (openErr == ESP_OK) {
186 readErr = nvs_get_u8(handle, kBootProfileKey, &stored);
187 nvs_close(handle);
188 }
189
190 // Treat first-boot (namespace or key absent) and a malformed value as a
191 // reason to wipe and re-seed the byte. A structural NVS error (anything
192 // other than ESP_OK / NOT_FOUND / TYPE_MISMATCH) is a hardware-level
193 // failure and must trigger lockdown rather than a destructive wipe.
194 bool structuralFault = false;
195 if (openErr != ESP_OK && openErr != ESP_ERR_NVS_NOT_FOUND) {
196 structuralFault = true;
197 }
198 if (openErr == ESP_OK
199 && readErr != ESP_OK
200 && readErr != ESP_ERR_NVS_NOT_FOUND
201 && readErr != ESP_ERR_NVS_TYPE_MISMATCH) {
202 structuralFault = true;
203 }
204
205 if (structuralFault) {
206 ESP_LOGE(TAG, "NVS unreadable (open=0x%X read=0x%X), entering lockdown", openErr, readErr);
208 cdc::core::LockdownReason::NVS_UNREADABLE, "boot_profile read failed");
209 return false;
210 }
211
212 bool present = (openErr == ESP_OK && readErr == ESP_OK);
213 bool valid = present && stored <= kMaxValid;
214 if (valid && stored == expected) {
215 return false;
216 }
217
218 if (!present) {
219 ESP_LOGW(TAG, "Build profile absent, factory reset and seed 0x%02X", expected);
220 } else if (!valid) {
221 ESP_LOGW(TAG, "Build profile malformed (0x%02X), factory reset and seed 0x%02X", stored, expected);
222 } else {
223 ESP_LOGW(TAG, "Build profile changed (0x%02X -> 0x%02X), factory reset", stored, expected);
224 }
225 ESP_ERROR_CHECK(cdc::core::wipeNvs());
226
227 // The profile byte is seeded only after the TROPIC01 wipe completes (see
228 // seedBuildProfile / wipeTropicForFactoryReset), so an interrupted reset
229 // re-runs on the next boot instead of being marked done prematurely.
230 return true;
231}
232
242 LOG_W(TAG, "Build profile change: wiping TROPIC01 ECC and R-Memory slots");
244 if (!result.sessionReady) {
245 LOG_E(TAG, "TROPIC01 factory wipe skipped (SE session unavailable), reset stays pending");
246 return;
247 }
248 LOG_W(TAG, "TROPIC01 factory wipe: deleted %u ECC keys, %u R-Memory slots",
249 result.eccDeleted, result.rmemDeleted);
251}
252
257static bool initCoreServices() {
258 if (!EventBus::instance().init()) {
259 return false; // Cannot log yet, USB not ready
260 }
261
262 if (!usb_cdc_init()) {
263 return false; // USB failed, no output channel available
264 }
265
266 log_init();
267
268 LOG_I(TAG, "=================================");
269 LOG_I(TAG, "CDC Badge OS v" APP_VERSION);
270 LOG_I(TAG, "Open Hardware Security");
271 LOG_I(TAG, "=================================");
272
273 LOG_I(TAG, "EventBus ready");
274 LOG_I(TAG, "USB CDC ready");
275 LOG_I(TAG, "ServiceRegistry ready (capacity: %u)", ServiceRegistry::MAX_SERVICES);
276 return true;
277}
278
282static void initRtc() {
284 if (rtc) {
285 rtc->init();
286 }
287 if (!rtc || !rtc->isTimeSet()) {
288 LOG_W(TAG, "System time not set");
289 }
290}
291
297 s_wakeupCause = esp_sleep_get_wakeup_cause();
298 if (s_wakeupCause == ESP_SLEEP_WAKEUP_EXT1) {
299 LOG_D(TAG, "Woke from deep sleep, deinit RTC GPIO");
300 rtc_gpio_deinit(EXP_IRQ_PIN);
301 }
302}
303
307static void initI2cBus() {
308 LOG_I(TAG, "Initializing I2C bus...");
310 if (s_i2cBus && s_i2cBus->init()) {
311 LOG_I(TAG, "I2C bus ready");
312 } else {
313 LOG_E(TAG, "I2C bus init failed!");
314 }
315}
316
321static void initPowerManager() {
322 LOG_I(TAG, "Initializing Power Management...");
324 if (s_powerManager && s_powerManager->init() && s_powerManager->start()) {
325 LOG_I(TAG, "Power Management ready (BQ25895)");
326 LOG_I(TAG, "Battery: %d%% (%dmV)", s_powerManager->getBatteryPercent(),
327 s_powerManager->getBatteryVoltage());
328 } else {
329 LOG_E(TAG, "Power Management init failed!");
330 }
331}
332
336static void initSleepController() {
337 LOG_I(TAG, "Initializing Sleep Controller...");
339 if (s_sleepController && s_sleepController->init() && s_sleepController->start()) {
340 LOG_I(TAG, "Sleep Controller ready (interval: %lus)",
341 (unsigned long)s_sleepController->getLightSleepInterval());
342 } else {
343 LOG_E(TAG, "Sleep Controller init failed!");
344 }
345}
346
350static void initWifiController() {
351 LOG_I(TAG, "Initializing WiFi Controller...");
353 if (wifiController && wifiController->init() && wifiController->start()) {
354 LOG_I(TAG, "WiFi Controller ready");
355 } else {
356 LOG_E(TAG, "WiFi Controller init failed!");
357 }
358}
359
364 LOG_I(TAG, "Initializing Bluetooth Controller...");
366 if (btController && btController->init() && btController->start()) {
367 LOG_I(TAG, "Bluetooth Controller ready");
368 } else {
369 LOG_E(TAG, "Bluetooth Controller init failed!");
370 }
371}
372
376static void initKeypad() {
377 LOG_I(TAG, "Initializing Keypad...");
379 if (s_keypad && s_keypad->init()) {
380 LOG_I(TAG, "Keypad ready (12 keys)");
381 } else {
382 LOG_E(TAG, "Keypad init failed!");
383 }
384}
385
389static void initSecureElement() {
390 LOG_I(TAG, "Initializing Secure Element...");
392 if (s_secureElement && s_secureElement->init() && s_secureElement->start()) {
393 if (s_secureElement->sessionStart()) {
394 LOG_I(TAG, "Secure Element ready (TROPIC01, session active)");
395 } else {
396 LOG_W(TAG, "Secure Element initialized but session start failed");
397 }
398 } else {
399 LOG_E(TAG, "Secure Element init failed!");
400 }
401}
402
420
430
434static void initTropicStorage() {
435 auto& tropicStorage = cdc::core::TropicStorage::instance();
436 tropicStorage.setSecureElement(s_secureElement);
437 tropicStorage.init();
438 tropicStorage.start();
439 ServiceRegistry::instance().registerService("tropic_storage", &tropicStorage);
440 LOG_I(TAG, "TropicStorage cache ready");
441}
442
448 LOG_I(TAG, "Serial Command Interface ready");
449}
450
457static void initMessageTransfer() {
458 auto& msg = cdc::msg::MessageTransfer::instance();
459 msg.init();
460 msg.start();
462 LOG_I(TAG, "Message transfer service ready");
463}
464
474
481static void initDisplay() {
482 LOG_I(TAG, "Initializing Display...");
484 if (s_display && s_display->init() && s_display->start()) {
485 LOG_I(TAG, "Display ready (%ux%u)", s_display->getWidth(), s_display->getHeight());
486
488
489 if (s_wakeupCause == ESP_SLEEP_WAKEUP_EXT1) {
490 s_display->showSplash("Waking up...");
491 } else {
492 s_display->showSplash();
493 }
494 LOG_I(TAG, "Splash screen done");
495 } else {
496 LOG_E(TAG, "Display init failed!");
497 }
498}
499
503static void initUi() {
504 LOG_I(TAG, "Initializing UI...");
505 cdc::ui::UiDeps deps;
506 deps.display = s_display;
507 deps.keypad = s_keypad;
508 deps.power = s_powerManager;
511 cdc::ui::ui_init(deps);
512}
513
520static void initModules() {
521 modules_register_all();
522
523 LOG_I(TAG, "Initializing modules...");
525
527}
528
534static void initPluginSystem() {
536 LOG_W(TAG, "PluginManager init failed - plugins disabled");
537 return;
538 }
541
542 // Scan the plugins FAT for available language files (fills the picker) and
543 // load the persisted language's overlay. Fires the language-changed
544 // callback registered in ui_init(), which rebuilds cached menu labels.
546}
547
551static void startApp() {
553
554 LOG_I(TAG, "=================================");
555 LOG_I(TAG, "System ready. Entering main loop.");
556 LOG_I(TAG, "=================================");
557}
558
565static void runMainLoopIteration() {
566 // Hard lockdown gate: if any subsystem latched the lockdown flag, run the
567 // halt-screen handler and deep-sleep without enabling wake sources.
569
573
574 if (s_powerManager) {
575 s_powerManager->update();
576 }
577
578 uint32_t nowMs = esp_timer_get_time() / 1000;
579 cdc::ui::ui_process(nowMs);
580 s_attestationService.onTick(nowMs);
581 cdc::msg::MessageTransfer::instance().tick(nowMs);
582
584
585#if DEBUG_MODE
586 static UBaseType_t s_minStackFree = 0xFFFFFFFFu;
587 UBaseType_t stackFree = uxTaskGetStackHighWaterMark(nullptr);
588 if (stackFree < s_minStackFree) {
589 s_minStackFree = stackFree;
590 LOG_I(TAG, "main stack low-water: %lu words", (unsigned long)stackFree);
591 }
592#endif
593
594 vTaskDelay(pdMS_TO_TICKS(10));
595}
596
600extern "C" void app_main(void)
601{
602 // STAGE 0: Hardware Minimum
603 initNvs();
604 bool profileChanged = checkBuildProfileAndWipeNvs();
605
606 // STAGE 1: Core Services
607 if (!initCoreServices()) {
608 // No log channel and no display available at this point: best-effort
609 // ESP_LOG to UART0, delay to let any output flush, then reboot.
610 ESP_LOGE(TAG, "Core service init failed, restarting in 5s");
611 vTaskDelay(pdMS_TO_TICKS(5000));
612 esp_restart();
613 }
614
615 // STAGE 2: Time
616 initRtc();
617
618 // STAGE 3: Hardware Peripherals
619 initHardware();
620
621 if (profileChanged) {
623 }
624
625 // STAGE 4: System Services
627
628 // STAGE 5: Display & UI
629 initDisplay();
630 initUi();
631
632 // STAGE 6: Modules
633 initModules();
634
635 // Restore persisted WiFi intent: reconnect if WiFi was on before reboot.
637
638 // STAGE 6b: Plugin system (WAMR + plugins partition)
640
641 // STAGE 7: Final startup
642 startApp();
643
644 // Main loop
645 while (true) {
647 }
648}
static const char * TAG
Native serial commands for direct GPIO / ADC / I2C poking. Independent of plugins - the user controls...
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
Discovers, loads, runs and unloads WASM plugins on the badge.
Serial console PLUGIN command bundle. Call once from main.cpp after PluginManager::init() to expose L...
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_D(tag, fmt,...)
Definition cdc_log.h:148
void log_init(void)
Logging API implementation.
Definition cdc_log.cpp:140
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
static EventBus & instance()
Returns singleton event-bus instance.
Definition EventBus.cpp:19
void process()
Drains queued events and dispatches matching handlers.
Definition EventBus.cpp:125
virtual bool start()=0
virtual bool init()=0
void runAllInitializers()
Executes all registered initializers and post-registration housekeeping.
static ModuleRegistry & instance()
Returns the singleton module registry instance.
void dispatchTick(uint32_t nowMs)
Dispatches periodic tick callback to started modules.
static PinManager & instance()
Returns singleton PIN manager instance.
void checkAndResetExpiredLockout()
Clears expired lockout state and resets retry counter.
static ServiceRegistry & instance()
Returns singleton service registry instance.
bool registerService(const char *name, IService *service)
Registers a named service instance.
static constexpr size_t MAX_SERVICES
void triggerLockdown(LockdownReason reason, const char *detail=nullptr)
Latches the lockdown flag. Idempotent and ISR-safe.
void enforceIfLocked()
If locked, runs the shutdown sequence and never returns. Otherwise returns immediately....
static SystemLock & instance()
Returns the process-wide lockdown latch singleton.
void setShutdownHandler(ShutdownHandler handler)
Installs an optional UI handler invoked just before deep sleep. Must be set from main task before mai...
static TropicStorage & instance()
Returns singleton instance of TROPIC metadata cache manager.
virtual bool isTimeSet() const =0
static PluginManager & instance() noexcept
static void init()
Public SerialCmd interface implementation.
static bool process()
Processes one pending input character from the serial console.
static I18n & instance()
Singleton accessor.
Definition I18n.cpp:287
bool loadOverlay()
Rescan available languages and (re)load the active overlay.
Definition I18n.cpp:536
void restoreOnBoot()
Restores the persisted WiFi intent at boot.
static WifiHandlers & instance()
Returns singleton Wi-Fi handlers instance.
#define BUILD_PROFILE_BYTE
bool valid
#define APP_VERSION
#define EXP_IRQ_PIN
Definition hw_config.h:19
static void initKeypad()
Initializes the keypad input scanner.
Definition main.cpp:376
static void initDisplay()
Initializes the e-paper display and shows a boot splash.
Definition main.cpp:481
static void initSystemServices()
Brings up high-level OS services that depend on hardware being ready.
Definition main.cpp:468
static void initSleepController()
Initializes the sleep controller which manages light/deep sleep.
Definition main.cpp:336
static void initI2cBus()
Brings up the I2C bus shared by the on-board peripherals.
Definition main.cpp:307
static cdc::hal::ISecureElement * s_secureElement
Definition main.cpp:58
static void initSerialCommandInterface()
Initializes the serial command processor over USB CDC.
Definition main.cpp:446
static cdc::hal::IKeypad * s_keypad
Definition main.cpp:56
static void wipeTropicForFactoryReset()
Wipes all TROPIC01 R-Memory and ECC slots used by application code, then seeds the build-profile byte...
Definition main.cpp:241
static esp_sleep_wakeup_cause_t s_wakeupCause
Definition main.cpp:62
static void handleWakeupAndReleaseRtcGpio()
Caches the wakeup cause and releases RTC GPIO when resuming from deep sleep so that subsequent I2C bu...
Definition main.cpp:296
static cdc::hal::IDisplay * s_display
Definition main.cpp:60
static void initMessageTransfer()
Initializes the badge-to-badge message transfer service.
Definition main.cpp:457
static void initBluetoothController()
Initializes the Bluetooth controller HAL singleton.
Definition main.cpp:363
void app_main(void)
Main firmware entry point.
Definition main.cpp:600
static void initPowerManager()
Initializes the BQ25895 power manager and reports current battery state to the log.
Definition main.cpp:321
static cdc::hal::II2cBus * s_i2cBus
Cached HAL/service singleton pointers used during boot sequence.
Definition main.cpp:55
static void initHardware()
Brings up all hardware peripherals in dependency order.
Definition main.cpp:410
static bool initCoreServices()
Stage 1: brings up event bus, USB CDC and the logging subsystem.
Definition main.cpp:257
static bool checkBuildProfileAndWipeNvs()
Compares the persisted build profile byte against the compiled-in value and triggers a NVS wipe on mi...
Definition main.cpp:177
static void initRtc()
Initializes the RTC and warns when the system time has not been set.
Definition main.cpp:282
static void initPluginSystem()
Bring up the WAMR runtime, mount the plugins partition and discover installed plugins....
Definition main.cpp:534
static void initTropicStorage()
Initializes and registers the TROPIC01 storage cache service.
Definition main.cpp:434
static void seedBuildProfile()
Persists the current build-profile byte, marking the factory reset complete.
Definition main.cpp:161
static void runMainLoopIteration()
Single iteration of the cooperative main loop.
Definition main.cpp:565
static cdc::core::AttestationKeyService s_attestationService
Definition main.cpp:61
static void lockdownShutdownHandler(cdc::core::LockdownReason reason, const char *detail)
Draws a modal-style system-lockdown halt screen before deep sleep.
Definition main.cpp:69
static void startApp()
Final startup step: completes USB CDC enumeration and prints banner.
Definition main.cpp:551
static void initUi()
Initializes the App UI layer with the previously prepared HAL deps.
Definition main.cpp:503
static void initAttestationService()
Registers the attestation-key service with the global ServiceRegistry.
Definition main.cpp:424
static cdc::hal::IPowerManager * s_powerManager
Definition main.cpp:57
static void initModules()
Registers all auto-generated modules and runs their initializers.
Definition main.cpp:520
static cdc::hal::ISleepController * s_sleepController
Definition main.cpp:59
static void initWifiController()
Initializes the WiFi controller HAL singleton.
Definition main.cpp:350
static void initSecureElement()
Initializes the TROPIC01 secure element and starts an active session.
Definition main.cpp:389
static bool initNvs()
Stage 0: initializes NVS flash storage with automatic erase on version-mismatch or out-of-pages error...
Definition main.cpp:143
esp_err_t wipeNvs()
Erases the NVS partition and re-initializes it blank.
TropicWipeResult wipeTropic(hal::ISecureElement *se, uint16_t progressEvery=0, void(*onRmemProgress)(uint16_t current, uint16_t total)=nullptr)
Iterates every TROPIC01 ECC slot (0..ECC_SLOT_COUNT-1) and R-Memory slot (0..RMEM_SLOT_COUNT-1),...
LockdownReason
Reason the system entered lockdown.
Definition SystemLock.h:11
constexpr const char * kBootProfileNs
constexpr const char * kBootProfileKey
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
IWifiController * getWifiControllerInstance()
Returns the singleton Wi-Fi controller service instance.
II2cBus * getI2cBus0()
Returns singleton instance of I2C bus 0.
Definition I2cBus.cpp:275
IKeypad * getKeypadInstance()
Returns the singleton keypad service instance.
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
IPowerManager * getPowerManagerInstance()
Returns the singleton power manager instance.
IRtc * getRtcInstance()
Returns the singleton RTC service instance.
Definition Rtc.cpp:304
ISleepController * getSleepControllerInstance()
Returns the singleton sleep controller service instance.
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
void ui_process(uint32_t nowMs)
Main UI tick: input processing, timeouts, status updates, and rendering.
Definition AppUi.cpp:1086
void ui_init(const UiDeps &deps)
Initializes App UI, builds all core views, and wires callbacks.
Definition AppUi.cpp:764
void ui_on_modules_ready()
Refreshes module-backed menus once module startup is complete.
Definition AppUi.cpp:1028
hal::ISleepController * sleep
Definition AppUi.h:21
hal::IDisplay * display
Definition AppUi.h:18
hal::IKeypad * keypad
Definition AppUi.h:19
hal::ISecureElement * secureElement
Definition AppUi.h:22
hal::IPowerManager * power
Definition AppUi.h:20
bool usb_cdc_start(void)
Starts USB CDC runtime (or triggers re-enumeration in early-debug mode).
Definition usb_cdc.cpp:166
bool usb_cdc_init(void)
Definition usb_cdc.cpp:129