mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[apps/ion/exam_mode] Store the exam mode activation in the flash
This way, it is not cleared by a reset
This commit is contained in:
committed by
EmilieNumworks
parent
e550005e43
commit
ebc5843795
@@ -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();
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
void setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus newStatus);
|
||||
OnBoarding::PopUpController * promptController();
|
||||
void redrawWindow();
|
||||
void activateExamMode();
|
||||
// Exam pop-up controller delegate
|
||||
void examDeactivatingPopUpIsDismissed() override;
|
||||
// Ion::StorageDelegate
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 @@ public:
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <ion/console.h>
|
||||
#include <ion/display.h>
|
||||
#include <ion/events.h>
|
||||
#include <ion/exam_mode.h>
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/power.h>
|
||||
|
||||
13
ion/include/ion/exam_mode.h
Normal file
13
ion/include/ion/exam_mode.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef ION_EXAM_MODE_H
|
||||
#define ION_EXAM_MODE_H
|
||||
|
||||
namespace Ion {
|
||||
namespace ExamMode {
|
||||
|
||||
bool FetchExamMode();
|
||||
void ToggleExamMode();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,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 \
|
||||
|
||||
73
ion/src/device/shared/drivers/exam_mode.cpp
Normal file
73
ion/src/device/shared/drivers/exam_mode.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <ion/exam_mode.h>
|
||||
#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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
14
ion/src/shared/dummy/exam_mode.cpp
Normal file
14
ion/src/shared/dummy/exam_mode.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <ion/exam_mode.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace ExamMode {
|
||||
|
||||
bool FetchExamMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToggleExamMode() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user