From e8a1bc6149d6c9c49b526d2e5c4bad80f3e2cb37 Mon Sep 17 00:00:00 2001 From: devdl11 <54149885+devdl11@users.noreply.github.com> Date: Thu, 11 Nov 2021 18:20:16 +0100 Subject: [PATCH] Creation of a protection system against unintentional updates to Epsilon 16 (foundation) + Recovery Improvement (#37) --- apps/apps_container.cpp | 6 +- apps/global_preferences.h | 18 + apps/home/base.de.i18n | 2 + apps/home/base.en.i18n | 2 + apps/home/base.es.i18n | 2 + apps/home/base.fr.i18n | 2 + apps/home/base.hu.i18n | 2 + apps/home/base.it.i18n | 2 + apps/home/base.nl.i18n | 2 + apps/home/base.pt.i18n | 2 + apps/home/controller.cpp | 24 + apps/on_boarding/logo_icon.png | Bin 10643 -> 10079 bytes apps/settings/Makefile | 2 + apps/settings/main_controller.cpp | 7 +- apps/settings/main_controller.h | 4 + apps/settings/main_controller_prompt_beta.cpp | 1 + apps/settings/main_controller_prompt_none.cpp | 28 +- .../main_controller_prompt_update.cpp | 1 + .../settings/sub_menu/usb_info_controller.cpp | 134 +++ apps/settings/sub_menu/usb_info_controller.h | 36 + .../usb_protection_level_controller.cpp | 77 ++ .../usb_protection_level_controller.h | 24 + apps/shared.de.i18n | 12 + apps/shared.en.i18n | 12 + apps/shared.es.i18n | 12 + apps/shared.fr.i18n | 12 + apps/shared.hu.i18n | 12 + apps/shared.it.i18n | 12 + apps/shared.nl.i18n | 12 + apps/shared.pt.i18n | 12 + apps/shared.universal.i18n | 5 + apps/usb/Makefile | 1 + apps/usb/app.cpp | 1 + apps/usb/base.de.i18n | 6 + apps/usb/base.en.i18n | 6 + apps/usb/base.es.i18n | 8 +- apps/usb/base.fr.i18n | 6 + apps/usb/base.hu.i18n | 6 + apps/usb/base.it.i18n | 6 + apps/usb/base.nl.i18n | 6 + apps/usb/base.pt.i18n | 6 + apps/usb/usb_connected_controller.cpp | 170 +++- apps/usb/usb_connected_controller.h | 9 +- apps/usb/usb_view.cpp | 56 ++ apps/usb/usb_view.h | 30 + build/device/secure_ext.py | 29 + build/targets.device.mak | 11 +- ion/include/ion/usb.h | 2 +- ion/src/device/flasher/main.cpp | 2 +- ion/src/device/n0110/drivers/board.cpp | 6 +- ion/src/device/n0110/flash.ld | 167 ++++ ion/src/device/shared/boot/isr.c | 6 +- ion/src/device/shared/boot/isr.h | 8 + ion/src/device/shared/boot/rt0.cpp | 251 ++++-- ion/src/device/shared/usb/calculator.cpp | 7 +- ion/src/device/shared/usb/calculator.h | 2 +- ion/src/device/shared/usb/dfu_interface.cpp | 804 +++++++++++------- ion/src/device/shared/usb/dfu_interface.h | 352 ++++---- ion/src/device/shared/usb/dfu_relocated.cpp | 6 +- ion/src/device/shared/usb/dfu_xip.cpp | 4 +- ion/src/shared/dummy/usb.cpp | 2 +- ion/src/simulator/3ds/driver/usb.cpp | 2 +- kandinsky/fonts/code_points.h | 1 + 63 files changed, 1893 insertions(+), 565 deletions(-) create mode 100644 apps/settings/sub_menu/usb_info_controller.cpp create mode 100644 apps/settings/sub_menu/usb_info_controller.h create mode 100644 apps/settings/sub_menu/usb_protection_level_controller.cpp create mode 100644 apps/settings/sub_menu/usb_protection_level_controller.h create mode 100644 apps/usb/usb_view.cpp create mode 100644 apps/usb/usb_view.h create mode 100644 build/device/secure_ext.py diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 3963d8c7d..dcc8539ba 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -138,7 +138,7 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) { Ion::LED::updateColorWithPlugAndCharge(); } if (event == Ion::Events::USBEnumeration) { - if (Ion::USB::isPlugged()) { + if (Ion::USB::isPlugged() && GlobalPreferences::sharedGlobalPreferences()->getDfuLevel() != 3) { App::Snapshot * activeSnapshot = (s_activeApp == nullptr ? appSnapshotAtIndex(0) : s_activeApp->snapshot()); /* Just after a software update, the battery timer does not have time to * fire before the calculator enters DFU mode. As the DFU mode blocks the @@ -147,7 +147,9 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) { * pictogram. */ updateBatteryState(); if (switchTo(usbConnectedAppSnapshot())) { - Ion::USB::DFU(); + Ion::USB::DFU(true, GlobalPreferences::sharedGlobalPreferences()->dfuStatus(), GlobalPreferences::sharedGlobalPreferences()->getDfuLevel()); + GlobalPreferences::sharedGlobalPreferences()->dfuResetStep(); + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(false); // Update LED when exiting DFU mode Ion::LED::updateColorWithPlugAndCharge(); bool switched = switchTo(activeSnapshot); diff --git a/apps/global_preferences.h b/apps/global_preferences.h index 641e48792..5345bd21c 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -30,6 +30,15 @@ public: void setTempExamMode(ExamMode examMode); bool showPopUp() const { return m_showPopUp; } void setShowPopUp(bool showPopUp) { m_showPopUp = showPopUp; } + bool dfuStatus() const { return m_dfuUnlocked; } + void setDfuStatus(bool status) { m_dfuUnlocked=status; } + int dfuCurrentStep() const { return m_dfuStep; } + void dfuIncreaseStep() { m_dfuStep++; } + void dfuResetStep() { m_dfuStep = 0; } + int getDfuLevel() const { return m_dfuProtectLevel; } + void setDfuLevel(int level) { m_dfuProtectLevel = level; } + bool showDfuDeacAlert() const { return m_showDeacAlert; } + void setDfuDeacAlert(bool value) { m_showDeacAlert = value; } bool autocomplete() const { return m_autoComplete; } void setAutocomplete(bool autocomple) { m_autoComplete = autocomple; } int brightnessLevel() const { return m_brightnessLevel; } @@ -37,6 +46,7 @@ public: const KDFont * font() const { return m_font; } void setFont(const KDFont * font) { m_font = font; } constexpr static int NumberOfBrightnessStates = 15; + constexpr static int DfuUnlockStep = 3; private: static_assert(I18n::NumberOfLanguages > 0, "I18n::NumberOfLanguages is not superior to 0"); // There should already have been an error when processing an empty EPSILON_I18N flag static_assert(I18n::NumberOfCountries > 0, "I18n::NumberOfCountries is not superior to 0"); // There should already have been an error when processing an empty EPSILON_COUNTRIES flag @@ -46,6 +56,10 @@ private: m_examMode(ExamMode::Unknown), m_tempExamMode(ExamMode::Standard), m_showPopUp(true), + m_dfuUnlocked(false), + m_dfuStep(0), + m_dfuProtectLevel(0), + m_showDeacAlert(true), m_autoComplete(true), m_brightnessLevel(Ion::Backlight::MaxBrightness), m_font(KDFont::LargeFont) {} @@ -56,6 +70,10 @@ private: mutable ExamMode m_examMode; mutable ExamMode m_tempExamMode; bool m_showPopUp; + bool m_dfuUnlocked; + int m_dfuStep; + int m_dfuProtectLevel; // 0: default; 1: OmegaMode; 2: Paranoid; 3: Paranoid++ + bool m_showDeacAlert; bool m_autoComplete; int m_brightnessLevel; const KDFont * m_font; diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index a0fc91420..5b9d53782 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -2,3 +2,5 @@ Apps = "Anwendungen" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Diese Anwendung ist im" ForbidenAppInExamMode2 = "Prüfungsmodus nicht erlaubt." +DfuWarning1 = "DFU-Schutzwarnung" +DfuWarning2 = "Mehr Informationen: bit.ly/upsiDfu" diff --git a/apps/home/base.en.i18n b/apps/home/base.en.i18n index 3468cca66..81e3e0914 100644 --- a/apps/home/base.en.i18n +++ b/apps/home/base.en.i18n @@ -2,3 +2,5 @@ Apps = "Applications" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" +DfuWarning1 = "DFU Protection Warning" +DfuWarning2 = "More informations: bit.ly/upsiDfu" diff --git a/apps/home/base.es.i18n b/apps/home/base.es.i18n index 3c262497c..12bcd7288 100644 --- a/apps/home/base.es.i18n +++ b/apps/home/base.es.i18n @@ -2,3 +2,5 @@ Apps = "Aplicaciones" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Esta aplicación está prohibida" ForbidenAppInExamMode2 = "en el modo de examen" +DfuWarning1 = "Advertencia de protección DFU" +DfuWarning2 = "Más información: bit.ly/upsiDfu" diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n index 4b4ab02dd..aba1412ef 100644 --- a/apps/home/base.fr.i18n +++ b/apps/home/base.fr.i18n @@ -2,3 +2,5 @@ Apps = "Applications" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Cette application n'est" ForbidenAppInExamMode2 = "pas autorisée en mode examen." +DfuWarning1 = "Alerte protection DFU" +DfuWarning2 = "Plus d'infos: bit.ly/upsiDfu" diff --git a/apps/home/base.hu.i18n b/apps/home/base.hu.i18n index d526193eb..2bcc9fd6e 100644 --- a/apps/home/base.hu.i18n +++ b/apps/home/base.hu.i18n @@ -2,3 +2,5 @@ Apps = "Alkalmazások" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Ez az alkalmazás" ForbidenAppInExamMode2 = "tilos vizsga módban" +DfuWarning1 = "DFU védelmi figyelmeztetés" +DfuWarning2 = "További információk: bit.ly/upsiDfu" diff --git a/apps/home/base.it.i18n b/apps/home/base.it.i18n index 4bd26ced3..09ae09d43 100644 --- a/apps/home/base.it.i18n +++ b/apps/home/base.it.i18n @@ -2,3 +2,5 @@ Apps = "Applicazioni" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Questa applicazione è" ForbidenAppInExamMode2 = "proibita nella modalità d'esame" +DfuWarning1 = "Avviso protezione DFU" +DfuWarning2 = "Più informazioni: bit.ly/upsiDfu" diff --git a/apps/home/base.nl.i18n b/apps/home/base.nl.i18n index 496e8d6a3..9037187aa 100644 --- a/apps/home/base.nl.i18n +++ b/apps/home/base.nl.i18n @@ -2,3 +2,5 @@ Apps = "Applicaties" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Deze applicatie is" ForbidenAppInExamMode2 = "uitgesloten in examenstand" +DfuWarning1 = "DFU-beveiligingswaarschuwing" +DfuWarning2 = "Meer informatie: bit.ly/upsiDfu" diff --git a/apps/home/base.pt.i18n b/apps/home/base.pt.i18n index 6c074aa86..bf1e307e4 100644 --- a/apps/home/base.pt.i18n +++ b/apps/home/base.pt.i18n @@ -2,3 +2,5 @@ Apps = "Aplicações" AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Esta aplicação é" ForbidenAppInExamMode2 = "proibida no Modo de Exame" +DfuWarning1 = "Aviso de proteção DFU" +DfuWarning2 = "Mais informações: bit.ly/upsiDfu" diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index df725f10f..8f02cc5b8 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -87,9 +87,33 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc } bool Controller::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::Six) { + GlobalPreferences::sharedGlobalPreferences()->dfuIncreaseStep(); + if (GlobalPreferences::sharedGlobalPreferences()->dfuCurrentStep() >= GlobalPreferences::DfuUnlockStep && !GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) { + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + Ion::LED::setColor(KDColorPurple); + Ion::LED::setBlinking(500, 0.5f); + } + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(true); + App::app()->displayWarning(I18n::Message::DfuWarning1, I18n::Message::DfuWarning2); + return true; + } else if (GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) { + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + Ion::LED::setColor(KDColorBlack); + } + GlobalPreferences::sharedGlobalPreferences()->dfuResetStep(); + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(false); + } + } if (event == Ion::Events::OK || event == Ion::Events::EXE) { AppsContainer * container = AppsContainer::sharedAppsContainer(); + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + Ion::LED::setColor(KDColorBlack); + } + GlobalPreferences::sharedGlobalPreferences()->dfuResetStep(); + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(false); + int index = selectionDataSource()->selectedRow()*k_numberOfColumns+selectionDataSource()->selectedColumn()+1; #ifdef HOME_DISPLAY_EXTERNALS if (index >= container->numberOfApps()) { diff --git a/apps/on_boarding/logo_icon.png b/apps/on_boarding/logo_icon.png index 0dc9c6174e307ea72fa48608abc14812106af16e..bfec4d823a249e04efb033f969e2fd467fb40be6 100644 GIT binary patch literal 10079 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 JPDHLkV1l0JjZ6Rl diff --git a/apps/settings/Makefile b/apps/settings/Makefile index e0598ec4e..ec6684d1a 100644 --- a/apps/settings/Makefile +++ b/apps/settings/Makefile @@ -25,6 +25,8 @@ app_settings_src = $(addprefix apps/settings/,\ sub_menu/contributors_controller.cpp \ sub_menu/math_options_controller.cpp \ sub_menu/selectable_view_with_messages.cpp \ + sub_menu/usb_info_controller.cpp \ + sub_menu/usb_protection_level_controller.cpp \ ) SFLAGS += -DOMEGA_STATE="$(OMEGA_STATE)" diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index 01145af4e..e28bab689 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -15,6 +15,8 @@ constexpr SettingsMessageTree s_modelFloatDisplayModeChildren[4] = {SettingsMess constexpr SettingsMessageTree s_modelComplexFormatChildren[3] = {SettingsMessageTree(I18n::Message::Real), SettingsMessageTree(I18n::Message::Cartesian), SettingsMessageTree(I18n::Message::Polar)}; constexpr SettingsMessageTree s_modelDateTimeChildren[3] = {SettingsMessageTree(I18n::Message::ActivateClock), SettingsMessageTree(I18n::Message::Date), SettingsMessageTree(I18n::Message::Time)}; constexpr SettingsMessageTree s_symbolChildren[4] = {SettingsMessageTree(I18n::Message::SymbolMultiplicationCross),SettingsMessageTree(I18n::Message::SymbolMultiplicationMiddleDot),SettingsMessageTree(I18n::Message::SymbolMultiplicationStar),SettingsMessageTree(I18n::Message::SymbolMultiplicationAutoSymbol)}; +constexpr SettingsMessageTree s_usbLevelSelector[3] = {SettingsMessageTree(I18n::Message::USBDefaultLevel), SettingsMessageTree(I18n::Message::USBLowLevel), SettingsMessageTree(I18n::Message::USBParanoidLevel)}; // , SettingsMessageTree(I18n::Message::USBMegaParanoidLevel) +constexpr SettingsMessageTree s_usbSteps[2] = {SettingsMessageTree(I18n::Message::USBProtection), SettingsMessageTree(I18n::Message::USBLevelProtect, s_usbLevelSelector)}; constexpr SettingsMessageTree s_symbolFunctionChildren[3] = {SettingsMessageTree(I18n::Message::SymbolDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgDefaultFunction), SettingsMessageTree(I18n::Message::SymbolArgFunction)}; constexpr SettingsMessageTree s_modelMathOptionsChildren[6] = {SettingsMessageTree(I18n::Message::AngleUnit, s_modelAngleChildren), SettingsMessageTree(I18n::Message::DisplayMode, s_modelFloatDisplayModeChildren), SettingsMessageTree(I18n::Message::EditionMode, s_modelEditionModeChildren), SettingsMessageTree(I18n::Message::SymbolFunction, s_symbolFunctionChildren), SettingsMessageTree(I18n::Message::ComplexFormat, s_modelComplexFormatChildren), SettingsMessageTree(I18n::Message::SymbolMultiplication, s_symbolChildren)}; constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)}; @@ -41,7 +43,8 @@ MainController::MainController(Responder * parentResponder, InputEventHandlerDel m_codeOptionsController(this), m_examModeController(this), m_aboutController(this), - m_preferencesController(this) + m_preferencesController(this), + m_usbInfoController(this) { for (int i = 0; i < k_numberOfSimpleChevronCells; i++) { m_cells[i].setMessageFont(KDFont::LargeFont); @@ -111,6 +114,8 @@ bool MainController::handleEvent(Ion::Events::Event event) { subController = &m_dateTimeController; } else if (title == I18n::Message::MathOptions) { subController = &m_mathOptionsController; + } else if (title == I18n::Message::UsbSetting) { + subController = &m_usbInfoController; } else if (title == I18n::Message::CodeApp) { subController = &m_codeOptionsController; } else { diff --git a/apps/settings/main_controller.h b/apps/settings/main_controller.h index c254fb8d1..2d7cec4b1 100644 --- a/apps/settings/main_controller.h +++ b/apps/settings/main_controller.h @@ -12,6 +12,7 @@ #include "sub_menu/localization_controller.h" #include "sub_menu/math_options_controller.h" #include "sub_menu/preferences_controller.h" +#include "sub_menu/usb_info_controller.h" namespace Settings { @@ -28,6 +29,8 @@ extern const Shared::SettingsMessageTree s_modelDateTimeChildren[3]; extern const Shared::SettingsMessageTree s_accessibilityChildren[6]; extern const Shared::SettingsMessageTree s_contributorsChildren[23]; extern const Shared::SettingsMessageTree s_modelAboutChildren[9]; +extern const Shared::SettingsMessageTree s_usbLevelSelector[3]; +extern const Shared::SettingsMessageTree s_usbSteps[2]; extern const Shared::SettingsMessageTree s_model; class MainController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource { @@ -76,6 +79,7 @@ private: ExamModeController m_examModeController; AboutController m_aboutController; PreferencesController m_preferencesController; + UsbInfoController m_usbInfoController; }; } diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp index 1b5f88143..7c4f4acf3 100644 --- a/apps/settings/main_controller_prompt_beta.cpp +++ b/apps/settings/main_controller_prompt_beta.cpp @@ -18,6 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] = #endif SettingsMessageTree(I18n::Message::BetaPopUp), SettingsMessageTree(I18n::Message::About, s_modelAboutChildren), + SettingsMessageTree(I18n::Message::UsbSetting, s_usbSteps), SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren)}; constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu); diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp index e3e129051..95c0b571e 100644 --- a/apps/settings/main_controller_prompt_none.cpp +++ b/apps/settings/main_controller_prompt_none.cpp @@ -1,28 +1,30 @@ -#include "main_controller.h" -#include "../exam_mode_configuration.h" #include +#include "../exam_mode_configuration.h" +#include "main_controller.h" + using namespace Shared; namespace Settings { constexpr SettingsMessageTree s_modelMenu[] = - {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), - SettingsMessageTree(I18n::Message::Brightness), - SettingsMessageTree(I18n::Message::DateTime, s_modelDateTimeChildren), - SettingsMessageTree(I18n::Message::Language), - SettingsMessageTree(I18n::Message::Country), - SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren), + {SettingsMessageTree(I18n::Message::MathOptions, s_modelMathOptionsChildren), + SettingsMessageTree(I18n::Message::Brightness), + SettingsMessageTree(I18n::Message::DateTime, s_modelDateTimeChildren), + SettingsMessageTree(I18n::Message::Language), + SettingsMessageTree(I18n::Message::Country), + SettingsMessageTree(I18n::Message::ExamMode, ExamModeConfiguration::s_modelExamChildren), #ifdef HAS_CODE - SettingsMessageTree(I18n::Message::CodeApp, s_codeChildren), + SettingsMessageTree(I18n::Message::CodeApp, s_codeChildren), #endif - SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren), - SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)}; + SettingsMessageTree(I18n::Message::UsbSetting, s_usbSteps), + SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren), + SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)}; constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu); I18n::Message MainController::promptMessage() const { - return I18n::Message::Default; + return I18n::Message::Default; } -} +} // namespace Settings diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp index acbb08d46..148f5c16d 100644 --- a/apps/settings/main_controller_prompt_update.cpp +++ b/apps/settings/main_controller_prompt_update.cpp @@ -18,6 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] = #endif SettingsMessageTree(I18n::Message::UpdatePopUp), SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren), + SettingsMessageTree(I18n::Message::UsbSetting, s_usbSteps), SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)}; constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu); diff --git a/apps/settings/sub_menu/usb_info_controller.cpp b/apps/settings/sub_menu/usb_info_controller.cpp new file mode 100644 index 000000000..f31bd3f82 --- /dev/null +++ b/apps/settings/sub_menu/usb_info_controller.cpp @@ -0,0 +1,134 @@ +#include "usb_info_controller.h" + +#include +#include +#include +#include +#include + +#include + +#include "../../apps_container.h" +#include "../../global_preferences.h" + +using namespace Poincare; +using namespace Shared; + +namespace Settings { + +UsbInfoController::UsbInfoController(Responder *parentResponder) : GenericSubController(parentResponder), + m_usbprotectlevel(this), + m_dfuLevel(KDFont::LargeFont, KDFont::SmallFont), + m_contentView(&m_selectableTableView) { + for (int i = 0; i < k_maxSwitchCells; i++) { + m_switchCells[i].setMessageFont(KDFont::LargeFont); + //Ancien code au cas ou on souhaite ajouter d'autres éléments dans le menu + // m_cell[i].setMessageFont(KDFont::LargeFont); + // m_cell[i].setAccessoryFont(KDFont::SmallFont); + // m_cell[i].setAccessoryTextColor(Palette::SecondaryText); + } +} + +bool UsbInfoController::handleEvent(Ion::Events::Event event) { + if ((Ion::Events::OK == event || Ion::Events::EXE == event) && selectedRow() == 0) { + if(GlobalPreferences::sharedGlobalPreferences()->showDfuDeacAlert()){ + GlobalPreferences::sharedGlobalPreferences()->setDfuDeacAlert(false); + Container::activeApp()->displayWarning(I18n::Message::USBDeacAlert1, I18n::Message::USBDeacAlert2); + return true; + } + if (!GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) { + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + Ion::LED::setColor(KDColorPurple); + Ion::LED::setBlinking(500, 0.5f); + } + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(true); + Container::activeApp()->displayWarning(I18n::Message::DfuWarning1, I18n::Message::DfuWarning2); + } else { + if (!GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { + Ion::LED::setColor(KDColorBlack); + } + GlobalPreferences::sharedGlobalPreferences()->setDfuStatus(false); + } + m_selectableTableView.reloadCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); + AppsContainer::sharedAppsContainer()->redrawWindow(true); + return true; + } + + if (GlobalPreferences::sharedGlobalPreferences()->dfuStatus() && event != Ion::Events::USBPlug && event != Ion::Events::USBEnumeration) { + Container::activeApp()->displayWarning(I18n::Message::UsbSetting, I18n::Message::USBSettingDeact); + return true; + } + + if ((Ion::Events::OK == event || Ion::Events::EXE == event) && selectedRow() == 1) { + GenericSubController *subController = &m_usbprotectlevel; + subController->setMessageTreeModel(m_messageTreeModel->childAtIndex(1)); + StackViewController *stack = stackController(); + m_lastSelect = selectedRow(); + stack->push(subController); + return true; + } + GlobalPreferences::sharedGlobalPreferences()->setDfuDeacAlert(true); + return GenericSubController::handleEvent(event); +} + +HighlightCell *UsbInfoController::reusableCell(int index, int type) { + assert(type == 2 || type == 1); + if (type == 2) { + assert(index >= 0 && index < k_maxSwitchCells); + return &m_switchCells[index]; + } + return &m_dfuLevel; +} + +int UsbInfoController::reusableCellCount(int type) { + assert(type == 2 || type == 1); + if (type == 2) { + return k_maxSwitchCells; + } + return 1; +} + +void UsbInfoController::willDisplayCellForIndex(HighlightCell *cell, int index) { + GenericSubController::willDisplayCellForIndex(cell, index); + + if (index == 0) { + MessageTableCellWithSwitch *myCell = (MessageTableCellWithSwitch *)cell; + SwitchView *mySwitch = (SwitchView *)myCell->accessoryView(); + mySwitch->setState(!GlobalPreferences::sharedGlobalPreferences()->dfuStatus()); + } else if (index == 1) { + MessageTableCellWithChevronAndMessage *mcell = (MessageTableCellWithChevronAndMessage *)cell; + int currentLevel = GlobalPreferences::sharedGlobalPreferences()->getDfuLevel(); + if (currentLevel == 0) { + // mcell->setSubtitle(I18n::Message::USBDefaultLevel); + mcell->setSubtitle(I18n::Message::USBDefaultLevelDesc); + } else if (currentLevel == 1) { + // mcell->setSubtitle(I18n::Message::USBLowLevel); + mcell->setSubtitle(I18n::Message::USBLowLevelDesc); + } else if (currentLevel == 2) { + // mcell->setSubtitle(I18n::Message::USBParanoidLevel); + mcell->setSubtitle(I18n::Message::USBParanoidLevelDesc); + } else { + // mcell->setSubtitle(I18n::Message::USBMegaParanoidLevel); + mcell->setSubtitle(I18n::Message::USBMegaParanoidLevelDesc); + } + } +} + +int UsbInfoController::typeAtLocation(int i, int j) { + switch (j) { + case 0: + return 2; + default: + return 1; + } +} + +void UsbInfoController::didEnterResponderChain(Responder *previousFirstResponder) { + m_contentView.reload(); + + if (numberOfInfoLines() > 0) { + I18n::Message infoMessages[] = {I18n::Message::USBE16_expl1, I18n::Message::USBE16_expl2, I18n::Message::USBE16_expl3}; + m_contentView.setMessages(infoMessages, numberOfInfoLines()); + } +} +} diff --git a/apps/settings/sub_menu/usb_info_controller.h b/apps/settings/sub_menu/usb_info_controller.h new file mode 100644 index 000000000..1bd602883 --- /dev/null +++ b/apps/settings/sub_menu/usb_info_controller.h @@ -0,0 +1,36 @@ +#ifndef SETTINGS_USB_INFO_CONTROLLER_H +#define SETTINGS_USB_INFO_CONTROLLER_H + +#include "generic_sub_controller.h" +#include "preferences_controller.h" +#include "selectable_view_with_messages.h" +#include "usb_protection_level_controller.h" + +namespace Settings { + +class UsbInfoController : public GenericSubController { + public: + UsbInfoController(Responder* parentResponder); + View* view() override { return &m_contentView; } + bool handleEvent(Ion::Events::Event event) override; + TELEMETRY_ID("UsbInfo"); + void didEnterResponderChain(Responder* previousFirstResponder) override; + + HighlightCell* reusableCell(int index, int type) override; + int reusableCellCount(int type) override; + void willDisplayCellForIndex(HighlightCell* cell, int index) override; + int typeAtLocation(int i, int j) override; + + private: + static constexpr int k_numberOfInfoE16MessageLines = 3; + int numberOfInfoLines() const { return k_numberOfInfoE16MessageLines; }; + static constexpr int k_maxSwitchCells = 1; + MessageTableCellWithSwitch m_switchCells[k_maxSwitchCells]; + UsbProtectionLevelController m_usbprotectlevel; + MessageTableCellWithChevronAndMessage m_dfuLevel; + SelectableViewWithMessages m_contentView; +}; + +} + +#endif diff --git a/apps/settings/sub_menu/usb_protection_level_controller.cpp b/apps/settings/sub_menu/usb_protection_level_controller.cpp new file mode 100644 index 000000000..6959b742a --- /dev/null +++ b/apps/settings/sub_menu/usb_protection_level_controller.cpp @@ -0,0 +1,77 @@ +#include "usb_protection_level_controller.h" + +#include +#include + +#include "../../apps_container.h" +#include "../../global_preferences.h" + +using namespace Poincare; +using namespace Shared; + +namespace Settings { +UsbProtectionLevelController::UsbProtectionLevelController(Responder *parentResponder) : GenericSubController(parentResponder) { + for (int i = 0; i < k_maxNumberOfCells; i++) { + m_cell[i].setMessageFont(KDFont::LargeFont); + m_cell[i].setAccessoryFont(KDFont::SmallFont); + } +} + +bool UsbProtectionLevelController::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::OK || event == Ion::Events::EXE) { + for (int i = 0; i < k_maxNumberOfCells; i++) { + m_cell[i].setAccessoryText(""); + } + if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::USBLowLevel) { + GlobalPreferences::sharedGlobalPreferences()->setDfuLevel(1); + } else if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::USBDefaultLevel) { + GlobalPreferences::sharedGlobalPreferences()->setDfuLevel(0); + } else if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::USBParanoidLevel) { + GlobalPreferences::sharedGlobalPreferences()->setDfuLevel(2); + } else if (m_messageTreeModel->childAtIndex(selectedRow())->label() == I18n::Message::USBMegaParanoidLevel) { + GlobalPreferences::sharedGlobalPreferences()->setDfuLevel(3); + } + + StackViewController * stack = stackController(); + stack->pop(); + return true; + } else { + return GenericSubController::handleEvent(event); + } +} + +HighlightCell *UsbProtectionLevelController::reusableCell(int index, int type) { + assert(index >= 0 && index < k_maxNumberOfCells); + return &m_cell[index]; +} + +int UsbProtectionLevelController::reusableCellCount(int type) { + return k_maxNumberOfCells; +} + +void UsbProtectionLevelController::willDisplayCellForIndex(HighlightCell *cell, int index) { + GenericSubController::willDisplayCellForIndex(cell, index); + I18n::Message childLabel = m_messageTreeModel->childAtIndex(index)->label(); + MessageTableCellWithBuffer *messageComp = (MessageTableCellWithBuffer *)cell; + + int currentLevel = GlobalPreferences::sharedGlobalPreferences()->getDfuLevel(); + + if (childLabel == I18n::Message::USBLowLevel && currentLevel == 1) { + // messageComp->setTextColor(Palette::Green); + messageComp->setAccessoryText("√"); + } else if (childLabel == I18n::Message::USBDefaultLevel && currentLevel == 0) { + // messageComp->setTextColor(Palette::Green); + messageComp->setAccessoryText("√"); + } else if (childLabel == I18n::Message::USBParanoidLevel && currentLevel == 2) { + // messageComp->setTextColor(Palette::Green); + messageComp->setAccessoryText("√"); + } else if (childLabel == I18n::Message::USBMegaParanoidLevel && currentLevel == 3) { + // messageComp->setTextColor(Palette::Green); + messageComp->setAccessoryText("√"); + } +} + +int UsbProtectionLevelController::typeAtLocation(int i, int j) { + return 0; +} +} // namespace Settings \ No newline at end of file diff --git a/apps/settings/sub_menu/usb_protection_level_controller.h b/apps/settings/sub_menu/usb_protection_level_controller.h new file mode 100644 index 000000000..bf7d23754 --- /dev/null +++ b/apps/settings/sub_menu/usb_protection_level_controller.h @@ -0,0 +1,24 @@ +#ifndef SETTINGS_USB_PROTECTION_LEVEL_CONTROLLER_H +#define SETTINGS_USB_PROTECTION_LEVEL_CONTROLLER_H + +#include "generic_sub_controller.h" +#include + +namespace Settings { + +class UsbProtectionLevelController : public GenericSubController { +public: + UsbProtectionLevelController(Responder * parentResponder); + bool handleEvent(Ion::Events::Event event) override; + HighlightCell * reusableCell(int index, int type) override; + int reusableCellCount(int type) override; + void willDisplayCellForIndex(HighlightCell * cell, int index) override; + int typeAtLocation(int i, int j) override; +private: + static constexpr int k_maxNumberOfCells = 3; + MessageTableCellWithBuffer m_cell[k_maxNumberOfCells]; +}; + +} + +#endif diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 721941691..ec7a063b4 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Standard " ExamModeModeNoSym = "Kein Symbol " ExamModeModeNoSymNoText = "Kein Symbol kein Text " ExamModeModeDutch = "Niederländisch " +USBE16_expl1= "USB-Schutz schützt Ihren" +USBE16_expl2= "Taschenrechner vor" +USBE16_expl3= "unbeabsichtigter Verriegelung" +USBProtection= "USB-Schutz" +USBSettingDeact = "Bitte schalte den Schutz ein" +USBLevelProtect = "Akzeptierte Updates" +USBDefaultLevel = "Basierend auf Upsilon" +USBLowLevel = "Basierend auf Omega" +USBParanoidLevel = "Nur Python" +USBMegaParanoidLevel = "Nichts" +USBDeacAlert1 = "Ändern Sie diesen Parameter nur," +USBDeacAlert2 = "wenn Sie wissen, was Sie tun!" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index d61df44e1..cdf85677e 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Standard " ExamModeModeNoSym = "No sym " ExamModeModeNoSymNoText = "No sym no text " ExamModeModeDutch = "Dutch " +USBE16_expl1= "The USB protection protects" +USBE16_expl2= "the calculator from" +USBE16_expl3= "unintentional locking" +USBProtection= "USB Protection" +USBSettingDeact = "Please turn on the protection" +USBLevelProtect = "Updates accepted" +USBDefaultLevel = "Based on Upsilon" +USBLowLevel = "Based on Omega" +USBParanoidLevel = "Python Only" +USBMegaParanoidLevel = "None" +USBDeacAlert1 = "Change this parameter only if" +USBDeacAlert2 = "you know what you are doing !" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index e542571ed..eb21e5516 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Estándar " ExamModeModeNoSym = "Sin simbólico " ExamModeModeNoSymNoText = "Sin simbólico sin texto " ExamModeModeDutch = "Holandés " +USBE16_expl1= "La protección USB protege" +USBE16_expl2= "su calculadora del" +USBE16_expl3= "bloqueo involuntario" +USBProtection= "Protección USB" +USBSettingDeact = "Enciende la protección" +USBLevelProtect = "Actualizaciones aceptadas" +USBDefaultLevel = "Basado en Upsilon" +USBLowLevel = "Basado en Omega" +USBParanoidLevel = "Solo Python" +USBMegaParanoidLevel = "Ninguno" +USBDeacAlert1 = "¡Cambie este parámetro solo" +USBDeacAlert2 = "si sabe lo que está haciendo!" diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 46b76202d..bd01041b5 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Standard " ExamModeModeNoSym = "Sans symbolique " ExamModeModeNoSymNoText = "Sans symbolique ni texte " ExamModeModeDutch = "Dutch " +USBE16_expl1= "La protection USB protège votre" +USBE16_expl2= "calculatrice contre un verrouillage" +USBE16_expl3= "non-intentionnel" +USBProtection= "Protection USB" +USBSettingDeact = "Veuillez activer la protection" +USBLevelProtect = "Mise à jour acceptées" +USBDefaultLevel = "Basées sur Upsilon" +USBLowLevel = "Basées sur Omega" +USBParanoidLevel = "Aucune" +USBMegaParanoidLevel = "Aucune" +USBDeacAlert1 = "Ne modifiez ce paramètre que" +USBDeacAlert2 = "si vous savez ce que vous faites !" diff --git a/apps/shared.hu.i18n b/apps/shared.hu.i18n index 60ba0754b..84b3c1451 100644 --- a/apps/shared.hu.i18n +++ b/apps/shared.hu.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Normál " ExamModeModeNoSym = "Szimbólikus nélkül " ExamModeModeNoSymNoText = "Szimbólikus és szöveg nélkül " ExamModeModeDutch = "Holland " +USBE16_expl1= "Az USB-védelem megvédi" +USBE16_expl2= "a számológépet a nem" +USBE16_expl3= "szándékos reteszeléstől" +USBProtection= "USB védelem" +USBSettingDeact = "Kérjük, kapcsolja be a védelmet" +USBLevelProtect = "Elfogadott frissítések" +USBDefaultLevel = "Upsilon alapján" +USBLowLevel = "Omega alapján" +USBParanoidLevel = "Csak Python" +USBMegaParanoidLevel = "Egyik sem" +USBDeacAlert1 = "Csak akkor módosítsa ezt a" +USBDeacAlert2 = "paramétert, ha tudja, mit csinál!" diff --git a/apps/shared.it.i18n b/apps/shared.it.i18n index 3322800c8..60cd96bce 100644 --- a/apps/shared.it.i18n +++ b/apps/shared.it.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Standard " ExamModeModeNoSym = "Nessun simbolo " ExamModeModeNoSymNoText = "Nessun simbolo nessun testo " ExamModeModeDutch = "Olandese " +USBE16_expl1= "La protezione USB protegge" +USBE16_expl2= "la calcolatrice dal" +USBE16_expl3= "blocco involontario" +USBProtection= "Protezione USB" +USBSettingDeact = "Si prega di attivare la protezione" +USBLevelProtect = "Aggiornamenti accettati" +USBDefaultLevel = "Basato su Upsilon" +USBLowLevel = "A base di Omega" +USBParanoidLevel = "Solo Python" +USBMegaParanoidLevel = "Nessuno" +USBDeacAlert1 = "Cambia questo parametro solo" +USBDeacAlert2 = "se sai cosa stai facendo !" diff --git a/apps/shared.nl.i18n b/apps/shared.nl.i18n index b3971aa6c..6fbde69a4 100644 --- a/apps/shared.nl.i18n +++ b/apps/shared.nl.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Standaard " ExamModeModeNoSym = "Geen sym " ExamModeModeNoSymNoText = "Geen sym geen tekst " ExamModeModeDutch = "Nederlands " +USBE16_expl1= "USB-beveiliging beschermt uw" +USBE16_expl2= "rekenmachine tegen" +USBE16_expl3= "onbedoelde vergrendeling" +USBProtection= "USB-beveiliging" +USBSettingDeact = "Schakel a.u.b. de bescherming in" +USBLevelProtect = "Updates geaccepteerd" +USBDefaultLevel = "Gebaseerd op Upsilon" +USBLowLevel = "Op basis van Omega" +USBParanoidLevel = "Alleen Python" +USBMegaParanoidLevel = "Geen" +USBDeacAlert1 = "Wijzig deze parameter alleen" +USBDeacAlert2 = "als u weet wat u doet!" diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index 39fa24c12..11f9a8b96 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -99,3 +99,15 @@ ExamModeModeStandard = "Padrão " ExamModeModeNoSym = "Sem sym " ExamModeModeNoSymNoText = "Sem sym sem texto " ExamModeModeDutch = "holandês " +USBE16_expl1= "A proteção USB protege" +USBE16_expl2= "sua calculadora contra" +USBE16_expl3= "bloqueios não intencionais" +USBProtection= "Proteção USB" +USBSettingDeact = "Por favor, ligue a proteção" +USBLevelProtect = "Atualizações aceitas" +USBDefaultLevel = "Baseado em Upsilon" +USBLowLevel = "Baseado em Ômega" +USBParanoidLevel = "Apenas Python" +USBMegaParanoidLevel = "Nenhum" +USBDeacAlert1 = "Mude este parâmetro somente" +USBDeacAlert2 = "se você souber o que está fazendo!" diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index 7c42fa088..4326c16a7 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -458,6 +458,11 @@ HartreeConstant = "4.3597447222071·10^-18_J" MagneticFluxQuantum = "2.067833848·10^-15_Wb" ConductanceQuantum = "7.748091729·10^-5_S" CirculationQuantum = "3.6369475516·10^-4_m^2_s^-1" +UsbSetting = "USB" +USBDefaultLevelDesc = "L0" +USBLowLevelDesc = "L1" +USBParanoidLevelDesc = "L2" +USBMegaParanoidLevelDesc = "L3" Cndcvt_Silver = "6.30·10^7_S_m^-1" Cndcvt_Copper = "5.96·10^7_S_m^-1" Cndcvt_Gold = "4.11·10^7_S_m^-1" diff --git a/apps/usb/Makefile b/apps/usb/Makefile index 79692e351..fb8cfd2b7 100644 --- a/apps/usb/Makefile +++ b/apps/usb/Makefile @@ -1,6 +1,7 @@ app_usb_src = $(addprefix apps/usb/,\ app.cpp \ usb_connected_controller.cpp \ + usb_view.cpp \ ) apps_src += $(app_usb_src) diff --git a/apps/usb/app.cpp b/apps/usb/app.cpp index 99cdd7a6c..ad0256be1 100644 --- a/apps/usb/app.cpp +++ b/apps/usb/app.cpp @@ -27,6 +27,7 @@ App::App(Snapshot * snapshot) : } bool App::processEvent(Ion::Events::Event e) { + // Impossible de gérer mes events ici lorsqu'on active le DFU return false; } diff --git a/apps/usb/base.de.i18n b/apps/usb/base.de.i18n index 794b4d36b..0cf269f75 100644 --- a/apps/usb/base.de.i18n +++ b/apps/usb/base.de.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide." ConnectedMessage4 = "Drücken Sie die Zurück-Taste am" ConnectedMessage5 = "Taschenrechner oder Kabel abziehen," ConnectedMessage6 = "um die Verbindung zu trennen." +DfuStatus1 = "Status des Rechners:" +DfuStatusProtected = "GESCHÜTZT" +DfuStatusUnProtected = "UNGESCHÜTZT" +USBProtectionLevel0 = "Standardschutz" +USBProtectionLevel1 = "Omega Schutz" +USBProtectionLevel2 = "Systemschutz" diff --git a/apps/usb/base.en.i18n b/apps/usb/base.en.i18n index 0eb615fa2..b7411ff54 100644 --- a/apps/usb/base.en.i18n +++ b/apps/usb/base.en.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Press the BACK key of your" ConnectedMessage5 = "calculator or unplug it to" ConnectedMessage6 = "disconnect it." +DfuStatus1 = "Calculator status:" +DfuStatusProtected = "PROTECTED" +DfuStatusUnProtected = "UNPROTECTED" +USBProtectionLevel0 = "Default Protection" +USBProtectionLevel1 = "Omega Protection" +USBProtectionLevel2 = "System Protection" diff --git a/apps/usb/base.es.i18n b/apps/usb/base.es.i18n index cf9b3d7b5..0ccebf53d 100644 --- a/apps/usb/base.es.i18n +++ b/apps/usb/base.es.i18n @@ -4,4 +4,10 @@ ConnectedMessage2 = "nuestra página desde su ordenador" ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Pulse el botón RETURN de la" ConnectedMessage5 = "calculadora o desenchúfela para" -ConnectedMessage6 = "desconectarla." \ No newline at end of file +ConnectedMessage6 = "desconectarla." +DfuStatus1 = "Estado de la calculadora:" +DfuStatusProtected = "PROTEGIDO" +DfuStatusUnProtected = "DESABRIGADO" +USBProtectionLevel0 = "Protección predeterminada" +USBProtectionLevel1 = "Protección Omega" +USBProtectionLevel2 = "Protección del sistema" diff --git a/apps/usb/base.fr.i18n b/apps/usb/base.fr.i18n index 33762ca54..d1a4e9448 100644 --- a/apps/usb/base.fr.i18n +++ b/apps/usb/base.fr.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Appuyez sur la touche RETOUR" ConnectedMessage5 = "de la calculatrice ou débranchez-la" ConnectedMessage6 = "pour la déconnecter." +DfuStatus1 = "Etat de la calculatrice:" +DfuStatusProtected = "PROTÉGÉE" +DfuStatusUnProtected = "NON PROTÉGÉE" +USBProtectionLevel0 = "Default Protection" +USBProtectionLevel1 = "Omega Protection" +USBProtectionLevel2 = "System Protection" diff --git a/apps/usb/base.hu.i18n b/apps/usb/base.hu.i18n index a8a3de172..d432c76d3 100644 --- a/apps/usb/base.hu.i18n +++ b/apps/usb/base.hu.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "fel getomega.dev/ide ra." ConnectedMessage4 = "Nyomjon majd a VISSZA gombra" ConnectedMessage5 = "vagy huzza ki a kábelt azért" ConnectedMessage6 = "hogy a másolás véget érjen." +DfuStatus1 = "Számológép állapota:" +DfuStatusProtected = "VÉDETT" +DfuStatusUnProtected = "VÉDTELEN" +USBProtectionLevel0 = "Alapértelmezett védelem" +USBProtectionLevel1 = "Omega védelem" +USBProtectionLevel2 = "Rendszervédelem" diff --git a/apps/usb/base.it.i18n b/apps/usb/base.it.i18n index fdc2be0bd..2fbed5da8 100644 --- a/apps/usb/base.it.i18n +++ b/apps/usb/base.it.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Premere sul tasto INDIETRO della" ConnectedMessage5 = "calcolatrice o scollegatela per" ConnectedMessage6 = "disconnetterla." +DfuStatus1 = "Stato della calcolatrice:" +DfuStatusProtected = "PROTETTO" +DfuStatusUnProtected = "INDIFESO" +USBProtectionLevel0 = "Protezione predefinita" +USBProtectionLevel1 = "Protezione Omega" +USBProtectionLevel2 = "Protezione del sistema" diff --git a/apps/usb/base.nl.i18n b/apps/usb/base.nl.i18n index db29c1127..e05db7f3f 100644 --- a/apps/usb/base.nl.i18n +++ b/apps/usb/base.nl.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Druk op de TERUG toets van je" ConnectedMessage5 = "rekenmachine of verwijder de" ConnectedMessage6 = " kabel om hem los te koppelen." +DfuStatus1 = "Rekenmachine status:" +DfuStatusProtected = "BESCHERMD" +DfuStatusUnProtected = "NIET BESCHERMD" +USBProtectionLevel0 = "Standaardbeveiliging" +USBProtectionLevel1 = "Omega Bescherming" +USBProtectionLevel2 = "Systeembeveiliging" diff --git a/apps/usb/base.pt.i18n b/apps/usb/base.pt.i18n index 408b24db7..91dd85e3c 100644 --- a/apps/usb/base.pt.i18n +++ b/apps/usb/base.pt.i18n @@ -5,3 +5,9 @@ ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Pressione o botão RETURN na" ConnectedMessage5 = "calculadora ou desligue-a para a" ConnectedMessage6 = "desconectar." +DfuStatus1 = "Status da calculadora:" +DfuStatusProtected = "PROTEGIDO" +DfuStatusUnProtected = "DESPROTEGIDO" +USBProtectionLevel0 = "Proteção padrão" +USBProtectionLevel1 = "Proteção Ômega" +USBProtectionLevel2 = "Proteção do sistema" diff --git a/apps/usb/usb_connected_controller.cpp b/apps/usb/usb_connected_controller.cpp index 71a354c4a..deb3abff0 100644 --- a/apps/usb/usb_connected_controller.cpp +++ b/apps/usb/usb_connected_controller.cpp @@ -1,42 +1,140 @@ #include "usb_connected_controller.h" #include +#include "../global_preferences.h" -namespace USB { - -static I18n::Message sUSBConnectedMessages[] = { - I18n::Message::USBConnected, - I18n::Message::ConnectedMessage1, - I18n::Message::ConnectedMessage2, - I18n::Message::ConnectedMessage3, - I18n::Message::BlankMessage, - I18n::Message::ConnectedMessage4, - I18n::Message::ConnectedMessage5, - I18n::Message::ConnectedMessage6}; - -static KDColor sUSBConnectedFGColors[] = { - Palette::PrimaryText, - Palette::PrimaryText, - Palette::PrimaryText, - Palette::AccentText, - KDColorWhite, - Palette::PrimaryText, - Palette::PrimaryText, - Palette::PrimaryText}; - -static KDColor sUSBConnectedBGColors[] = { - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard, - Palette::BackgroundHard}; - -USBConnectedController::USBConnectedController() : - ViewController(nullptr), - m_messageView(sUSBConnectedMessages, sUSBConnectedFGColors, sUSBConnectedBGColors, 8) +namespace USB { -} + + static I18n::Message sUSBConnectedMessagesProtection0[10] = { + I18n::Message::USBConnected, + I18n::Message::ConnectedMessage1, + I18n::Message::ConnectedMessage2, + I18n::Message::ConnectedMessage3, + I18n::Message::BlankMessage, + I18n::Message::ConnectedMessage4, + I18n::Message::ConnectedMessage5, + I18n::Message::ConnectedMessage6, + I18n::Message::DfuStatus1, + I18n::Message::USBProtectionLevel0}; + + static I18n::Message sUSBConnectedMessagesProtection1[10] = { + I18n::Message::USBConnected, + I18n::Message::ConnectedMessage1, + I18n::Message::ConnectedMessage2, + I18n::Message::ConnectedMessage3, + I18n::Message::BlankMessage, + I18n::Message::ConnectedMessage4, + I18n::Message::ConnectedMessage5, + I18n::Message::ConnectedMessage6, + I18n::Message::DfuStatus1, + I18n::Message::USBProtectionLevel1}; + + static I18n::Message sUSBConnectedMessagesProtection2[10] = { + I18n::Message::USBConnected, + I18n::Message::ConnectedMessage1, + I18n::Message::ConnectedMessage2, + I18n::Message::ConnectedMessage3, + I18n::Message::BlankMessage, + I18n::Message::ConnectedMessage4, + I18n::Message::ConnectedMessage5, + I18n::Message::ConnectedMessage6, + I18n::Message::DfuStatus1, + I18n::Message::USBProtectionLevel2}; + + static I18n::Message sUSBConnectedMessagesUnProtected[10] = { + I18n::Message::USBConnected, + I18n::Message::ConnectedMessage1, + I18n::Message::ConnectedMessage2, + I18n::Message::ConnectedMessage3, + I18n::Message::BlankMessage, + I18n::Message::ConnectedMessage4, + I18n::Message::ConnectedMessage5, + I18n::Message::ConnectedMessage6, + I18n::Message::DfuStatus1, + I18n::Message::DfuStatusUnProtected}; + + static KDColor sUSBConnectedFGColors[] = { + Palette::PrimaryText, + Palette::PrimaryText, + Palette::PrimaryText, + Palette::AccentText, + KDColorWhite, + Palette::PrimaryText, + Palette::PrimaryText, + Palette::PrimaryText, + Palette::PrimaryText, + Palette::AccentText}; + + static KDColor sUSBConnectedBGColors[] = { + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard, + Palette::BackgroundHard}; + + USBConnectedController::USBConnectedController() : ViewController(nullptr), step(0), already_init(false), m_messageView(sUSBConnectedMessagesProtection0, sUSBConnectedFGColors, sUSBConnectedBGColors, 10) + { + if(already_init){ + return; + } + if (step == 0 && GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) + { + getMessageView()->update(sUSBConnectedMessagesUnProtected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + }else if(step == 0){ + if(GlobalPreferences::sharedGlobalPreferences()->getDfuLevel() == 1){ + getMessageView()->update(sUSBConnectedMessagesProtection1, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + }else if(GlobalPreferences::sharedGlobalPreferences()->getDfuLevel() == 2){ + getMessageView()->update(sUSBConnectedMessagesProtection2, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + } + } + // else if (step == 1 && GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) + // { + // getMessageView()->update(sUSBConnectedMessages2UnProtected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // } + // else if (step == 1 && !GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) + // { + // getMessageView()->update(sUSBConnectedMessages2Protected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // } + already_init = true; + } + + // bool USBConnectedController::handleEvent(Ion::Events::Event event) + // { + // if (event.isKeyboardEvent()) + // { + // // Ne fonctionne pas pour l'instant fait bugger les events + // // if (step == 0) + // // { + // // if (GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) + // // { + // // getMessageView()->update(sUSBConnectedMessagesUnProtected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // // } + // // else + // // { + // // getMessageView()->update(sUSBConnectedMessagesProtected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // // } + // // step = 1; + // // } + // // else + // // { + // // if (GlobalPreferences::sharedGlobalPreferences()->dfuStatus()) + // // { + // // getMessageView()->update(sUSBConnectedMessages2UnProtected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // // } + // // else + // // { + // // getMessageView()->update(sUSBConnectedMessages2Protected, sUSBConnectedFGColors, sUSBConnectedBGColors, 10); + // // } + // // step = 0; + // // } + // return false; + // } + // return false; + // } } diff --git a/apps/usb/usb_connected_controller.h b/apps/usb/usb_connected_controller.h index c733062ed..b2f5a269f 100644 --- a/apps/usb/usb_connected_controller.h +++ b/apps/usb/usb_connected_controller.h @@ -2,7 +2,7 @@ #define USB_USB_CONNECTED_CONTROLLER_H #include -#include "../shared/message_view.h" +#include "usb_view.h" namespace USB { @@ -10,9 +10,12 @@ class USBConnectedController : public ViewController { public: USBConnectedController(); View * view() override { return &m_messageView; } - bool handleEvent(Ion::Events::Event event) override { return false; } + USBView * getMessageView() {return &m_messageView; } + bool handleEvent(Ion::Events::Event event) override { return false; }; private: - MessageView m_messageView; + int step; + bool already_init; + USBView m_messageView; }; } diff --git a/apps/usb/usb_view.cpp b/apps/usb/usb_view.cpp new file mode 100644 index 000000000..2eab7ee40 --- /dev/null +++ b/apps/usb/usb_view.cpp @@ -0,0 +1,56 @@ +#include "usb_view.h" +#include + +namespace USB { + USBView::USBView(I18n::Message *messages, KDColor *fgcolors, KDColor *bgcolors, uint8_t numberOfMessages) { + m_numberOfMessages = numberOfMessages < k_maxNumberOfMessages ? numberOfMessages : k_maxNumberOfMessages; + for (uint8_t i = 0; i < m_numberOfMessages; i++) { + m_messageTextViews[i].setFont(i == 0 ? KDFont::LargeFont : KDFont::SmallFont); + m_messageTextViews[i].setMessage(messages[i]); + m_messageTextViews[i].setAlignment(0.5f, 0.5f); + m_messageTextViews[i].setTextColor(fgcolors[i]); + m_messageTextViews[i].setBackgroundColor(bgcolors[i]); + } + } + + void USBView::drawRect(KDContext *ctx, KDRect rect) const { + ctx->fillRect(bounds(), Palette::BackgroundHard); + } + + View *USBView::subviewAtIndex(int index) { + if (index >= m_numberOfMessages) { + assert(false); + return nullptr; + } + return &(m_messageTextViews[index]); + } + + void USBView::update(I18n::Message *messages, KDColor *fgcolors, KDColor *bgcolors, uint8_t numberOfMessages){ + m_numberOfMessages = numberOfMessages < k_maxNumberOfMessages ? numberOfMessages : k_maxNumberOfMessages; + for (uint8_t i = 0; i < m_numberOfMessages; i++) { + m_messageTextViews[i].setFont(i == 0 ? KDFont::LargeFont : KDFont::SmallFont); + m_messageTextViews[i].setMessage(messages[i]); + m_messageTextViews[i].setAlignment(0.5f, 0.5f); + m_messageTextViews[i].setTextColor(fgcolors[i]); + m_messageTextViews[i].setBackgroundColor(bgcolors[i]); + } + reload(); + } + + void USBView::reload() { + markRectAsDirty(bounds()); + } + + void USBView::layoutSubviews(bool force) { + if (m_numberOfMessages == 0) { + return; + } + KDCoordinate width = bounds().width(); + KDCoordinate titleHeight = m_messageTextViews[0].minimalSizeForOptimalDisplay().height(); + KDCoordinate textHeight = KDFont::SmallFont->glyphSize().height(); + m_messageTextViews[0].setFrame(KDRect(0, k_titleMargin, width, titleHeight), force); + for (uint8_t i = 1; i < m_numberOfMessages; i++) { + m_messageTextViews[i].setFrame(KDRect(0, k_paragraphHeight + (i - 1) * textHeight, width, textHeight), force); + } + } +} diff --git a/apps/usb/usb_view.h b/apps/usb/usb_view.h new file mode 100644 index 000000000..67e274093 --- /dev/null +++ b/apps/usb/usb_view.h @@ -0,0 +1,30 @@ +#ifndef USB_VIEW_H +#define USB_VIEW_H + +#include + +namespace USB +{ + class USBView : public View + { + public: + USBView(I18n::Message *messages, KDColor *fgcolors, KDColor *bgcolors, uint8_t numberOfMessages); + void drawRect(KDContext *ctx, KDRect rect) const override; + void reload(); + void update(I18n::Message *messages, KDColor *fgcolors, KDColor *bgcolors, uint8_t numberOfMessages); + protected: + int numberOfSubviews() const override { return m_numberOfMessages; } + View *subviewAtIndex(int index) override; + void layoutSubviews(bool force = false) override; + + private: + constexpr static KDCoordinate k_titleMargin = 30; + constexpr static KDCoordinate k_paragraphHeight = 80; + constexpr static uint8_t k_maxNumberOfMessages = 10; + MessageTextView m_messageTextViews[k_maxNumberOfMessages]; + uint8_t m_numberOfMessages; + }; + +} + +#endif diff --git a/build/device/secure_ext.py b/build/device/secure_ext.py new file mode 100644 index 000000000..68d2a6a65 --- /dev/null +++ b/build/device/secure_ext.py @@ -0,0 +1,29 @@ +import sys +import os + +MAGIK_CODE = [0x64, 0x6c, 0x31, 0x31, 0x23, 0x39, 0x38, 0x33, 0x35] +MAGIK_POS = [0x03, 0xb, 0x44f] + +if len(sys.argv) > 1: + print("Patching external bin...") + ext_path = os.path.join(os.getcwd(), sys.argv[1]) + if not os.path.isfile(ext_path): + print("Error: File not found!") + sys.exit(-1) + file = open(ext_path, "r+b") + first_packet = bytearray(file.read(2048)) + for b in first_packet: + if b != 255: + print("Error: Invalid file! (maybe already patched?)") + sys.exit(-1) + + for i in range(4): + first_packet[MAGIK_POS[0] + i] = MAGIK_CODE[i] + for i in range(4): + first_packet[MAGIK_POS[1] + i] = MAGIK_CODE[i + 5] + for i in range(len(MAGIK_CODE)): + first_packet[MAGIK_POS[2] + i] = MAGIK_CODE[i] + + file.seek(0) + file.write(first_packet) + print("External bin Patched!") \ No newline at end of file diff --git a/build/targets.device.mak b/build/targets.device.mak index 9498371c4..850cbf076 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -27,11 +27,11 @@ $(BUILD_DIR)/%.map: $(BUILD_DIR)/%.elf @echo "LDMAP $@" $(Q) $(LD) $^ $(LDFLAGS) -Wl,-M -Wl,-Map=$@ -o /dev/null -.PHONY: %_memory_map -%_memory_map: $(BUILD_DIR)/%.map - @echo "========== MEMORY MAP =========" - $(Q) awk -f build/device/memory_map.awk < $< - @echo "===============================" +.PHONY: mapfile +mapfile: + $(BUILD_DIR)/%.map: $(BUILD_DIR)/%.elf + @echo "LDMAP $@" + $(Q) $(LD) $^ $(LDFLAGS) -Wl,-M -Wl,-Map=$@ -o /dev/null .PHONY: openocd openocd: @@ -72,3 +72,4 @@ binpack: $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/epsilon.onboarding.two_bina cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack cd $(BUILD_DIR) && for binary in flasher.light.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* + $(PYTHON) build/device/secure_ext.py $(BUILD_DIR)/epsilon.onboarding.external.bin \ No newline at end of file diff --git a/ion/include/ion/usb.h b/ion/include/ion/usb.h index e5760bb94..d9ec8afd7 100644 --- a/ion/include/ion/usb.h +++ b/ion/include/ion/usb.h @@ -8,7 +8,7 @@ bool isPlugged(); bool isEnumerated(); // Speed-enumerated, to be accurate void clearEnumerationInterrupt(); -void DFU(bool exitWithKeyboard = true); +void DFU(bool exitWithKeyboard = true, bool unlocked = false, int level = 0); void enable(); void disable(); diff --git a/ion/src/device/flasher/main.cpp b/ion/src/device/flasher/main.cpp index 3dcfcd80d..09f05e8ca 100644 --- a/ion/src/device/flasher/main.cpp +++ b/ion/src/device/flasher/main.cpp @@ -11,6 +11,6 @@ void ion_main(int argc, const char * const argv[]) { Ion::USB::enable(); while (!Ion::USB::isEnumerated()) { } - Ion::USB::DFU(false); + Ion::USB::DFU(false, false, 0); } } diff --git a/ion/src/device/n0110/drivers/board.cpp b/ion/src/device/n0110/drivers/board.cpp index 0ed24868c..228fbe6fc 100644 --- a/ion/src/device/n0110/drivers/board.cpp +++ b/ion/src/device/n0110/drivers/board.cpp @@ -30,7 +30,9 @@ void initMPU() { Cache::dmb(); // 1.2 Disable fault exceptions - CORTEX.SHCRS()->setMEMFAULTENA(false); + CORTEX.SHCRS()->setMEMFAULTENA(true); + CORTEX.SHCRS()->setBUSFAULTENA(true); + CORTEX.SHCRS()->setUSGFAULTENA(true); // 1.3 Disable the MPU and clear the control register MPU.CTRL()->setENABLE(false); @@ -121,7 +123,7 @@ void init() { initClocks(); // The bootloader leaves its own after flashing - //SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory); + // SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory); // Ensure right location of interrupt vectors CORTEX.VTOR()->setVTOR((void*)&InitialisationVector); diff --git a/ion/src/device/n0110/flash.ld b/ion/src/device/n0110/flash.ld index 9728e4ad3..1f2ebb8bb 100644 --- a/ion/src/device/n0110/flash.ld +++ b/ion/src/device/n0110/flash.ld @@ -63,6 +63,14 @@ SECTIONS { . = ALIGN(4); *(.text.start) *(.text.abort) + *(.text.uf_abort) + *(.text.bf_abort) + *(.text.nmi_abort) + *(.text.abort_init) + *(.text.abort_core) + *(.text.abort_sleeping) + *(.text.abort_economy) + *(.text.abort_screen) *(.text.isr_systick) *(.text.__assert) *(.text.memcpy) @@ -89,6 +97,123 @@ SECTIONS { /* 'abort' dependencies */ *(.text._ZN3Ion6Device5Reset4coreEv) + *(.text._ZN3Ion3LED8setColorE7KDColor) + *(.text._ZN3Ion3LED11setBlinkingEtf) + *(.text._ZN3Ion6Device3LED*) + *(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv) + *(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4BaseEv) + *(.text._ZNK3Ion6Device4Regs3*) + *(.text.___ZN3Ion6Device4Regs8*) + *(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv) + *(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv*) + *(.text._ZN3Ion6Device5Board15initPeripheralsEb) + *(.text._ZN3Ion6Device9Backlight4initEv) + *(.text._ZN3Ion6Device6Timing4initEv) + *(.text._ZN3Ion6Timing6msleepEj) + *(.text._ZN3Ion6Device8Keyboard4initEv) + *(.text._ZN3Ion6Device7Battery8initGPIOEv) + *(.text._ZN3Ion6Device3USB8initGPIOEv) + *(.text._ZN3Ion6Device9Backlight8setLevelEh) + *(.text._ZN3Ion6Device6Timing19setSysTickFrequencyEi) + *(.text._ZN3Ion6Device5Board17setClockFrequencyENS1_9FrequencyE) + *(.text._ZN3Ion6Device9Backlight10sendPulsesEi) + *(.text._ZN3Ion6Device5Board19shutdownPeripheralsEb) + *(.text._ZN3Ion6Device6Timing8shutdownEv) + *(.text._ZN3Ion6Device8Keyboard8shutdownEv) + *(.text._ZN9KDContext10drawStringEPKc7KDPointPK6KDFont7KDColorS6_i) + *(.text._ZNK8TextArea11ContentView12drawStringAtEP9KDContextiiPKci7KDColorS5_S4_S4_S5_*) + *(.text._ZL11KDPointZero*) + *(.text._ZGVZN12KDIonContext13sharedContextEvE7context) + *(.text._ZZN12KDIonContext13sharedContextEvE7context) + *(.text._ZN12KDIonContext13sharedContextEv) + *(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor) + *(.text._ZNK6KDRect15intersectedWithERKS_) + *(.text._ZN7KDColor6RGB888Ehhh) + *(.text._ZN3Ion6Device7Display14setDrawingAreaE6KDRectNS1_11OrientationE) + *(.text.powf) + *(.text._ZN9KDContext16pushOrPullStringEPKc7KDPointPK6KDFont7KDColorS6_ibPi) + *(.text._ZNK7KDPoint12translatedByES_) + *(.text._ZNK6KDRect12translatedByE7KDPoint) + *(.text._Z7toGammai) + *(.text._ZN3Ion7Display8pullRectE6KDRectP7KDColor) + *(.text._ZN24KDPostProcessZoomContext8pullRectE6KDRectP7KDColor) + *(.text._ZN16KDRealIonContext8pullRectE6KDRectP7KDColor) + *(.text._ZN25KDPostProcessGammaContext8pullRectE6KDRectP7KDColor) + *(.text._ZN12KDIonContext8pullRectE6KDRectP7KDColor) + *(.text._ZN26KDPostProcessInvertContext8pullRectE6KDRectP7KDColor) + *(.text._ZN20KDPostProcessContext8pullRectE6KDRectP7KDColor) + *(.text.sqrtf) + *(.text._ZN11UTF8Decoder13nextCodePointEv) + *(.text._ZN7KDColor5blendES_S_h) + *(.text._ZN9KDContext17blendRectWithMaskE6KDRect7KDColorPKhPS1_) + *(.text.scalbnf) + *(.text._ZNK6KDRect10intersectsERKS_) + *(.text._ZN9KDContext18fillRectWithPixelsE6KDRectPK7KDColorPS1_) + *(.text._ZN9KDContext15setClippingRectE6KDRect) + *(.text._ZN9KDContext9setOriginE7KDPoint) + *(.text._ZN20KDPostProcessContext9setOriginE7KDPoint) + *(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN20KDPostProcessContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN12KDIonContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN26KDPostProcessInvertContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN24KDPostProcessZoomContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN25KDPostProcessGammaContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor) + *(.text._ZN16KDRealIonContext8pushRectE6KDRectPK7KDColor) + *(.text._ZN3Ion7Display8pushRectE6KDRectPK7KDColor) + *(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor) + *(.text._ZNK6KDRect7isEmptyEv) + *(.text._ZN20KDPostProcessContext15setClippingRectE6KDRect) + *(.text._ZNK6KDFont17indexForCodePointE9CodePoint) + *(.text._ZNK6KDFont26fetchGrayscaleGlyphAtIndexEhPh) + *(.text.LZ4_decompress_safe) + *(.text.LZ4_wildCopy) + *(.text.*DFU*) + *(.text.*isEnumerated*) + *(.text._ZN3Ion3USB6enableEv) + *(.text._ZN3Ion7Battery5levelEv) + *(.text._ZN3Ion7Battery7voltageEv) + *(.text._ZN3Ion3USB9isPluggedEv) + *(.text.*sleepConfiguration*) + *(.text.*onOnOffKeyDown*) + *(.text.*WakeUp*) + *(.text._ZN3Ion6Device9Backlight*) + *(.text._ZN3Ion9Backlight*) + + /* Rodata Truc Relou */ + *(.text._ZNK10Statistics5Store6medianEi) + *(.text._ZNK10Regression5Store12meanOfColumnEiib) + *(.text._ZNK6Shared15DoublePairStore11sumOfColumnEiib) + *(.text._ZNK10Statistics5Store13firstQuartileEi) + *(.text._ZNK10Regression5Store23squaredValueSumOfColumnEiib) + *(.text._ZNK10Regression5Store16varianceOfColumnEiib) + *(.text._ZNK10Statistics5Store8maxValueEi) + *(.text._ZNK10Regression5Store25standardDeviationOfColumnEiib) + *(.text._ZNK10Statistics5Store13thirdQuartileEi) + *(.text._ZNK10Statistics5Store8minValueEi) + *(.text._ZNK6Shared8Interval18IntervalParameters*) + *(.text._ZN6Shared8Interval18IntervalParameters*) + *(.text.sqrt) + *(.text.log) + *(.text._ZN17GlobalPreferences23sharedGlobalPreferencesEv) + *(.text._ZNK10Statistics5Store16sumOfOccurrencesEi) + *(.text.floor) + *(.text.ceil) + *(.text._ZNK10Statistics5Store21frequenciesAreIntegerEi) + *(.text._ZNK10Statistics5Store34sortedElementAtCumulatedPopulationEidb) + *(.text._ZNK10Statistics5Store33sortedElementAtCumulatedFrequencyEidb) + *(.text.round) + *(.text._ZNK10Statistics5Store8minIndexEPdi*) /* 'standby' dependencies '*/ *(.text._ZN3Ion6Device5Power20internalFlashStandbyEv) @@ -113,7 +238,49 @@ SECTIONS { /* 'start' dependencies */ *(.rodata._ZN3Ion6Device4RegsL5GPIOAE) *(.rodata._ZN3Ion6Device4RegsL5GPIOBE) + *(.rodata._ZN3Ion6Device3LED6ConfigL7RGBPinsE) *(.rodata._ZN3Ion6Device5Board4initEv.str1.4) + *(.rodata._ZL12KDColorWhite*) + *(.rodata._ZL10KDColorRed*) + *(.rodata._ZL12KDColorBlack*) + *(.rodata._ZN3Ion6Device3SWD6ConfigL4PinsE) + *(.rodata._ZN3Ion6Device7Display6ConfigL8FSMCPinsE) + *(.rodata._ZZN3Ion6Device7Display9initPanelEvE11calibration) + *(.rodata._ZN3Ion6Device3USB6ConfigL5DmPinE) + *(.rodata._ZN3Ion6Device3USB6ConfigL5DpPinE) + *(.rodata._ZN3Ion6Device3USB6ConfigL7VbusPinE) + *(.rodata._ZN3Ion6Device8Keyboard6ConfigL10ColumnPinsE) + *(.rodata._ZN3Ion6Device8Keyboard6ConfigL7RowPinsE) + *(.rodata._ZZN3Ion6Device3USB12shutdownGPIOEvE4Pins) + *(.rodata._ZN6KDFont16privateLargeFontE) + *(.rodata.abort.str1.1) + *(.rodata.uf_abort.str1.1) + *(.rodata.bf_abort.str1.1) + *(.rodata.nmi_abort.str1.1) + *(.rodata.abort_screen.str1.1) + *(.rodata._ZL5table*) + *(.rodata._ZL15glyphDataOffset*) + *(.rodata._ZL11KDPointZero*) + *(.rodata._ZL9glyphData*) + *(.rodata._ZN4CodeL14HighlightColorE*) + *(.rodata._ZTV12KDIonContext) + *(.rodata._ZTV16KDRealIonContext) + *(.rodata._ZTV24KDPostProcessZoomContext) + *(.rodata._ZTV25KDPostProcessGammaContext) + *(.rodata._ZTV26KDPostProcessInvertContext) + *(.rodata.bp) + *(.rodata.dp_h) + *(.rodata.dp_l) + *(.rodata._ZN11MicroPython5Color5ParseEPvNS0_4ModeE*) + *(.rodata._ZN9Clipboard10storedTextEv*) + *(.rodata._ZN8Sequence14ListController27toolboxForInputEventHandlerEP17InputEventHandler*) + *(.rodata._ZN8Sequence23TypeParameterController25willDisplayCellAtLocationEP13HighlightCellii*) + *(.rodata._ZN6KDFont16privateSmallFontE) + *(.rodata._ZN4I18nL23CountryPreferencesArrayE) + *(.rodata.abort_sleeping.str1.1) + *(.rodata.abort_core.str1.1) + *(.rodata.dfu_bootloader) + *(.rodata) } >INTERNAL_FLASH .exam_mode_buffer ORIGIN(EXTERNAL_FLASH) : { diff --git a/ion/src/device/shared/boot/isr.c b/ion/src/device/shared/boot/isr.c index 5c201aa67..2ebde5c64 100644 --- a/ion/src/device/shared/boot/isr.c +++ b/ion/src/device/shared/boot/isr.c @@ -18,11 +18,11 @@ ISR InitialisationVector[INITIALISATION_VECTOR_SIZE] = { (ISR)&_stack_start, // Stack start start, // Reset service routine, - 0, // NMI service routine, + nmi_abort, // NMI service routine, abort, // HardFault service routine, 0, // MemManage service routine, - 0, // BusFault service routine, - 0, // UsageFault service routine, + bf_abort, // BusFault service routine, + uf_abort, // UsageFault service routine, 0, 0, 0, 0, // Reserved 0, // SVCall service routine, 0, // DebugMonitor service routine, diff --git a/ion/src/device/shared/boot/isr.h b/ion/src/device/shared/boot/isr.h index ca5becb13..be46adee9 100644 --- a/ion/src/device/shared/boot/isr.h +++ b/ion/src/device/shared/boot/isr.h @@ -5,6 +5,14 @@ extern "C" { #endif +void bf_abort(); +void uf_abort(); +void nmi_abort(); +void abort_init(); +void abort_core(const char *); +void abort_sleeping(); +void abort_economy(); +void abort_screen(const char *); void start(); void abort(); void isr_systick(); diff --git a/ion/src/device/shared/boot/rt0.cpp b/ion/src/device/shared/boot/rt0.cpp index 4cb671496..6444117f0 100644 --- a/ion/src/device/shared/boot/rt0.cpp +++ b/ion/src/device/shared/boot/rt0.cpp @@ -1,45 +1,52 @@ -#include "isr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include + #include "../drivers/board.h" -#include "../drivers/rtc.h" #include "../drivers/reset.h" +#include "../drivers/rtc.h" #include "../drivers/timing.h" +#include "isr.h" typedef void (*cxx_constructor)(); extern "C" { - extern char _data_section_start_flash; - extern char _data_section_start_ram; - extern char _data_section_end_ram; - extern char _bss_section_start_ram; - extern char _bss_section_end_ram; - extern cxx_constructor _init_array_start; - extern cxx_constructor _init_array_end; -} - -void __attribute__((noinline)) abort() { -#ifdef NDEBUG - Ion::Device::Reset::core(); -#else - while (1) { - } -#endif +extern char _data_section_start_flash; +extern char _data_section_start_ram; +extern char _data_section_end_ram; +extern char _bss_section_start_ram; +extern char _bss_section_end_ram; +extern cxx_constructor _init_array_start; +extern cxx_constructor _init_array_end; } /* In order to ensure that this method is execute from the external flash, we * forbid inlining it.*/ static void __attribute__((noinline)) external_flash_start() { - /* Init the peripherals. We do not initialize the backlight in case there is + /* Init the peripherals. We do not initialize the backlight in case there is * an on boarding app: indeed, we don't want the user to see the LCD tests * happening during the on boarding app. The backlight will be initialized * after the Power-On Self-Test if there is one or before switching to the * home app otherwise. */ - Ion::Device::Board::initPeripherals(false); - - return ion_main(0, nullptr); + Ion::Device::Board::initPeripherals(false); + return ion_main(0, nullptr); } /* This additional function call 'jump_to_external_flash' serves two purposes: @@ -61,7 +68,155 @@ static void __attribute__((noinline)) external_flash_start() { * internal flash to the external flash. */ static void __attribute__((noinline)) jump_to_external_flash() { - external_flash_start(); + external_flash_start(); +} + +void __attribute__((noinline)) abort_init() { + Ion::Device::Board::shutdownPeripherals(true); + Ion::Device::Board::initPeripherals(false); + Ion::Timing::msleep(100); + Ion::Backlight::init(); + Ion::LED::setColor(KDColorRed); + Ion::Backlight::setBrightness(180); +} + +void __attribute__((noinline)) abort_economy() { + int brightness = Ion::Backlight::brightness(); + bool plugged = Ion::USB::isPlugged(); + while (brightness > 0) { + brightness--; + Ion::Backlight::setBrightness(brightness); + Ion::Timing::msleep(50); + if(plugged || (!plugged && Ion::USB::isPlugged())){ + Ion::Backlight::setBrightness(180); + return; + } + } + Ion::Backlight::shutdown(); + while (1) { + Ion::Device::Power::sleepConfiguration(); + Ion::Device::WakeUp::onUSBPlugging(); + Ion::Device::WakeUp::onChargingEvent(); + Ion::Device::Power::internalFlashSuspend(true); + if (!plugged && Ion::USB::isPlugged()) { + break; + } + plugged = Ion::USB::isPlugged(); + }; + Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High); + Ion::Backlight::init(); + Ion::Backlight::setBrightness(180); +} + +void __attribute__((noinline)) abort_sleeping() { + if (Ion::Battery::level() != Ion::Battery::Charge::EMPTY) { + return; + } + // we don't use Ion::Power::suspend because we don't want to move the exam buffer into the internal + Ion::Device::Board::shutdownPeripherals(true); + bool plugged = Ion::USB::isPlugged(); + while (1) { + Ion::Device::Battery::initGPIO(); + Ion::Device::USB::initGPIO(); + Ion::Device::LED::init(); + Ion::Device::Power::sleepConfiguration(); + Ion::Device::Board::shutdownPeripherals(true); + Ion::Device::WakeUp::onUSBPlugging(); + Ion::Device::WakeUp::onChargingEvent(); + Ion::Device::Power::internalFlashSuspend(true); + Ion::Device::USB::initGPIO(); + if (!plugged && Ion::USB::isPlugged()) { + break; + } + plugged = Ion::USB::isPlugged(); + } + Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High); + abort_init(); +} + +void __attribute__((noinline)) abort_core(const char * text) { + Ion::Timing::msleep(100); + int counting; + while (true) { + counting = 0; + if (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) { + abort_sleeping(); + abort_screen(text); + } + + Ion::USB::enable(); + Ion::Battery::Charge previous_state = Ion::Battery::level(); + while (!Ion::USB::isEnumerated()) { + if (Ion::Battery::level() == Ion::Battery::Charge::LOW) { + if (previous_state != Ion::Battery::Charge::LOW) { + previous_state = Ion::Battery::Charge::LOW; + counting = 0; + } + Ion::Timing::msleep(500); + if (counting >= 20) { + abort_sleeping(); + abort_screen(text); + counting = -1; + } + counting++; + + } else { + if (previous_state == Ion::Battery::Charge::LOW) { + previous_state = Ion::Battery::level(); + counting = 0; + } + Ion::Timing::msleep(100); + if (counting >= 300) { + abort_economy(); + counting = -1; + } + counting++; + } + } + Ion::USB::DFU(false, false, 0); + } +} + +void __attribute__((noinline)) abort_screen(const char * text){ + KDRect screen = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height); + Ion::Display::pushRectUniform(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), KDColor::RGB24(0xffffff)); + KDContext* ctx = KDIonContext::sharedContext(); + ctx->setOrigin(KDPointZero); + ctx->setClippingRect(screen); + ctx->drawString("UPSILON CRASH", KDPoint(90, 10), KDFont::LargeFont, KDColorRed, KDColor::RGB24(0xffffff)); + ctx->drawString("An error occurred", KDPoint(10, 30), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("If you have some important data, please", KDPoint(10, 45), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("use bit.ly/upsiBackup to backup them.", KDPoint(10, 60), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("YOU WILL LOSE ALL YOUR DATA", KDPoint(10, 85), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("→ You can try to reboot by presssing the", KDPoint(10, 110), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("reset button at the back of the calculator", KDPoint(10, 125), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("→ If Upsilon keeps crashing, you can connect", KDPoint(10, 140), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("the calculator to a computer or a phone", KDPoint(10, 160), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString("and try to reinstall Upsilon", KDPoint(10, 175), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff)); + ctx->drawString(text, KDPoint(220, 200), KDFont::SmallFont, KDColorRed, KDColor::RGB24(0xffffff)); +} + +void __attribute__((noinline)) abort() { + abort_init(); + abort_screen("HARDFAULT"); + abort_core("HARDFAULT"); +} + +void __attribute__((noinline)) nmi_abort() { + abort_init(); + abort_screen("NMIFAULT"); + abort_core("NMIFAULT"); +} + +void __attribute__((noinline)) bf_abort() { + abort_init(); + abort_screen("BUSFAULT"); + abort_core("BUSFAULT"); +} +void __attribute__((noinline)) uf_abort() { + abort_init(); + abort_screen("USAGEFAULT"); + abort_core("USAGEFAULT"); } /* When 'start' is executed, the external flash is supposed to be shutdown. We @@ -69,27 +224,27 @@ static void __attribute__((noinline)) jump_to_external_flash() { * (just in case 'start' was to be called from the external flash). */ void __attribute__((noinline)) start() { - /* This is where execution starts after reset. + /* This is where execution starts after reset. * Many things are not initialized yet so the code here has to pay attention. */ - /* Copy data section to RAM + /* Copy data section to RAM * The data section is R/W but its initialization value matters. It's stored * in Flash, but linked as if it were in RAM. Now's our opportunity to copy * it. Note that until then the data section (e.g. global variables) contains * garbage values and should not be used. */ - size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); - memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); + size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); + memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); - /* Zero-out the bss section in RAM + /* Zero-out the bss section in RAM * Until we do, any uninitialized global variable will be unusable. */ - size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram); - memset(&_bss_section_start_ram, 0, bssSectionLength); + size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram); + memset(&_bss_section_start_ram, 0, bssSectionLength); - /* Initialize the FPU as early as possible. + /* Initialize the FPU as early as possible. * For example, static C++ objects are very likely to manipulate float values */ - Ion::Device::Board::initFPU(); + Ion::Device::Board::initFPU(); - /* Call static C++ object constructors + /* Call static C++ object constructors * The C++ compiler creates an initialization function for each static object. * The linker then stores the address of each of those functions consecutively * between _init_array_start and _init_array_end. So to initialize all C++ @@ -97,30 +252,30 @@ void __attribute__((noinline)) start() { * call the pointed function. */ #define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0 #if SUPPORT_CPP_GLOBAL_CONSTRUCTORS - for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) { - (*c)(); - } + for (cxx_constructor* c = &_init_array_start; c < &_init_array_end; c++) { + (*c)(); + } #else - /* In practice, static initialized objects are a terrible idea. Since the init + /* In practice, static initialized objects are a terrible idea. Since the init * order is not specified, most often than not this yields the dreaded static * init order fiasco. How about bypassing the issue altogether? */ - if (&_init_array_start != &_init_array_end) { - abort(); - } + if (&_init_array_start != &_init_array_end) { + abort(); + } #endif - Ion::Device::Board::init(); + Ion::Device::Board::init(); - /* At this point, we initialized clocks and the external flash but no other + /* At this point, we initialized clocks and the external flash but no other * peripherals. */ - jump_to_external_flash(); + jump_to_external_flash(); - abort(); + abort(); } void __attribute__((interrupt, noinline)) isr_systick() { - auto t = Ion::Device::Timing::MillisElapsed; - t++; - Ion::Device::Timing::MillisElapsed = t; + auto t = Ion::Device::Timing::MillisElapsed; + t++; + Ion::Device::Timing::MillisElapsed = t; } diff --git a/ion/src/device/shared/usb/calculator.cpp b/ion/src/device/shared/usb/calculator.cpp index 73d6bf579..4d3818601 100644 --- a/ion/src/device/shared/usb/calculator.cpp +++ b/ion/src/device/shared/usb/calculator.cpp @@ -7,10 +7,11 @@ namespace Ion { namespace Device { namespace USB { -void Calculator::PollAndReset(bool exitWithKeyboard) { +void Calculator::PollAndReset(bool exitWithKeyboard, bool unlock, int level) { char serialNumber[Ion::Device::SerialNumber::Length+1]; Ion::Device::SerialNumber::copy(serialNumber); Calculator c(serialNumber); + /* Leave DFU mode if the Back key is pressed, the calculator unplugged or the * USB core soft-disconnected. */ @@ -19,6 +20,10 @@ void Calculator::PollAndReset(bool exitWithKeyboard) { uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey); Ion::Device::Keyboard::activateRow(exitKeyRow); + c.m_dfuInterface.setLevel(level); + if (unlock) { + c.m_dfuInterface.unlockDfu(); + } while (!(exitWithKeyboard && !c.isErasingAndWriting() && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) && Ion::USB::isPlugged() && diff --git a/ion/src/device/shared/usb/calculator.h b/ion/src/device/shared/usb/calculator.h index cf083860c..bf0a04db4 100644 --- a/ion/src/device/shared/usb/calculator.h +++ b/ion/src/device/shared/usb/calculator.h @@ -25,7 +25,7 @@ namespace USB { class Calculator : public Device { public: - static void PollAndReset(bool exitWithKeyboard) + static void PollAndReset(bool exitWithKeyboard, bool unlocked, int level) __attribute__((section(".dfu_entry_point"))) // Needed to pinpoint this symbol in the linker script __attribute__((used)) // Make sure this symbol is not discarded at link time ; // Return true if reset is needed diff --git a/ion/src/device/shared/usb/dfu_interface.cpp b/ion/src/device/shared/usb/dfu_interface.cpp index 2b98274b4..69370c0cb 100644 --- a/ion/src/device/shared/usb/dfu_interface.cpp +++ b/ion/src/device/shared/usb/dfu_interface.cpp @@ -1,286 +1,518 @@ -#include "dfu_interface.h" -#include -#include -#include - -namespace Ion { -namespace Device { -namespace USB { - -static inline uint32_t minUint32T(uint32_t x, uint32_t y) { return x < y ? x : y; } - -void DFUInterface::StatusData::push(Channel * c) const { - c->push(m_bStatus); - c->push(m_bwPollTimeout[2]); - c->push(m_bwPollTimeout[1]); - c->push(m_bwPollTimeout[0]); - c->push(m_bState); - c->push(m_iString); -} - -void DFUInterface::StateData::push(Channel * c) const { - c->push(m_bState); -} - -void DFUInterface::wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) { - if (request->bRequest() == (uint8_t) DFURequest::Download) { - // Handle a download request - if (request->wValue() == 0) { - // The request is a special command - switch (transferBuffer[0]) { - case (uint8_t) DFUDownloadCommand::SetAddressPointer: - setAddressPointerCommand(request, transferBuffer, *transferBufferLength); - return; - case (uint8_t) DFUDownloadCommand::Erase: - eraseCommand(transferBuffer, *transferBufferLength); - return; - default: - m_state = State::dfuERROR; - m_status = Status::errSTALLEDPKT; - return; - } - } - if (request->wValue() == 1) { - m_ep0->stallTransaction(); - return; - } - if (request->wLength() > 0) { - // The request is a "real" download. Compute the writing address. - m_writeAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer; - // Store the received data until we copy it on the flash. - memcpy(m_largeBuffer, transferBuffer, *transferBufferLength); - m_largeBufferLength = *transferBufferLength; - m_state = State::dfuDNLOADSYNC; - } - } -} - -void DFUInterface::wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) { - if (request->bRequest() == (uint8_t) DFURequest::GetStatus) { - // Do any needed action after the GetStatus request. - if (m_state == State::dfuMANIFEST) { - /* If we leave the DFU and reset immediately, dfu-util outputs an error: - * "File downloaded successfully - * dfu-util: Error during download get_status" - * If we sleep 1us here, there is no error. We put 1ms for security. - * This error might be due to the USB connection being cut too soon after - * the last USB exchange, so the host does not have time to process the - * answer received for the last GetStatus request. */ - Ion::Timing::msleep(1); - // Leave DFU routine: Leave DFU, reset device, jump to application code - leaveDFUAndReset(); - } else if (m_state == State::dfuDNBUSY) { - if (m_largeBufferLength != 0) { - // Here, copy the data from the transfer buffer to the flash memory - writeOnMemory(); - } - changeAddressPointerIfNeeded(); - eraseMemoryIfNeeded(); - m_state = State::dfuDNLOADIDLE; - } - } -} - -bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) { - if (Interface::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) { - return true; - } - switch (request->bRequest()) { - case (uint8_t) DFURequest::Detach: - m_device->detach(); - return true; - case (uint8_t) DFURequest::Download: - return processDownloadRequest(request->wLength(), transferBufferLength); - case (uint8_t) DFURequest::Upload: - return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t) DFURequest::GetStatus: - return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t) DFURequest::ClearStatus: - return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t) DFURequest::GetState: - return getState(transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t) DFURequest::Abort: - return dfuAbort(transferBufferLength); - } - return false; -} - -bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength) { - if (m_state != State::dfuIDLE && m_state != State::dfuDNLOADIDLE) { - m_state = State::dfuERROR; - m_status = Status::errNOTDONE; - m_ep0->stallTransaction(); - return false; - } - if (wLength == 0) { - // Leave DFU routine: Reset the device and jump to application code - m_state = State::dfuMANIFESTSYNC; - } else { - // Prepare to receive the download data - m_ep0->clearForOutTransactions(wLength); - m_state = State::dfuDNLOADSYNC; - } - return true; -} - -bool DFUInterface::processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) { - if (m_state != State::dfuIDLE && m_state != State::dfuUPLOADIDLE) { - m_ep0->stallTransaction(); - return false; - } - if (request->wValue() == 0) { - /* The host requests to read the commands supported by the bootloader. After - * receiving this command, the device should returns N bytes representing - * the command codes for : - * Get command / Set Address Pointer / Erase / Read Unprotect - * We no not need it for now. */ - return false; - } else if (request->wValue() == 1) { - m_ep0->stallTransaction(); - return false; - } else { - /* We decided to never protect Read operation. Else we would have to check - * here it is not protected before reading. */ - - // Compute the reading address - uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer; - // Copy the requested memory zone into the transfer buffer. - uint16_t copySize = minUint32T(transferBufferMaxLength, request->wLength()); - memcpy(transferBuffer, (void *)readAddress, copySize); - *transferBufferLength = copySize; - } - m_state = State::dfuUPLOADIDLE; - return true; -} - -void DFUInterface::setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength) { - assert(transferBufferLength == 5); - // Compute the new address but change it after the next getStatus request. - m_potentialNewAddressPointer = transferBuffer[1] - + (transferBuffer[2] << 8) - + (transferBuffer[3] << 16) - + (transferBuffer[4] << 24); - m_state = State::dfuDNLOADSYNC; -} - -void DFUInterface::changeAddressPointerIfNeeded() { - if (m_potentialNewAddressPointer == 0) { - // There was no address change waiting. - return; - } - // If there is a new address pointer waiting, change the pointer address. - m_addressPointer = m_potentialNewAddressPointer; - m_potentialNewAddressPointer = 0; - m_state = State::dfuDNLOADIDLE; - m_status = Status::OK; -} - -void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength) { - /* We determine whether the commands asks for a mass erase or which sector to - * erase. The erase must be done after the next getStatus request. */ - m_state = State::dfuDNLOADSYNC; - - if (transferBufferLength == 1) { - // Mass erase - m_erasePage = Flash::TotalNumberOfSectors(); - return; - } - - // Sector erase - assert(transferBufferLength == 5); - - uint32_t eraseAddress = transferBuffer[1] - + (transferBuffer[2] << 8) - + (transferBuffer[3] << 16) - + (transferBuffer[4] << 24); - - m_erasePage = Flash::SectorAtAddress(eraseAddress); - if (m_erasePage < 0) { - // Unrecognized sector - m_state = State::dfuERROR; - m_status = Status::errTARGET; - } -} - - -void DFUInterface::eraseMemoryIfNeeded() { - if (m_erasePage < 0) { - // There was no erase waiting. - return; - } - - willErase(); - if (m_erasePage == Flash::TotalNumberOfSectors()) { - Flash::MassErase(); - } else { - Flash::EraseSector(m_erasePage); - } - - /* Put an out of range value in m_erasePage to indicate that no erase is - * waiting. */ - m_erasePage = -1; - m_state = State::dfuDNLOADIDLE; - m_status = Status::OK; -} - -void DFUInterface::writeOnMemory() { - if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) { - // Write on SRAM - // FIXME We should check that we are not overriding the current instructions. - memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength); - } else if (Flash::SectorAtAddress(m_writeAddress) >= 0) { - Flash::WriteMemory(reinterpret_cast(m_writeAddress), m_largeBuffer, m_largeBufferLength); - } else { - // Invalid write address - m_largeBufferLength = 0; - m_state = State::dfuERROR; - m_status = Status::errTARGET; - return; - } - - // Reset the buffer length - m_largeBufferLength = 0; - // Change the interface state and status - m_state = State::dfuDNLOADIDLE; - m_status = Status::OK; -} - - -bool DFUInterface::getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) { - // Change the status if needed - if (m_state == State::dfuMANIFESTSYNC) { - m_state = State::dfuMANIFEST; - } else if (m_state == State::dfuDNLOADSYNC) { - m_state = State::dfuDNBUSY; - } - // Copy the status on the TxFifo - *transferBufferLength = StatusData(m_status, m_state).copy(transferBuffer, transferBufferMaxLength); - return true; -} - -bool DFUInterface::clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) { - m_status = Status::OK; - m_state = State::dfuIDLE; - return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); -} - -bool DFUInterface::getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize) { - *transferBufferLength = StateData(m_state).copy(transferBuffer, maxSize); - return true; -} - -bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) { - m_status = Status::OK; - m_state = State::dfuIDLE; - *transferBufferLength = 0; - return true; -} - -void DFUInterface::leaveDFUAndReset() { - m_device->setResetOnDisconnect(true); - m_device->detach(); -} - -} -} -} + +#include "dfu_interface.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace Ion { +namespace Device { +namespace USB { + +static inline uint32_t minUint32T(uint32_t x, uint32_t y) { return x < y ? x : y; } + +void DFUInterface::StatusData::push(Channel *c) const { + c->push(m_bStatus); + c->push(m_bwPollTimeout[2]); + c->push(m_bwPollTimeout[1]); + c->push(m_bwPollTimeout[0]); + c->push(m_bState); + c->push(m_iString); +} + +void DFUInterface::StateData::push(Channel *c) const { + c->push(m_bState); +} + +void DFUInterface::wholeDataReceivedCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) { + if (request->bRequest() == (uint8_t)DFURequest::Download) { + // Handle a download request + if (request->wValue() == 0) { + // The request is a special command + switch (transferBuffer[0]) { + case (uint8_t)DFUDownloadCommand::SetAddressPointer: + setAddressPointerCommand(request, transferBuffer, *transferBufferLength); + return; + case (uint8_t)DFUDownloadCommand::Erase: + eraseCommand(transferBuffer, *transferBufferLength); + return; + default: + m_state = State::dfuERROR; + m_status = Status::errSTALLEDPKT; + return; + } + } + if (request->wValue() == 1) { + m_ep0->stallTransaction(); + return; + } + if (request->wLength() > 0) { + // The request is a "real" download. Compute the writing address. + m_writeAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer; + + // Store the received data until we copy it on the flash. + memcpy(m_largeBuffer, transferBuffer, *transferBufferLength); + m_largeBufferLength = *transferBufferLength; + m_state = State::dfuDNLOADSYNC; + } + } +} + +void DFUInterface::wholeDataSentCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) { + if (request->bRequest() == (uint8_t)DFURequest::GetStatus) { + // Do any needed action after the GetStatus request. + if (m_state == State::dfuMANIFEST) { + if (m_dfuLevel == 1 && m_isFirstExternalFlash && !m_isInternalLocked) { + return; + } + /* If we leave the DFU and reset immediately, dfu-util outputs an error: + * "File downloaded successfully + * dfu-util: Error during download get_status" + * If we sleep 1us here, there is no error. We put 1ms for security. + * This error might be due to the USB connection being cut too soon after + * the last USB exchange, so the host does not have time to process the + * answer received for the last GetStatus request. */ + Ion::Timing::msleep(1); + // Leave DFU routine: Leave DFU, reset device, jump to application code + leaveDFUAndReset(); + } else if (m_state == State::dfuDNBUSY) { + m_state = State::dfuDNBUSY; + if (m_largeBufferLength != 0) { + // Here, copy the data from the transfer buffer to the flash memory + writeOnMemory(); + } + changeAddressPointerIfNeeded(); + eraseMemoryIfNeeded(); + m_state = State::dfuDNLOADIDLE; + } + } +} + +bool DFUInterface::processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { + if (Interface::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) { + return true; + } + switch (request->bRequest()) { + case (uint8_t)DFURequest::Detach: + m_device->detach(); + return true; + case (uint8_t)DFURequest::Download: + return processDownloadRequest(request->wLength(), transferBufferLength); + case (uint8_t)DFURequest::Upload: + return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength); + case (uint8_t)DFURequest::GetStatus: + return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); + case (uint8_t)DFURequest::ClearStatus: + return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); + case (uint8_t)DFURequest::GetState: + return getState(transferBuffer, transferBufferLength, transferBufferMaxLength); + case (uint8_t)DFURequest::Abort: + return dfuAbort(transferBufferLength); + } + return false; +} + +bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t *transferBufferLength) { + if (m_state != State::dfuIDLE && m_state != State::dfuDNLOADIDLE) { + m_state = State::dfuERROR; + m_status = Status::errNOTDONE; + m_ep0->stallTransaction(); + return false; + } + if (wLength == 0) { + // Leave DFU routine: Reset the device and jump to application code + m_state = State::dfuMANIFESTSYNC; + } else { + // Prepare to receive the download data + m_ep0->clearForOutTransactions(wLength); + m_state = State::dfuDNLOADSYNC; + } + return true; +} + +bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { + if (m_state != State::dfuIDLE && m_state != State::dfuUPLOADIDLE) { + m_ep0->stallTransaction(); + return false; + } + if (request->wValue() == 0) { + /* The host requests to read the commands supported by the bootloader. After + * receiving this command, the device should returns N bytes representing + * the command codes for : + * Get command / Set Address Pointer / Erase / Read Unprotect + * We no not need it for now. */ + return false; + } else if (request->wValue() == 1) { + m_ep0->stallTransaction(); + return false; + } else { + /* We decided to never protect Read operation. Else we would have to check + * here it is not protected before reading. */ + + // Compute the reading address + uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer; + // Copy the requested memory zone into the transfer buffer. + uint16_t copySize = minUint32T(transferBufferMaxLength, request->wLength()); + memcpy(transferBuffer, (void *)readAddress, copySize); + *transferBufferLength = copySize; + } + m_state = State::dfuUPLOADIDLE; + return true; +} + +void DFUInterface::setAddressPointerCommand(SetupPacket *request, uint8_t *transferBuffer, uint16_t transferBufferLength) { + assert(transferBufferLength == 5); + // Compute the new address but change it after the next getStatus request. + m_potentialNewAddressPointer = transferBuffer[1] + (transferBuffer[2] << 8) + (transferBuffer[3] << 16) + (transferBuffer[4] << 24); + m_state = State::dfuDNLOADSYNC; +} + +void DFUInterface::changeAddressPointerIfNeeded() { + if (m_potentialNewAddressPointer == 0) { + // There was no address change waiting. + return; + } + // If there is a new address pointer waiting, change the pointer address. + m_addressPointer = m_potentialNewAddressPointer; + m_potentialNewAddressPointer = 0; + m_state = State::dfuDNLOADIDLE; + m_status = Status::OK; +} + +void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength) { + /* We determine whether the commands asks for a mass erase or which sector to + * erase. The erase must be done after the next getStatus request. */ + m_state = State::dfuDNLOADSYNC; + + if (transferBufferLength == 1) { + // Mass erase + m_erasePage = Flash::TotalNumberOfSectors(); + return; + } + + // Sector erase + assert(transferBufferLength == 5); + + m_eraseAddress = transferBuffer[1] + (transferBuffer[2] << 8) + (transferBuffer[3] << 16) + (transferBuffer[4] << 24); + + m_erasePage = Flash::SectorAtAddress(m_eraseAddress); + if (m_erasePage < 0) { + // Unrecognized sector + m_state = State::dfuERROR; + m_status = Status::errTARGET; + } +} + +void DFUInterface::eraseMemoryIfNeeded() { + if (m_erasePage < 0) { + return; + } + willErase(); + if ((m_eraseAddress >= k_ExternalBorderAddress && m_eraseAddress < ExternalFlash::Config::EndAddress) || m_dfuUnlocked) { + int32_t order = Flash::SectorAtAddress(m_eraseAddress); + Flash::EraseSector(order); + } + m_state = State::dfuDNLOADIDLE; + m_status = Status::OK; + m_erasePage = -1; +} + +void DFUInterface::writeOnMemory() { + if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) { + // Write on SRAM + // FIXME We should check that we are not overriding the current instructions. + memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength); + reset_custom_vars(); // On reset les vars car la ram n'a pas de secteur à effacer + } else if (Flash::SectorAtAddress(m_writeAddress) >= 0) { + if (m_dfuLevel == 2) { + m_largeBufferLength = 0; + m_state = State::dfuERROR; + m_status = Status::errWRITE; + return; + } + + int current_memory_flashed; + if (m_writeAddress >= InternalFlash::Config::StartAddress && m_writeAddress <= InternalFlash::Config::EndAddress) { + if (m_isInternalLocked && !m_dfuUnlocked) // On vérifie si l'external a été flash + { + m_largeBufferLength = 0; + m_state = State::dfuERROR; + m_status = Status::errTARGET; + leaveDFUAndReset(false); // Numworks flash l'internal avant donc on exit pour afficher le message + return; + } + current_memory_flashed = 0; + + //on écrit dans la mémoire interne + if (m_isFirstInternalFlash && !m_dfuUnlocked) { + m_temp_is_valid = true; + for (int i = 0; i < 4; i++) { + if (magik[i] != m_largeBuffer[magik_adrs + i]) { + m_temp_is_valid = false; + break; + } + } + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + m_state = State::dfuERROR; + m_status = Status::errVERIFY; + //leaveDFUAndReset(); On ne leave plus sinon on fait crash la calc si il n'y a que la partie ext. + return; + } else { + m_isFirstInternalFlash = false; + } + } + } else { + current_memory_flashed = 1; + // Nous écrivons la partie external os + if (m_writeAddress < k_ExternalBorderAddress && m_isFirstExternalFlash && !m_dfuUnlocked) // On vérifie si on installe des apps ext + { + // if (m_dfuLevel == 1 && m_isInternalLocked) { + // m_largeBufferLength = 0; + // m_state = State::dfuERROR; + // m_status = Status::errTARGET; + // return; + // } + if (m_dfuLevel == 0) { + // Partie moche du code parce que je n'arrivais pas à compil avec 3 boucles for sous msys + int adress_magik = magik_ext_adrs[0]; + m_temp_is_valid = external_magik[0] == m_largeBuffer[adress_magik]; + m_largeBuffer[adress_magik] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[1] == m_largeBuffer[adress_magik + 1]; + m_largeBuffer[adress_magik + 1] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[2] == m_largeBuffer[adress_magik + 2]; + m_largeBuffer[adress_magik + 2] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[3] == m_largeBuffer[adress_magik + 3]; + m_largeBuffer[adress_magik + 3] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + adress_magik = magik_ext_adrs[1]; + + m_temp_is_valid = external_magik[5] == m_largeBuffer[adress_magik]; + m_largeBuffer[adress_magik] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[6] == m_largeBuffer[adress_magik + 1]; + m_largeBuffer[adress_magik + 1] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[7] == m_largeBuffer[adress_magik + 2]; + m_largeBuffer[adress_magik + 2] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[8] == m_largeBuffer[adress_magik + 3]; + m_largeBuffer[adress_magik + 3] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + adress_magik = magik_ext_adrs[2]; + m_temp_is_valid = true; + + m_temp_is_valid = external_magik[0] == m_largeBuffer[adress_magik]; + m_largeBuffer[adress_magik] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[1] == m_largeBuffer[adress_magik + 1]; + m_largeBuffer[adress_magik + 1] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[2] == m_largeBuffer[adress_magik + 2]; + m_largeBuffer[adress_magik + 2] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[3] == m_largeBuffer[adress_magik + 3]; + m_largeBuffer[adress_magik + 3] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + + m_temp_is_valid = external_magik[4] == m_largeBuffer[adress_magik + 4]; + m_largeBuffer[adress_magik + 4] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + m_temp_is_valid = external_magik[5] == m_largeBuffer[adress_magik + 5]; + m_largeBuffer[adress_magik + 5] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + m_temp_is_valid = external_magik[6] == m_largeBuffer[adress_magik + 6]; + m_largeBuffer[adress_magik + 6] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + m_temp_is_valid = external_magik[7] == m_largeBuffer[adress_magik + 7]; + m_largeBuffer[adress_magik + 7] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } + m_temp_is_valid = external_magik[8] == m_largeBuffer[adress_magik + 8]; + m_largeBuffer[adress_magik + 8] = 0xff; + + if (!m_temp_is_valid) { + m_largeBufferLength = 0; + leaveDFUAndReset(false); + return; + } else { + m_isFirstExternalFlash = false; + m_isInternalLocked = false; + } + } else { + m_isFirstExternalFlash = false; + m_isInternalLocked = false; + } + } + } + if (m_last_memoryFlashed >= 0 && current_memory_flashed != m_last_memoryFlashed) { + m_last_memoryFlashed = -1; + } + + m_erasePage = Flash::SectorAtAddress(m_writeAddress); + + //On vérifie qu'on a pas déjà effacé le secteur et si ce n'est pas un secteur external déjà effacé + if ((m_last_memoryFlashed < 0 || m_erasePage != m_lastPageErased) && m_writeAddress < k_ExternalBorderAddress && !m_dfuUnlocked) { + Flash::EraseSector(m_erasePage); + m_last_memoryFlashed = current_memory_flashed; + } + + m_lastPageErased = m_erasePage; + m_erasePage = -1; + + Ion::Timing::msleep(1); + Flash::WriteMemory(reinterpret_cast(m_writeAddress), m_largeBuffer, m_largeBufferLength); + } else { + // Invalid write address + m_largeBufferLength = 0; + m_state = State::dfuERROR; + m_status = Status::errTARGET; + return; + } + // Reset the buffer length + m_largeBufferLength = 0; + // Change the interface state and status + m_state = State::dfuDNLOADIDLE; + m_status = Status::OK; +} + +bool DFUInterface::getStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { + // Change the status if needed + if (m_state == State::dfuMANIFESTSYNC) { + m_state = State::dfuMANIFEST; + } else if (m_state == State::dfuDNLOADSYNC) { + m_state = State::dfuDNBUSY; + } + // Copy the status on the TxFifo + *transferBufferLength = StatusData(m_status, m_state).copy(transferBuffer, transferBufferMaxLength); + return true; +} + +bool DFUInterface::clearStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { + m_status = Status::OK; + m_state = State::dfuIDLE; + return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); +} + +bool DFUInterface::getState(uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t maxSize) { + *transferBufferLength = StateData(m_state).copy(transferBuffer, maxSize); + return true; +} + +bool DFUInterface::dfuAbort(uint16_t *transferBufferLength) { + m_status = Status::OK; + m_state = State::dfuIDLE; + *transferBufferLength = 0; + return true; +} + +void DFUInterface::leaveDFUAndReset(bool do_reset) { + reset_custom_vars(); + m_isInternalLocked = true; + m_isFirstInternalFlash = true; + m_isFirstExternalFlash = true; + m_device->setResetOnDisconnect(do_reset); + m_device->detach(); +} + +} // namespace USB +} // namespace Device +} // namespace Ion diff --git a/ion/src/device/shared/usb/dfu_interface.h b/ion/src/device/shared/usb/dfu_interface.h index 0ae25d874..7d4ce8a7d 100644 --- a/ion/src/device/shared/usb/dfu_interface.h +++ b/ion/src/device/shared/usb/dfu_interface.h @@ -8,173 +8,217 @@ #include "stack/endpoint0.h" #include "stack/setup_packet.h" #include "stack/streamable.h" +#include +#include -namespace Ion { -namespace Device { -namespace USB { - -class DFUInterface : public Interface { - -public: - DFUInterface(Device * device, Endpoint0 * ep0, uint8_t bInterfaceAlternateSetting) : - Interface(ep0), - m_device(device), - m_status(Status::OK), - m_state(State::dfuIDLE), - m_addressPointer(0), - m_potentialNewAddressPointer(0), - m_erasePage(-1), - m_largeBuffer{0}, - m_largeBufferLength(0), - m_writeAddress(0), - m_bInterfaceAlternateSetting(bInterfaceAlternateSetting), - m_isErasingAndWriting(false) +namespace Ion +{ + namespace Device { - } - uint32_t addressPointer() const { return m_addressPointer; } - void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override; - void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override; - bool isErasingAndWriting() const { return m_isErasingAndWriting; } - -protected: - void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override { - assert(interfaceAlternativeIndex == m_bInterfaceAlternateSetting); - } - uint8_t getActiveInterfaceAlternative() override { - return m_bInterfaceAlternateSetting; - } - bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override; - -private: - // DFU Request Codes - enum class DFURequest { - Detach = 0, - Download = 1, - Upload = 2, - GetStatus = 3, - ClearStatus = 4, - GetState = 5, - Abort = 6 - }; - - // DFU Download Commmand Codes - enum class DFUDownloadCommand { - GetCommand = 0x00, - SetAddressPointer = 0x21, - Erase = 0x41, - ReadUnprotect = 0x92 - }; - - enum class Status : uint8_t { - OK = 0x00, - errTARGET = 0x01, - errFILE = 0x02, - errWRITE = 0x03, - errERASE = 0x04, - errCHECK_ERASED = 0x05, - errPROG = 0x06, - errVERIFY = 0x07, - errADDRESS = 0x08, - errNOTDONE = 0x09, - errFIRMWARE = 0x0A, - errVENDOR = 0x0B, - errUSBR = 0x0C, - errPOR = 0x0D, - errUNKNOWN = 0x0E, - errSTALLEDPKT = 0x0F - }; - - enum class State : uint8_t { - appIDLE = 0, - appDETACH = 1, - dfuIDLE = 2, - dfuDNLOADSYNC = 3, - dfuDNBUSY = 4, - dfuDNLOADIDLE = 5, - dfuMANIFESTSYNC = 6, - dfuMANIFEST = 7, - dfuMANIFESTWAITRESET = 8, - dfuUPLOADIDLE = 9, - dfuERROR = 10 - }; - - class StatusData : public Streamable { - public: - StatusData(Status status, State state, uint32_t pollTimeout = 1) : - /* We put a default pollTimeout value of 1ms: if the device is busy, the - * host has to wait 1ms before sending a getStatus Request. */ - m_bStatus((uint8_t)status), - m_bwPollTimeout{uint8_t((pollTimeout>>16) & 0xFF), uint8_t((pollTimeout>>8) & 0xFF), uint8_t(pollTimeout & 0xFF)}, - m_bState((uint8_t)state), - m_iString(0) + namespace USB { - } - protected: - void push(Channel * c) const override; - private: - uint8_t m_bStatus; // Status resulting from the execution of the most recent request - uint8_t m_bwPollTimeout[3]; // m_bwPollTimeout is 24 bits - uint8_t m_bState; // State of the device immediately following transmission of this response - uint8_t m_iString; - }; - class StateData : public Streamable { - public: - StateData(State state) : m_bState((uint8_t)state) {} - protected: - void push(Channel * c) const override; - private: - uint8_t m_bState; // Current state of the device - }; + class DFUInterface : public Interface + { - /* The Flash and SRAM addresses are in flash.ld. However, dfu_interface is + public: + DFUInterface(Device *device, Endpoint0 *ep0, uint8_t bInterfaceAlternateSetting) : Interface(ep0), + m_device(device), + m_status(Status::OK), + m_state(State::dfuIDLE), + m_addressPointer(0), + m_potentialNewAddressPointer(0), + m_erasePage(-1), + m_largeBuffer{0}, + m_largeBufferLength(0), + m_writeAddress(0), + m_eraseAddress(0), + m_bInterfaceAlternateSetting(bInterfaceAlternateSetting), + m_isErasingAndWriting(false), + m_isFirstInternalFlash(true), + m_temp_is_valid(false), + m_isInternalLocked(true), + m_isFirstExternalFlash(true), + m_last_memoryFlashed(-1), + m_lastPageErased(-1), + m_dfuUnlocked(false), + m_dfuLevel(0) + { + } + uint32_t addressPointer() const { return m_addressPointer; } + void wholeDataReceivedCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) override; + void wholeDataSentCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) override; + bool isErasingAndWriting() const { return m_isErasingAndWriting; } + void unlockDfu() {m_dfuUnlocked = true;}; + void setLevel(int lvl) {m_dfuLevel = lvl; } + + protected: + void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override + { + assert(interfaceAlternativeIndex == m_bInterfaceAlternateSetting); + } + uint8_t getActiveInterfaceAlternative() override + { + return m_bInterfaceAlternateSetting; + } + bool processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) override; + + private: + // DFU Request Codes + enum class DFURequest + { + Detach = 0, + Download = 1, + Upload = 2, + GetStatus = 3, + ClearStatus = 4, + GetState = 5, + Abort = 6 + }; + + // DFU Download Commmand Codes + enum class DFUDownloadCommand + { + GetCommand = 0x00, + SetAddressPointer = 0x21, + Erase = 0x41, + ReadUnprotect = 0x92 + }; + + enum class Status : uint8_t + { + OK = 0x00, + errTARGET = 0x01, + errFILE = 0x02, + errWRITE = 0x03, + errERASE = 0x04, + errCHECK_ERASED = 0x05, + errPROG = 0x06, + errVERIFY = 0x07, + errADDRESS = 0x08, + errNOTDONE = 0x09, + errFIRMWARE = 0x0A, + errVENDOR = 0x0B, + errUSBR = 0x0C, + errPOR = 0x0D, + errUNKNOWN = 0x0E, + errSTALLEDPKT = 0x0F + }; + + enum class State : uint8_t + { + appIDLE = 0, + appDETACH = 1, + dfuIDLE = 2, + dfuDNLOADSYNC = 3, + dfuDNBUSY = 4, + dfuDNLOADIDLE = 5, + dfuMANIFESTSYNC = 6, + dfuMANIFEST = 7, + dfuMANIFESTWAITRESET = 8, + dfuUPLOADIDLE = 9, + dfuERROR = 10 + }; + + class StatusData : public Streamable + { + public: + StatusData(Status status, State state, uint32_t pollTimeout = 10) : /* We put a default pollTimeout value of 1ms: if the device is busy, the + * host has to wait 1ms before sending a getStatus Request. */ + m_bStatus((uint8_t)status), + m_bwPollTimeout{uint8_t((pollTimeout >> 16) & 0xFF), uint8_t((pollTimeout >> 8) & 0xFF), uint8_t(pollTimeout & 0xFF)}, + m_bState((uint8_t)state), + m_iString(0) + { + } + + protected: + void push(Channel *c) const override; + + private: + uint8_t m_bStatus; // Status resulting from the execution of the most recent request + uint8_t m_bwPollTimeout[3]; // m_bwPollTimeout is 24 bits + uint8_t m_bState; // State of the device immediately following transmission of this response + uint8_t m_iString; + }; + + class StateData : public Streamable + { + public: + StateData(State state) : m_bState((uint8_t)state) {} + + protected: + void push(Channel *c) const override; + + private: + uint8_t m_bState; // Current state of the device + }; + + /* The Flash and SRAM addresses are in flash.ld. However, dfu_interface is * linked with dfu.ld, so we cannot access the values. */ - constexpr static uint32_t k_sramStartAddress = 0x20000000; - constexpr static uint32_t k_sramEndAddress = 0x20040000; + constexpr static uint32_t k_sramStartAddress = 0x20000000; + constexpr static uint32_t k_sramEndAddress = 0x20040000; + constexpr static uint32_t k_ExternalBorderAddress = 0x90200000; - // Download and upload - bool processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength); - bool processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength); - // Address pointer - void setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength); - void changeAddressPointerIfNeeded(); - // Access memory - void eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength); - void eraseMemoryIfNeeded(); - void writeOnMemory(); - void unlockFlashMemory(); - void lockFlashMemoryAndPurgeCaches(); - // Status - bool getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength); - bool clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength); - // State - bool getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize); - // Abort - bool dfuAbort(uint16_t * transferBufferLength); - // Leave DFU - void leaveDFUAndReset(); - /* Erase and Write state. After starting the erase of flash memory, the user + const static int magik_adrs = 0x1C4; + constexpr static int magik_ext_adrs[3] = {0x03, 0xb, 0x44f}; + constexpr static uint8_t magik[4] = {0xF0, 0x0D, 0xC0, 0xDE}; + constexpr static uint8_t external_magik[9] = {0x64, 0x6c, 0x31, 0x31, 0x23, 0x39, 0x38, 0x33, 0x35}; + + // Download and upload + bool processDownloadRequest(uint16_t wLength, uint16_t *transferBufferLength); + bool processUploadRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength); + // Address pointer + void setAddressPointerCommand(SetupPacket *request, uint8_t *transferBuffer, uint16_t transferBufferLength); + void changeAddressPointerIfNeeded(); + // Access memory + void eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength); + void eraseMemoryIfNeeded(); + void eraseMemoryIfNeededWithoutErasingAtAll(); + void writeOnMemory(); + void unlockFlashMemory(); + void lockFlashMemoryAndPurgeCaches(); + // Status + bool getStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength); + bool clearStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength); + // State + bool getState(uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t maxSize); + // Abort + bool dfuAbort(uint16_t *transferBufferLength); + // Leave DFU + void leaveDFUAndReset(bool do_reset=true); + /* Erase and Write state. After starting the erase of flash memory, the user * can no longer leave DFU mode by pressing the Back key of the keyboard. This * way, we prevent the user from interrupting a software download. After every * software download, the calculator resets, which unlocks the "exit on * pressing back". */ - void willErase() { m_isErasingAndWriting = true; } + void willErase() { m_isErasingAndWriting = true; } + void reset_custom_vars() {m_temp_is_valid = true; m_last_memoryFlashed = -1; m_lastPageErased = -1;} - Device * m_device; - Status m_status; - State m_state; - uint32_t m_addressPointer; - uint32_t m_potentialNewAddressPointer; - int32_t m_erasePage; - uint8_t m_largeBuffer[Endpoint0::MaxTransferSize]; - uint16_t m_largeBufferLength; - uint32_t m_writeAddress; - uint8_t m_bInterfaceAlternateSetting; - bool m_isErasingAndWriting; -}; + Device *m_device; + Status m_status; + State m_state; + uint32_t m_addressPointer; + uint32_t m_potentialNewAddressPointer; + int32_t m_erasePage; + uint8_t m_largeBuffer[Endpoint0::MaxTransferSize]; + uint16_t m_largeBufferLength; + uint32_t m_writeAddress; + uint32_t m_eraseAddress; + uint8_t m_bInterfaceAlternateSetting; + bool m_isErasingAndWriting; + bool m_isFirstInternalFlash; + bool m_temp_is_valid; + bool m_isInternalLocked; + bool m_isFirstExternalFlash; + int m_last_memoryFlashed; // -1: aucune; 0: internal; 1: external + int m_lastPageErased; // -1 par défaut + bool m_dfuUnlocked; + int m_dfuLevel; + }; -} -} + } + } } #endif diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 82868a88c..f68cd2e96 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -11,9 +11,9 @@ extern char _dfu_bootloader_flash_end; namespace Ion { namespace USB { -typedef void (*PollFunctionPointer)(bool exitWithKeyboard); +typedef void (*PollFunctionPointer)(bool exitWithKeyboard, bool unlocked, int level); -void DFU(bool exitWithKeyboard) { +void DFU(bool exitWithKeyboard, bool unlocked, int level) { /* DFU transfers can serve two purposes: * - Transfering RAM data between the machine and a host, e.g. Python scripts @@ -74,7 +74,7 @@ void DFU(bool exitWithKeyboard) { * add-symbol-file ion/src/device/usb/dfu.elf 0x20038000 */ - dfu_bootloader_entry(exitWithKeyboard); + dfu_bootloader_entry(exitWithKeyboard, unlocked, level); /* 5- Restore interrupts */ Device::Timing::init(); diff --git a/ion/src/device/shared/usb/dfu_xip.cpp b/ion/src/device/shared/usb/dfu_xip.cpp index 753948f73..dce014cd2 100644 --- a/ion/src/device/shared/usb/dfu_xip.cpp +++ b/ion/src/device/shared/usb/dfu_xip.cpp @@ -3,8 +3,8 @@ namespace Ion { namespace USB { -void DFU(bool exitWithKeyboard) { - Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard); +void DFU(bool exitWithKeyboard, bool unlocked, int level) { + Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, unlocked, level); } } diff --git a/ion/src/shared/dummy/usb.cpp b/ion/src/shared/dummy/usb.cpp index ce3c0fd2c..4f3554dff 100644 --- a/ion/src/shared/dummy/usb.cpp +++ b/ion/src/shared/dummy/usb.cpp @@ -14,7 +14,7 @@ bool isEnumerated() { void clearEnumerationInterrupt() { } -void DFU(bool) { +void DFU(bool, bool, int) { } void enable() { diff --git a/ion/src/simulator/3ds/driver/usb.cpp b/ion/src/simulator/3ds/driver/usb.cpp index 59e83bea5..17cea3ce7 100644 --- a/ion/src/simulator/3ds/driver/usb.cpp +++ b/ion/src/simulator/3ds/driver/usb.cpp @@ -13,7 +13,7 @@ bool Ion::USB::isEnumerated() { void Ion::USB::clearEnumerationInterrupt() { } -void Ion::USB::DFU(bool) { +void Ion::USB::DFU(bool, bool, int) { } void Ion::USB::enable() { diff --git a/kandinsky/fonts/code_points.h b/kandinsky/fonts/code_points.h index 1804fc64a..8671637f9 100644 --- a/kandinsky/fonts/code_points.h +++ b/kandinsky/fonts/code_points.h @@ -105,6 +105,7 @@ uint32_t CodePoints[] = { 0x7d, // } // RIGHT CURLY BRACKET 0x7e, // ~ // TILDE + 0xa1, // ¡ // INVERTED EXCLAMATION MARK 0xb0, // ° // DEGREE SIGN 0xb7, // · // MIDDLE DOT