/ src / app / app_odiin.cpp
app_odiin.cpp
  1  #include "app_odiin.h"
  2  
  3  #include "app_error.h"
  4  #include "app_timer.h"
  5  #include "nrf_drv_clock.h"
  6  #include "nrf_log_ctrl.h"
  7  #include "nrf_log_default_backends.h"
  8  
  9  #include "app_settings.h"
 10  #include "crypto/crypto_shared.h"
 11  #include "global/global_data.h"
 12  #include "fsm/app_odiin_fsm.h"
 13  #include "platform/platform_battery.h"
 14  #include "platform/platform_watchdog.h"
 15  #include "timer/timer.h"
 16  #include "usb/usb.h"
 17  
 18  #include "app_log_module.ii"
 19  
 20  APP_TIMER_DEF(app_odiin_1sec);
 21  
 22  namespace app
 23  {
 24  	namespace
 25  	{
 26  		platform_power_driver_t& power = platform_power_nrf52;
 27  		platform_battery_driver_t& battery = platform_battery_makerdiary;
 28  		platform_watchdog_driver_t& watchdog = platform_watchdog_nrf52;
 29  
 30  		// #todo: This code should be in a HAL.
 31  		// magic values from UF2 bootloader (see bootloader's main.c)
 32  		// GPREGRET and GPREGRET2 are general
 33  		// purpose retention registers. They are
 34  		// 1 byte each, and their value persists
 35  		// through wakeups from SYSOFF. The bootloader
 36  		// checks for these magic values.
 37  		enum DfuMagic {
 38  			OtaReset = 0xA8,
 39  			SerialOnlyReset = 0x4E,
 40  			Uf2Reset = 0x57,
 41  			Skip = 0x6d,
 42  		};
 43  	}
 44  
 45  	//////////////////////////////////////////////////////////////////////////
 46  	// Public interface
 47  
 48  	Odiin* Odiin::GetInstance()
 49  	{
 50  		static Odiin instance;
 51  		return &instance;
 52  	}
 53  
 54  	files::Fat32* Odiin::GetSdCard()
 55  	{
 56  		return GetInstance()->sdCard;
 57  	}
 58  
 59  	Odiin::~Odiin()
 60  	{
 61  
 62  	}
 63  
 64  	void Odiin::Update()
 65  	{
 66  		uint32_t ticksCurrent = timer_get_ticks();
 67  		uint32_t ticksDelta = ticksCurrent - ticksPrevious;
 68  		float delta = timer_ticks_to_seconds(ticksDelta);
 69  
 70  		NRF_LOG_FLUSH();
 71  
 72  		usb::device::Update();
 73  		screen->Update(delta);
 74  		statusLed->Update(delta);
 75  
 76  		UpdateBattery();
 77  
 78  		ticksPrevious = ticksCurrent;
 79  
 80  		watchdog.feed();
 81  
 82  		// power update will handle our WFE/SEV
 83  		power.update();
 84  	}
 85  
 86  	void Odiin::SetNfcTagPayload(const char* filename)
 87  	{
 88  		if (nfcTagEmulator->IsEnabled())
 89  		{
 90  			NRF_LOG_ERROR("Cannot set payload while NFCT is enabled!");
 91  			return;
 92  		}
 93  
 94  		files::Fat32File file;
 95  		if (sdCard->FileOpen(file, filename, FA_READ) == false)
 96  		{
 97  			nfcTagEmulator->SetPayload(nullptr);
 98  			memset(activeNfctFilename, 0, FF_MAX_LFN);
 99  			NRF_LOG_WARNING("Failed to open `%s` to set payload data!", NRF_LOG_PUSH((char*)filename));
100  			// todo: dispatch an error event
101  		}
102  
103  		size_t amountRead = 0;
104  		if (sdCard->FileRead(file, payload.GetRawTagMemory(), payload.TagMemoryCapacity, &amountRead))
105  		{
106  			// read successful. load it up and away we go!
107  			nfcTagEmulator->SetPayload(&payload);
108  			snprintf(activeNfctFilename, FF_MAX_LFN, "%s", filename);
109  			NRF_LOG_INFO("NFCT payload set to `%s`", NRF_LOG_PUSH(activeNfctFilename));
110  			SendEvent(app::fsm::NfctActivateEvent());
111  		}
112  		else
113  		{
114  			nfcTagEmulator->SetPayload(nullptr);
115  			memset(activeNfctFilename, 0, FF_MAX_LFN);
116  			NRF_LOG_WARNING("Failed to read `%s` to set payload data!", NRF_LOG_PUSH((char*)filename));
117  			// todo: dispatch an error event
118  		}
119  	}
120  
121  	void Odiin::SetNfcTagEnabled(bool enabled)
122  	{
123  		if (enabled)
124  		{
125  			nfcTagEmulator->EnableTag();
126  		}
127  		else
128  		{
129  			nfcTagEmulator->DisableTag();
130  		}
131  	}
132  
133  	const char* Odiin::GetActiveNfcTagPayloadFilename()
134  	{
135  		return activeNfctFilename;
136  	}
137  
138  	//////////////////////////////////////////////////////////////////////////
139  	// Settings
140  
141  	void Odiin::SetBacklightBrightness(float level)
142  	{
143  		if (level < 0.01f)
144  		{
145  			level = 0.01f;
146  		}
147  
148  		SETTINGS.backlight_brightness = (uint8_t)(UINT8_MAX * level);
149  		screen->SetBacklightBrightness(level);
150  	}
151  
152  	void Odiin::LoadSettings()
153  	{
154  		if (app::settings::Load(*flash))
155  		{
156  			NRF_LOG_INFO("Settings loaded!");
157  		}
158  		else
159  		{
160  			NRF_LOG_ERROR("Failed to load settings. Will try to save default settings.");
161  			SaveSettings();
162  		}
163  	}
164  
165  	void Odiin::SaveSettings()
166  	{
167  		if (app::settings::Save(*flash))
168  		{
169  			NRF_LOG_INFO("Settings saved!");
170  		}
171  		else
172  		{
173  			NRF_LOG_ERROR("Failed to save settings. Is the flash device working?");
174  			// we probably should panic, but lets try to continue...
175  		}
176  	}
177  
178  	//////////////////////////////////////////////////////////////////////////
179  	// USB Listener interface
180  	void Odiin::UsbWillEnable(app_usbd_event_type_t event)
181  	{
182  		app::fsm::UsbConnectionEvent uce = {
183  			.IsConnected = true
184  		};
185  		StateMachine::dispatch(uce);
186  	}
187  
188  	void Odiin::UsbDidDisable(app_usbd_event_type_t event)
189  	{
190  		app::fsm::UsbConnectionEvent uce = {
191  			.IsConnected = false
192  		};
193  		StateMachine::dispatch(uce);
194  	}
195  
196  	//////////////////////////////////////////////////////////////////////////
197  	// Private interface
198  
199  	//////////////////////////////////////////////////////////////////////////
200  	// Application component initilization
201  
202  	Odiin::Odiin()
203  	{
204  		NRF_LOG_RAW_INFO("=====================================\n");
205  		NRF_LOG_INFO("Odiin initializing...");
206  
207  		InitializeLogs();
208  		InitializeClocks();
209  		InitializeTimers();
210  		InitializeBsp();
211  		InitializePower();
212  		InitializeFlash();
213  		LoadSettings();
214  		InitializeCrypto();
215  		InitializeUsbDevice();
216  		InitializeSdCard();
217  		InitializeInput();
218  		InitializeScreen();
219  		InitializeLeds();
220  		InitializeNfcTag();
221  
222  		NRF_LOG_INFO("Odiin initialization complete " NRF_LOG_FLOAT_MARKER " seconds. Starting application.", NRF_LOG_FLOAT(timer_get_elapsed_seconds()));
223  
224  		StartApplication();
225  
226  		NRF_LOG_RAW_INFO("=====================================\n");
227  
228  		// prevent a giant time delta on the first frame,
229  		// by initializing the timer to the current amount of ticks.
230  		ticksPrevious = timer_get_ticks();
231  	}
232  
233  	void Odiin::InitializeLogs()
234  	{
235  		ret_code_t err_code = NRF_LOG_INIT(app_usbd_sof_timestamp_get);
236  		APP_ERROR_CHECK(err_code);
237  
238  		NRF_LOG_DEFAULT_BACKENDS_INIT();
239  	}
240  
241  	void Odiin::InitializeClocks()
242  	{
243  		// initialize nrf clock module. (calls nrfx_clock_init inside.)
244  		ret_code_t ret = nrf_drv_clock_init();
245  		APP_ERROR_CHECK(ret);
246  	}
247  
248  	void Odiin::InitializeTimers()
249  	{
250  		ret_code_t err_code = app_timer_init();
251  		APP_ERROR_CHECK(err_code);
252  
253  		// initialize the frame timer based on the rtc
254  		timer_initialize();
255  		timer_start();
256  
257  		// initialize the 1-sec tick timer
258  		err_code = app_timer_create(&app_odiin_1sec,
259  									APP_TIMER_MODE_REPEATED,
260  									[](void* ctx){ ((app::Odiin*)ctx)->TimerTick1Sec(); });
261  
262  		APP_ERROR_CHECK(err_code);
263  		constexpr uint32_t ticks1SecMs = 1000;
264  		constexpr uint32_t ticks1Sec = APP_TIMER_TICKS(ticks1SecMs);
265  		app_timer_start(app_odiin_1sec, ticks1Sec, this);
266  
267  		// initialize the watchdog timer
268  		platform_watchdog_driver_config_t wdt_config;
269  		watchdog.initialize(&wdt_config);
270  		watchdog.enable();
271  	}
272  
273  	void Odiin::InitializeBsp()
274  	{
275  		uint32_t err_code;
276  
277  		const uint32_t bspFlags = BSP_INIT_LEDS | BSP_INIT_BUTTONS;
278  
279  		err_code = bsp_init(bspFlags, Odiin::BspEventHandler);
280  		APP_ERROR_CHECK(err_code);
281  	}
282  
283  	void Odiin::InitializePower()
284  	{
285  		power.event_handler = Odiin::ShutdownHandler;
286  		power.initialize();
287  
288  		battery.initialize();
289  		// battery is initialized, we want to update at first chance
290  		forceUpdateBattery = true;
291  	}
292  
293  	void Odiin::InitializeFlash()
294  	{
295  		static files::Littlefs fs;
296  		flash = &fs;
297  		if (flash->Mount() == false)
298  		{
299  			NRF_LOG_WARNING("Flash failed to mount!");
300  		}
301  	}
302  
303  	void Odiin::InitializeCrypto() // crypto means cryptography
304  	{
305  		crypto::Initialize();
306  	}
307  
308  	void Odiin::InitializeUsbDevice()
309  	{
310  		usb::device::Initialize();
311  		static usb::MassStorageClass msc;
312  		usbMassStorageClass = &msc;
313  		usbMassStorageClass->RegisterClass();
314  		usb::device::Enable();
315  
316  		// We'd like to hear about USB events.
317  		usb::device::RegisterListener(this);
318  	}
319  
320  	void Odiin::InitializeSdCard()
321  	{
322  		static files::Fat32 sdc;
323  		sdCard = &sdc;
324  
325  		// we will go ahead and start mounted
326  		sdCard->Mount();
327  
328  		// SDCARD will want to listen for USB events.
329  		usb::device::RegisterListener(sdCard);
330  	}
331  
332  	void Odiin::InitializeInput()
333  	{
334  		static input::Keypad kp;
335  		keypad = &kp;
336  	}
337  
338  	void Odiin::InitializeScreen()
339  	{
340  		float backlightBrightness = (SETTINGS.backlight_brightness / (float)UINT8_MAX);
341  		static display::Screen scr(keypad, backlightBrightness);
342  		screen = &scr;
343  	}
344  
345  	void Odiin::InitializeLeds()
346  	{
347  		static StatusLed led;
348  		statusLed = &led;
349  		statusLed->SetModeBoot();
350  	}
351  
352  	void Odiin::InitializeNfcTag()
353  	{
354  		static NfcTagEmulator tagEmu;
355  		nfcTagEmulator = &tagEmu;
356  
357  		nfcTagEmulator->Initialize();
358  		nfcTagEmulator->SetPasswordAuthenticationAckResponse(
359  			SETTINGS.ntag_default_password_authentication_acknowledgement);
360  	}
361  
362  	void Odiin::StartApplication()
363  	{
364  		// and then...
365  		// let's get down to business!
366  		// to defeat
367  		// the punssss.
368  		StateMachine::SetOdiin(this);
369  		StateMachine::start();
370  	}
371  
372  	//////////////////////////////////////////////////////////////////////////
373  	// Timer Handlers
374  	void Odiin::TimerTick1Sec()
375  	{
376  		updateBattery = true;
377  	}
378  
379  	//////////////////////////////////////////////////////////////////////////
380  	// Private Updaters
381  
382  	void Odiin::UpdateBattery()
383  	{
384  		constexpr size_t SAMPLE_COUNT = 10;
385  		static uint32_t sample = 0;
386  		static uint8_t numSamples = 0;
387  
388  		if (updateBattery || forceUpdateBattery)
389  		{
390  			updateBattery = false;
391  
392  			battery.update();
393  			uint8_t percent = battery.get_battery_state_of_charge();
394  			sample += percent;
395  			numSamples++;
396  
397  			platform_battery_state_t batteryState = battery.get_battery_state();
398  			bool newIsCharging = batteryState == PLATFORM_BATTERY_STATE_CHARGING;
399  
400  			// did our charging status change? if so, force update!
401  			if (batteryIsCharging != newIsCharging)
402  			{
403  				forceUpdateBattery = true;
404  			}
405  
406  			batteryIsCharging = newIsCharging;
407  
408  			if (numSamples >= SAMPLE_COUNT || forceUpdateBattery)
409  			{
410  				forceUpdateBattery = false;
411  
412  				// calculate the average by the number of samples we've taken.
413  				batteryStateOfCharge = (sample / numSamples);
414  
415  #if defined(ODIIN_VERBOSE_LOGGING) && ODIIN_VERBOSE_LOGGING == 1
416  				uint16_t currentMilliVolts = battery.get_battery_voltage();
417  				const char* chargingText = "";
418  
419  				switch (batteryState)
420  				{
421  					case PLATFORM_BATTERY_STATE_CHARGING:
422  						chargingText = "charging";
423  						break;
424  					case PLATFORM_BATTERY_CAN_CHARGE:
425  						chargingText = "charge possible";
426  						break;
427  					case PLATFORM_BATTERY_CAN_NOT_CHARGE:
428  						chargingText = "can't charge";
429  						break;
430  					case PLATFORM_BATTERY_CAN_CHARGE_UNKNOWN:
431  						chargingText = "unknown state";
432  						break;
433  				}
434  
435  				NRF_LOG_VERBOSE("[Bat]: %d%% [%s] (%d mV)", average, chargingText, currentMilliVolts);
436  #endif // ODIIN_VERBOSE_LOGGING
437  
438  				// reset samples
439  				numSamples = 0;
440  				sample = 0;
441  
442  				// update ui
443  				screen->SetBatteryStatus(batteryStateOfCharge, batteryIsCharging);
444  			}
445  		}
446  	}
447  
448  	//////////////////////////////////////////////////////////////////////////
449  	// Power Event Triggers
450  
451  	void Odiin::Sleep()
452  	{
453  		power.shutdown(PLATFORM_POWER_SLEEP);
454  	}
455  
456  	void Odiin::PowerOff()
457  	{
458  		power.shutdown(PLATFORM_POWER_OFF);
459  	}
460  
461  	void Odiin::RebootToDfu()
462  	{
463  		power.shutdown(PLATFORM_POWER_DFU);
464  	}
465  
466  	void Odiin::Reboot()
467  	{
468  		power.shutdown(PLATFORM_POWER_RESET);
469  	}
470  
471  
472  	//////////////////////////////////////////////////////////////////////////
473  	// Power Event Handlers
474  
475  	bool Odiin::OnSleep()
476  	{
477  		NRF_LOG_WARNING("Device going to sleep.");
478  		screen->BacklightOffImmediate();
479  		screen->DisplaySleep();
480  		statusLed->SetModeShutdown();
481  
482  		// tell the bootloader that we want to come right back to the app.
483  		NRF_POWER->GPREGRET = DfuMagic::Skip;
484  
485  		return true;
486  	}
487  
488  	bool Odiin::OnPowerOff()
489  	{
490  		NRF_LOG_WARNING("Device will power OFF!");
491  		return true;
492  	}
493  
494  	bool Odiin::OnRebootToDfu()
495  	{
496  		NRF_LOG_WARNING("Device rebooting to DFU!");
497  
498  		// tell the bootloader to boot into uf2 dfu mode
499  		NRF_POWER->GPREGRET = DfuMagic::Uf2Reset;
500  
501  		return true;
502  	}
503  
504  	bool Odiin::OnReboot()
505  	{
506  		NRF_LOG_WARNING("Device rebooting!");
507  
508  		// tell the bootloader that we want to come right back to the app.
509  		NRF_POWER->GPREGRET = DfuMagic::Skip;
510  		return true;
511  	}
512  
513  	void Odiin::BspEventHandler(bsp_event_t event)
514  	{
515  		switch (event)
516  		{
517  			// any key events (button presses) from
518  			// our regular buttons, we will feed
519  			// the power management. this resets
520  			// the sleep timer, and keeps the device active.
521  			case BSP_EVENT_KEY_0:
522  			case BSP_EVENT_KEY_1:
523  			case BSP_EVENT_KEY_2:
524  			case BSP_EVENT_KEY_3:
525  			case BSP_EVENT_KEY_4:
526  			case BSP_EVENT_KEY_5:
527  			case BSP_EVENT_KEY_6:
528  				power.feed();
529  				break;
530  			default:
531  				NRF_LOG_DEBUG("[BSP] Unhandled event: %d", event);
532  				break;
533  		}
534  	}
535  
536  	bool Odiin::ShutdownHandler(platform_power_event_t event)
537  	{
538  		switch (event)
539  		{
540  			case PLATFORM_POWER_EVENT_PREPARE_WAKEUP:
541  				return Odiin::GetInstance()->OnSleep();
542  			case PLATFORM_POWER_EVENT_PREPARE_OFF:
543  				return Odiin::GetInstance()->OnPowerOff();
544  			case PLATFORM_POWER_EVENT_PREPARE_DFU:
545  				return Odiin::GetInstance()->OnRebootToDfu();
546  			case PLATFORM_POWER_EVENT_PREPARE_RESET:
547  				return Odiin::GetInstance()->OnReboot();
548  			default:
549  				NRF_LOG_WARNING("Unhandled shutdown event: %d", event);
550  				return true;
551  		}
552  	}
553  } // namespace app