Skip to content

Commit

Permalink
[apps/ion/exam_mode] Store the exam mode activation in the flash
Browse files Browse the repository at this point in the history
This way, it is not cleared by a reset
  • Loading branch information
LeaNumworks committed Dec 11, 2019
1 parent 002e746 commit 418a721
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 29 deletions.
13 changes: 11 additions & 2 deletions apps/apps_container.cpp
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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. */
Expand Down Expand 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();
Expand Down
1 change: 1 addition & 0 deletions apps/apps_container.h
Expand Up @@ -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
Expand Down
13 changes: 4 additions & 9 deletions apps/exam_pop_up_controller.cpp
Expand Up @@ -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() {
Expand Down Expand Up @@ -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();
Expand Down
16 changes: 16 additions & 0 deletions apps/global_preferences.cpp
Expand Up @@ -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;
Expand Down
19 changes: 11 additions & 8 deletions apps/global_preferences.h
Expand Up @@ -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; }
Expand All @@ -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;
};
Expand Down
2 changes: 1 addition & 1 deletion apps/settings/sub_menu/exam_mode_controller.cpp
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/title_bar_view.cpp
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions ion/include/ion.h
Expand Up @@ -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>
Expand Down
13 changes: 13 additions & 0 deletions 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
1 change: 1 addition & 0 deletions ion/src/blackbox/Makefile
Expand Up @@ -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 \
Expand Down
25 changes: 17 additions & 8 deletions ion/src/device/n0100/flash.ld
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions ion/src/device/shared/drivers/Makefile
Expand Up @@ -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 \
Expand Down
73 changes: 73 additions & 0 deletions ion/src/device/shared/drivers/exam_mode.cpp
@@ -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));
}

}
}
1 change: 1 addition & 0 deletions ion/src/emscripten/Makefile
Expand Up @@ -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 \
Expand Down
1 change: 1 addition & 0 deletions ion/src/sdl/Makefile
Expand Up @@ -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 \
Expand Down
14 changes: 14 additions & 0 deletions ion/src/shared/dummy/exam_mode.cpp
@@ -0,0 +1,14 @@
#include <ion/exam_mode.h>

namespace Ion {
namespace ExamMode {

bool FetchExamMode() {
return false;
}

void ToggleExamMode() {
}

}
}
1 change: 1 addition & 0 deletions ion/src/simulator/Makefile
Expand Up @@ -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 \
Expand Down

0 comments on commit 418a721

Please sign in to comment.