From 418a721b0e5b65b328cbddc1e30abd285ae0c7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 9 Dec 2019 16:40:16 +0100 Subject: [PATCH] [apps/ion/exam_mode] Store the exam mode activation in the flash This way, it is not cleared by a reset --- apps/apps_container.cpp | 13 +++- apps/apps_container.h | 1 + apps/exam_pop_up_controller.cpp | 13 +--- apps/global_preferences.cpp | 16 ++++ apps/global_preferences.h | 19 +++-- .../sub_menu/exam_mode_controller.cpp | 2 +- apps/title_bar_view.cpp | 2 +- ion/include/ion.h | 1 + ion/include/ion/exam_mode.h | 13 ++++ ion/src/blackbox/Makefile | 1 + ion/src/device/n0100/flash.ld | 25 +++++-- ion/src/device/shared/drivers/Makefile | 1 + ion/src/device/shared/drivers/exam_mode.cpp | 73 +++++++++++++++++++ ion/src/emscripten/Makefile | 1 + ion/src/sdl/Makefile | 1 + ion/src/shared/dummy/exam_mode.cpp | 14 ++++ ion/src/simulator/Makefile | 1 + 17 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 ion/include/ion/exam_mode.h create mode 100644 ion/src/device/shared/drivers/exam_mode.cpp create mode 100644 ion/src/shared/dummy/exam_mode.cpp diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index adb66610492..4e268205c26 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -168,7 +168,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) { // Warning: if the window is dirtied, you need to call window()->redraw() if (event == Ion::Events::USBPlug) { if (Ion::USB::isPlugged()) { - if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) { + if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { displayExamModePopUp(false); window()->redraw(); } else { @@ -208,6 +208,9 @@ bool AppsContainer::switchTo(App::Snapshot * snapshot) { void AppsContainer::run() { window()->setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height)); + if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { + activateExamMode(); + } refreshPreferences(); /* ExceptionCheckpoint stores the value of the stack pointer when setjump is @@ -283,7 +286,7 @@ void AppsContainer::shutdownDueToLowBattery() { } while (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) { Ion::Backlight::setBrightness(0); - if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Deactivate) { + if (!GlobalPreferences::sharedGlobalPreferences()->examMode()) { /* Unless the LED is lit up for the exam mode, switch off the LED. IF the * low battery event happened during the Power-On Self-Test, a LED might * have stayed lit up. */ @@ -316,6 +319,12 @@ void AppsContainer::redrawWindow() { m_window.redraw(); } +void AppsContainer::activateExamMode() { + reset(); + Ion::LED::setColor(KDColorRed); + Ion::LED::setBlinking(1000, 0.1f); +} + void AppsContainer::examDeactivatingPopUpIsDismissed() { if (Ion::USB::isPlugged()) { Ion::USB::enable(); diff --git a/apps/apps_container.h b/apps/apps_container.h index bd0c8fb2c85..834d7e336dc 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -46,6 +46,7 @@ class AppsContainer : public Container, ExamPopUpControllerDelegate, Ion::Storag void setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus newStatus); OnBoarding::PopUpController * promptController(); void redrawWindow(); + void activateExamMode(); // Exam pop-up controller delegate void examDeactivatingPopUpIsDismissed() override; // Ion::StorageDelegate diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index 47bbe60074f..38d6d90cec2 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -13,10 +13,8 @@ ExamPopUpController::ExamPopUpController(ExamPopUpControllerDelegate * delegate) } void ExamPopUpController::setActivatingExamMode(bool activatingExamMode) { - if (m_isActivatingExamMode != activatingExamMode) { - m_isActivatingExamMode = activatingExamMode; - m_contentView.setMessages(activatingExamMode); - } + m_isActivatingExamMode = activatingExamMode; + m_contentView.setMessages(activatingExamMode); } View * ExamPopUpController::view() { @@ -52,13 +50,10 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) : }, parentResponder), KDFont::SmallFont), m_okButton(parentResponder, I18n::Message::Ok, Invocation([](void * context, void * sender) { ExamPopUpController * controller = (ExamPopUpController *)context; - GlobalPreferences::ExamMode nextExamMode = controller->isActivatingExamMode() ? GlobalPreferences::ExamMode::Activate : GlobalPreferences::ExamMode::Deactivate; - GlobalPreferences::sharedGlobalPreferences()->setExamMode(nextExamMode); + GlobalPreferences::sharedGlobalPreferences()->setExamMode(controller->isActivatingExamMode()); AppsContainer * container = AppsContainer::sharedAppsContainer(); if (controller->isActivatingExamMode()) { - container->reset(); - Ion::LED::setColor(KDColorRed); - Ion::LED::setBlinking(1000, 0.1f); + container->activateExamMode(); } else { Ion::LED::setColor(KDColorBlack); Ion::LED::updateColorWithPlugAndCharge(); diff --git a/apps/global_preferences.cpp b/apps/global_preferences.cpp index f509b7a6578..b78102ba91e 100644 --- a/apps/global_preferences.cpp +++ b/apps/global_preferences.cpp @@ -5,6 +5,22 @@ GlobalPreferences * GlobalPreferences::sharedGlobalPreferences() { return &globalPreferences; } +bool GlobalPreferences::examMode() const { + if (m_examMode == ExamMode::Unknown) { + m_examMode = (ExamMode)Ion::ExamMode::FetchExamMode(); + } + assert((int)m_examMode == 0 || (int)m_examMode == 1); + return (bool)m_examMode; +} + +void GlobalPreferences::setExamMode(bool activateExamMode) { + if (((bool)examMode()) == activateExamMode) { + return; + } + Ion::ExamMode::ToggleExamMode(); + m_examMode = (ExamMode)activateExamMode; +} + void GlobalPreferences::setBrightnessLevel(int brightnessLevel) { if (m_brightnessLevel != brightnessLevel) { brightnessLevel = brightnessLevel < 0 ? 0 : brightnessLevel; diff --git a/apps/global_preferences.h b/apps/global_preferences.h index 88a57109fa5..f9fe8e044d5 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -5,15 +5,11 @@ class GlobalPreferences { public: - enum class ExamMode { - Activate, - Deactivate - }; static GlobalPreferences * sharedGlobalPreferences(); I18n::Language language() const { return m_language; } void setLanguage(I18n::Language language) { m_language = language; } - ExamMode examMode() const { return m_examMode; } - void setExamMode(ExamMode examMode) { m_examMode = examMode; } + bool examMode() const; + void setExamMode(bool activateExamMode); bool showPopUp() const { return m_showPopUp; } void setShowPopUp(bool showPopUp) { m_showPopUp = showPopUp; } int brightnessLevel() const { return m_brightnessLevel; } @@ -22,11 +18,18 @@ class GlobalPreferences { private: GlobalPreferences() : m_language(I18n::Language::EN), - m_examMode(ExamMode::Deactivate), + m_examMode(ExamMode::Unknown), m_showPopUp(true), m_brightnessLevel(Ion::Backlight::MaxBrightness) {} I18n::Language m_language; - ExamMode m_examMode; + enum class ExamMode : uint8_t { + Deactivate = 0, + Activate = 1, + Unknown = 2 + }; + static_assert((uint8_t)GlobalPreferences::ExamMode::Deactivate == 0, "GlobalPreferences::setExamMode and examMode() are not right"); + static_assert((uint8_t)GlobalPreferences::ExamMode::Activate == 1, "GlobalPreferences::setExamMode and examMode() are not right"); + mutable ExamMode m_examMode; bool m_showPopUp; int m_brightnessLevel; }; diff --git a/apps/settings/sub_menu/exam_mode_controller.cpp b/apps/settings/sub_menu/exam_mode_controller.cpp index def1a10d3ac..1590f04c4a4 100644 --- a/apps/settings/sub_menu/exam_mode_controller.cpp +++ b/apps/settings/sub_menu/exam_mode_controller.cpp @@ -39,7 +39,7 @@ int ExamModeController::reusableCellCount(int type) { void ExamModeController::willDisplayCellForIndex(HighlightCell * cell, int index) { GenericSubController::willDisplayCellForIndex(cell, index); - if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) { + if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { MessageTableCell * myCell = (MessageTableCell *)cell; myCell->setMessage(I18n::Message::ExamModeActive); } diff --git a/apps/title_bar_view.cpp b/apps/title_bar_view.cpp index 56c7010771b..fc5c23992b3 100644 --- a/apps/title_bar_view.cpp +++ b/apps/title_bar_view.cpp @@ -71,7 +71,7 @@ void TitleBarView::layoutSubviews() { m_preferenceView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, m_preferenceView.minimalSizeForOptimalDisplay().width(), bounds().height())); KDSize batterySize = m_batteryView.minimalSizeForOptimalDisplay(); m_batteryView.setFrame(KDRect(bounds().width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize)); - if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) { + if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { m_examModeIconView.setFrame(KDRect(k_examIconMargin, (bounds().height() - k_examIconHeight)/2, k_examIconWidth, k_examIconHeight)); } else { m_examModeIconView.setFrame(KDRectZero); diff --git a/ion/include/ion.h b/ion/include/ion.h index f9ae2f129a3..4095d4d90ca 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/ion/include/ion/exam_mode.h b/ion/include/ion/exam_mode.h new file mode 100644 index 00000000000..ef0c5c92b9c --- /dev/null +++ b/ion/include/ion/exam_mode.h @@ -0,0 +1,13 @@ +#ifndef ION_EXAM_MODE_H +#define ION_EXAM_MODE_H + +namespace Ion { +namespace ExamMode { + +bool FetchExamMode(); +void ToggleExamMode(); + +} +} + +#endif diff --git a/ion/src/blackbox/Makefile b/ion/src/blackbox/Makefile index 2ac36a1aaa7..a47729da9cc 100644 --- a/ion/src/blackbox/Makefile +++ b/ion/src/blackbox/Makefile @@ -17,6 +17,7 @@ ion_src += $(addprefix ion/src/shared/, \ dummy/battery.cpp \ dummy/display.cpp \ dummy/events_modifier.cpp \ + dummy/exam_mode.cpp \ dummy/fcc_id.cpp \ dummy/led.cpp \ dummy/keyboard.cpp \ diff --git a/ion/src/device/n0100/flash.ld b/ion/src/device/n0100/flash.ld index 487cd2565ce..30afc7b7d36 100644 --- a/ion/src/device/n0100/flash.ld +++ b/ion/src/device/n0100/flash.ld @@ -9,14 +9,16 @@ * This will let us use shortcuts such as ">FLASH" to ask for a given section to * be stored in Flash. */ MEMORY { - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + FLASH_FIRST_SECTOR (rx) : ORIGIN = 0x08000000, LENGTH = 16K + FLASH_SECOND_SECTOR (rx) : ORIGIN = (0x08000000 + 16K), LENGTH = 16K + FLASH_LAST_SECTORS (rx) : ORIGIN = (0x08000000 + 32K), LENGTH = (1024K - 32K) SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K } STACK_SIZE = 32K; SECTIONS { - .isr_vector_table ORIGIN(FLASH) : { + .isr_vector_table ORIGIN(FLASH_FIRST_SECTOR) : { /* When booting, the STM32F412 fetches the content of address 0x0, and * extracts from it various key infos: the initial value of the PC register * (program counter), the initial value of the stack pointer, and various @@ -31,30 +33,37 @@ SECTIONS { * convenient: using function pointers, we can easily point to the service * routine for each interrupt. */ KEEP(*(.isr_vector_table)) - } >FLASH + } >FLASH_FIRST_SECTOR .header : { KEEP(*(.header)) - } >FLASH + } >FLASH_FIRST_SECTOR + + .exam_mode_persistence ORIGIN(FLASH_SECOND_SECTOR): { + _exam_mode_persistence_start = .; + /* Note: We don't increment "." here, we set it. */ + . = (ORIGIN(FLASH_SECOND_SECTOR) + LENGTH(FLASH_SECOND_SECTOR)); + _exam_mode_persistence_end = .; + } >FLASH_SECOND_SECTOR .text : { . = ALIGN(4); *(.text) *(.text.*) - } >FLASH + } >FLASH_LAST_SECTORS .init_array : { . = ALIGN(4); _init_array_start = .; KEEP (*(.init_array*)) _init_array_end = .; - } >FLASH + } >FLASH_LAST_SECTORS .rodata : { . = ALIGN(4); *(.rodata) *(.rodata.*) - } >FLASH + } >FLASH_LAST_SECTORS .data : { /* The data section is written to Flash but linked as if it were in RAM. @@ -75,7 +84,7 @@ SECTIONS { *(.data) *(.data.*) _data_section_end_ram = .; - } >SRAM AT> FLASH + } >SRAM AT> FLASH_LAST_SECTORS .bss : { /* The bss section contains data for all uninitialized variables diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 4b227faf2d5..fa4d9bfb28c 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -7,6 +7,7 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ crc32.cpp \ display.cpp \ events_keyboard_platform.cpp \ + exam_mode.cpp \ external_flash.cpp \ flash.cpp \ keyboard.cpp \ diff --git a/ion/src/device/shared/drivers/exam_mode.cpp b/ion/src/device/shared/drivers/exam_mode.cpp new file mode 100644 index 00000000000..9d9caa32216 --- /dev/null +++ b/ion/src/device/shared/drivers/exam_mode.cpp @@ -0,0 +1,73 @@ +#include +#include "flash.h" + +namespace Ion { +namespace ExamMode { + +extern "C" { + extern char _exam_mode_persistence_start; + extern char _exam_mode_persistence_end; +} + +/* The exam mode is written in flash so that it is resilient to resets. + * We erase the dedicated flash sector (all bits written to 1) and, upon + * activating or deactivating the exam mode we write one bit to 0. To determine + * if we are in exam mode, we count the number of leading 0 bits. If it is even, + * the exam mode is deactivated, if it is odd, the exam mode is activated. */ + +/* significantExamModeAddress returns the first uint32_t * in the exam mode + * flash sector that does not point to 0. If this flash sector has only 0s, it + * is erased (to 1) and significantExamModeAddress returns the start of the + * sector. */ + +uint32_t * SignificantExamModeAddress() { + uint32_t * persitence_start = (uint32_t *)&_exam_mode_persistence_start; + uint32_t * persitence_end = (uint32_t *)&_exam_mode_persistence_end; + while (persitence_start < persitence_end && *persitence_start == 0x0) { + // Skip even number of zero bits + persitence_start++; + } + if (persitence_start == persitence_end) { + assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start) >= 0); + Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start)); + return (uint32_t *)&_exam_mode_persistence_start; + } + return persitence_start; +} + +size_t firstOneBit(int i, size_t size) { + int minShift = 0; + int maxShift = size; + while (maxShift > minShift+1) { + int shift = (minShift + maxShift)/2; + int shifted = i >> shift; + if (shifted == 0) { + maxShift = shift; + } else { + minShift = shift; + } + } + return maxShift; +} + +bool FetchExamMode() { + uint32_t * readingAddress = SignificantExamModeAddress(); + size_t numberOfLeading0 = 32 - firstOneBit(*readingAddress, 32); + return numberOfLeading0 % 2 == 1; +} + +void ToggleExamMode() { + uint32_t * writingAddress = SignificantExamModeAddress(); + assert(*writingAddress != 0); + // Compute the new value with one bit switched + uint8_t numberOfLeadingZeroes = 32 - firstOneBit(*writingAddress, 32); + /* When writing in flash, we can only switch a 1 to a 0. If we want to switch + * the fifth bit in a byte, we can thus write "11110111". */ + uint32_t newValue = ~(1 << (31 - numberOfLeadingZeroes)); + + // Write the value in flash + Ion::Device::Flash::WriteMemory((uint8_t *)writingAddress, (uint8_t *)&newValue, sizeof(uint32_t)); +} + +} +} diff --git a/ion/src/emscripten/Makefile b/ion/src/emscripten/Makefile index 3f3d8e85d0d..0ecc32c6ffe 100644 --- a/ion/src/emscripten/Makefile +++ b/ion/src/emscripten/Makefile @@ -14,6 +14,7 @@ ion_src += $(addprefix ion/src/shared/, \ dummy/backlight.cpp \ dummy/battery.cpp \ dummy/display.cpp \ + dummy/exam_mode.cpp \ dummy/fcc_id.cpp \ dummy/led.cpp \ dummy/serial_number.cpp \ diff --git a/ion/src/sdl/Makefile b/ion/src/sdl/Makefile index c8cc23984ac..9bda513122d 100644 --- a/ion/src/sdl/Makefile +++ b/ion/src/sdl/Makefile @@ -10,6 +10,7 @@ ion_src += $(addprefix ion/src/shared/, \ dummy/backlight.cpp \ dummy/battery.cpp \ dummy/display.cpp \ + dummy/exam_mode.cpp \ dummy/fcc_id.cpp \ dummy/led.cpp \ dummy/serial_number.cpp \ diff --git a/ion/src/shared/dummy/exam_mode.cpp b/ion/src/shared/dummy/exam_mode.cpp new file mode 100644 index 00000000000..02d78e591cd --- /dev/null +++ b/ion/src/shared/dummy/exam_mode.cpp @@ -0,0 +1,14 @@ +#include + +namespace Ion { +namespace ExamMode { + +bool FetchExamMode() { + return false; +} + +void ToggleExamMode() { +} + +} +} diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 6b9968ae1fd..92a52e4c113 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -17,6 +17,7 @@ ion_src += $(addprefix ion/src/shared/, \ dummy/backlight.cpp \ dummy/battery.cpp \ dummy/display.cpp \ + dummy/exam_mode.cpp \ dummy/fcc_id.cpp \ dummy/led.cpp \ dummy/serial_number.cpp \