mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
@@ -147,7 +147,7 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
|
||||
* pictogram. */
|
||||
updateBatteryState();
|
||||
if (switchTo(usbConnectedAppSnapshot())) {
|
||||
Ion::USB::DFU(true, GlobalPreferences::sharedGlobalPreferences()->dfuUnlocked(), GlobalPreferences::sharedGlobalPreferences()->dfuLevel());
|
||||
Ion::USB::DFU(true);
|
||||
// Update LED when exiting DFU mode
|
||||
Ion::LED::updateColorWithPlugAndCharge();
|
||||
bool switched = switchTo(activeSnapshot);
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
|
||||
#endif
|
||||
SettingsMessageTree(I18n::Message::BetaPopUp),
|
||||
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren),
|
||||
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren)};
|
||||
|
||||
constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu);
|
||||
|
||||
@@ -17,7 +17,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
|
||||
#ifdef HAS_CODE
|
||||
SettingsMessageTree(I18n::Message::CodeApp, s_codeChildren),
|
||||
#endif
|
||||
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren),
|
||||
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
|
||||
#endif
|
||||
SettingsMessageTree(I18n::Message::UpdatePopUp),
|
||||
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren),
|
||||
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
|
||||
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)};
|
||||
|
||||
constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu);
|
||||
|
||||
@@ -1,21 +1,49 @@
|
||||
|
||||
bootloader_src += $(addprefix bootloader/,\
|
||||
utility.cpp \
|
||||
boot.cpp \
|
||||
main.cpp \
|
||||
kernel_header.cpp \
|
||||
userland_header.cpp \
|
||||
slot.cpp \
|
||||
interface.cpp \
|
||||
jump_to_firmware.s \
|
||||
trampoline.cpp \
|
||||
usb_desc.cpp \
|
||||
recovery.cpp \
|
||||
usb_data.cpp \
|
||||
)
|
||||
|
||||
bootloader_src += $(addprefix bootloader/slots/, \
|
||||
slot_exam_mode.cpp \
|
||||
slot.cpp \
|
||||
userland_header.cpp \
|
||||
kernel_header.cpp \
|
||||
)
|
||||
|
||||
bootloader_src += $(addprefix bootloader/drivers/, \
|
||||
stm32_drivers.cpp \
|
||||
)
|
||||
|
||||
bootloader_src += $(addprefix bootloader/interface/static/, \
|
||||
interface.cpp \
|
||||
)
|
||||
|
||||
bootloader_src += $(addprefix bootloader/interface/src/,\
|
||||
menu.cpp \
|
||||
)
|
||||
|
||||
|
||||
bootloader_src += $(addprefix bootloader/interface/menus/, \
|
||||
about.cpp \
|
||||
home.cpp \
|
||||
dfu.cpp \
|
||||
installer.cpp \
|
||||
warning.cpp \
|
||||
slot_recovery.cpp \
|
||||
crash.cpp \
|
||||
)
|
||||
|
||||
bootloader_images = $(addprefix bootloader/, \
|
||||
cable.png \
|
||||
computer.png \
|
||||
)
|
||||
|
||||
bootloader_src += $(filter-out ion/src/device/shared/drivers/usb_desc.cpp,$(ion_src)) $(simple_kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images)
|
||||
bootloader_src += $(ion_src) $(simple_kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images)
|
||||
|
||||
$(eval $(call depends_on_image,bootloader/interface.cpp,$(bootloader_images)))
|
||||
$(eval $(call depends_on_image,bootloader/interface/static/interface.cpp,$(bootloader_images)))
|
||||
$(eval $(call depends_on_image,bootloader/interface/src/menu.cpp,$(bootloader_images)))
|
||||
|
||||
@@ -1,66 +1,151 @@
|
||||
#include <bootloader/boot.h>
|
||||
#include <bootloader/slot.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <bootloader/interface/static/interface.h>
|
||||
#include <bootloader/recovery.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
#include <bootloader/utility.h>
|
||||
#include <bootloader/interface/menus/home.h>
|
||||
#include <bootloader/interface/menus/warning.h>
|
||||
#include <ion.h>
|
||||
#include <ion/src/device/shared/drivers/flash.h>
|
||||
#include <ion/src/device/shared/drivers/board.h>
|
||||
#include <ion/src/device/shared/drivers/reset.h>
|
||||
#include <bootloader/interface.h>
|
||||
#include <ion/src/device/shared/drivers/external_flash.h>
|
||||
#include <ion/src/device/n0110/drivers/power.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
using namespace Utility;
|
||||
|
||||
extern "C" {
|
||||
extern char _fake_isr_function_start;
|
||||
}
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
BootConfig * Boot::config() {
|
||||
static BootConfig * bootcfg = new BootConfig();
|
||||
return bootcfg;
|
||||
}
|
||||
|
||||
BootMode Boot::mode() {
|
||||
// We use the exam mode driver as storage for the boot mode
|
||||
uint8_t mode = Ion::ExamMode::FetchExamMode();
|
||||
|
||||
if (mode > 3)
|
||||
return Unknown;
|
||||
|
||||
return (BootMode) mode;
|
||||
return BootMode::SlotA;
|
||||
}
|
||||
|
||||
void Boot::setMode(BootMode mode) {
|
||||
BootMode currentMode = Boot::mode();
|
||||
if (currentMode == mode)
|
||||
return;
|
||||
// We dont use the exam mode driver as storage for the boot mode because we need the 16k of storage x)
|
||||
}
|
||||
|
||||
assert(mode != BootMode::Unknown);
|
||||
int8_t deltaMode = (int8_t)mode - (int8_t)currentMode;
|
||||
deltaMode = deltaMode < 0 ? deltaMode + 4 : deltaMode;
|
||||
assert(deltaMode > 0);
|
||||
Ion::ExamMode::IncrementExamMode(deltaMode);
|
||||
void Boot::busError() {
|
||||
Ion::Device::Flash::ClearInternalFlashErrors();
|
||||
asm("mov r12, lr");
|
||||
if (config()->isBooting()) {
|
||||
asm("mov lr, r12");
|
||||
asm("bx lr");
|
||||
}
|
||||
Bootloader::Recovery::crash_handler("BusFault");
|
||||
}
|
||||
|
||||
bool Boot::isKernelPatched(const Slot & s) {
|
||||
if (s.userlandHeader()->isOmega()) {
|
||||
// we don't need to patch the kernel
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's an epsilon kernel, so we need to check if it's patched
|
||||
|
||||
uint32_t origin_isr = s.address() + sizeof(Bootloader::KernelHeader) - sizeof(uint32_t) * 3;
|
||||
|
||||
if (*(uint32_t *)(origin_isr + sizeof(uint32_t) * 12) == (uint32_t)0x0) {
|
||||
// fake epsilon
|
||||
return true;
|
||||
}
|
||||
|
||||
// return *(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;*(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;*(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;
|
||||
return *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;
|
||||
}
|
||||
|
||||
__attribute((section(".fake_isr_function"))) __attribute__((used)) void Boot::flash_interrupt() {
|
||||
// a simple function
|
||||
Ion::Device::Flash::ClearInternalFlashErrors();
|
||||
asm("bx lr");
|
||||
}
|
||||
|
||||
void Boot::patchKernel(const Slot & s) {
|
||||
uint32_t origin_isr = s.address() + sizeof(Bootloader::KernelHeader) - sizeof(uint32_t) * 3 - 0x90000000;
|
||||
// we allocate a big buffer to store the first sector
|
||||
uint8_t data[1024*4];
|
||||
memcpy(data, (void*)0x90000000, 1024*4);
|
||||
uint32_t dummy_address = (uint32_t)&_fake_isr_function_start + 1;
|
||||
uint8_t * ptr = (uint8_t *)&dummy_address;
|
||||
data[origin_isr + sizeof(uint32_t) * 6] = ptr[0]; // BusFault
|
||||
data[origin_isr + sizeof(uint32_t) * 6 + 1] = ptr[1];
|
||||
data[origin_isr + sizeof(uint32_t) * 6 + 2] = ptr[2];
|
||||
data[origin_isr + sizeof(uint32_t) * 6 + 3] = ptr[3];
|
||||
|
||||
// data[origin_isr + sizeof(uint32_t) * 5] = ptr[0]; // MemManage
|
||||
// data[origin_isr + sizeof(uint32_t) * 5 + 1] = ptr[1];
|
||||
// data[origin_isr + sizeof(uint32_t) * 5 + 2] = ptr[2];
|
||||
// data[origin_isr + sizeof(uint32_t) * 5 + 3] = ptr[3];
|
||||
|
||||
data[origin_isr + sizeof(uint32_t) * 7] = ptr[0]; // UsageFault
|
||||
data[origin_isr + sizeof(uint32_t) * 7 + 1] = ptr[1];
|
||||
data[origin_isr + sizeof(uint32_t) * 7 + 2] = ptr[2];
|
||||
data[origin_isr + sizeof(uint32_t) * 7 + 3] = ptr[3];
|
||||
|
||||
// data[origin_isr + sizeof(uint32_t) * 4] = ptr[0];//hardfault
|
||||
// data[origin_isr + sizeof(uint32_t) * 4 + 1] = ptr[1];
|
||||
// data[origin_isr + sizeof(uint32_t) * 4 + 2] = ptr[2];
|
||||
// data[origin_isr + sizeof(uint32_t) * 4 + 3] = ptr[3];
|
||||
|
||||
Ion::Device::ExternalFlash::EraseSector(0);
|
||||
Ion::Device::ExternalFlash::WriteMemory((uint8_t*)0x90000000, data, 1024*4);
|
||||
}
|
||||
|
||||
void Boot::bootSlot(Bootloader::Slot s) {
|
||||
config()->setSlot(&s);
|
||||
if (!s.userlandHeader()->isOmega() && !s.userlandHeader()->isUpsilon()) {
|
||||
// We are trying to boot epsilon, so we check the version and show an advertisement if needed
|
||||
const char * version = s.userlandHeader()->version();
|
||||
const char * min = "18.2.4";
|
||||
int versionSum = Utility::versionSum(version, strlen(version));
|
||||
int minimalVersionTrigger = Utility::versionSum(min, strlen(min));
|
||||
if (versionSum >= minimalVersionTrigger) {
|
||||
WarningMenu menu = WarningMenu();
|
||||
menu.open();
|
||||
return;
|
||||
}
|
||||
}
|
||||
bootSelectedSlot();
|
||||
}
|
||||
|
||||
void Boot::bootSelectedSlot() {
|
||||
lockInternal();
|
||||
config()->setBooting(true);
|
||||
Ion::Device::Flash::EnableInternalSessionLock();
|
||||
config()->slot()->boot();
|
||||
}
|
||||
|
||||
__attribute__((noreturn)) void Boot::boot() {
|
||||
assert(mode() != BootMode::Unknown);
|
||||
|
||||
if (!Slot::A().kernelHeader()->isValid() && !Slot::B().kernelHeader()->isValid()) {
|
||||
// Bootloader if both invalid
|
||||
bootloader();
|
||||
} else if (!Slot::A().kernelHeader()->isValid()) {
|
||||
// If slot A is invalid and B valid, boot B
|
||||
setMode(BootMode::SlotB);
|
||||
Slot::B().boot();
|
||||
} else if (!Slot::B().kernelHeader()->isValid()) {
|
||||
// If slot B is invalid and A valid, boot A
|
||||
setMode(BootMode::SlotA);
|
||||
Slot::A().boot();
|
||||
} else {
|
||||
// Both valid, boot the selected one
|
||||
if (mode() == BootMode::SlotA) {
|
||||
Slot::A().boot();
|
||||
} else if (mode() == BootMode::SlotB) {
|
||||
Slot::B().boot();
|
||||
}
|
||||
Boot::config()->clearSlot();
|
||||
Boot::config()->setBooting(false);
|
||||
|
||||
while (true) {
|
||||
HomeMenu menu = HomeMenu();
|
||||
menu.open(true);
|
||||
}
|
||||
|
||||
// Achivement unlocked: How Did We Get Here?
|
||||
// Achievement unlocked: How Did We Get Here?
|
||||
bootloader();
|
||||
}
|
||||
|
||||
__attribute__ ((noreturn)) void Boot::bootloader() {
|
||||
void Boot::bootloader() {
|
||||
USBData data = USBData::DEFAULT();
|
||||
for(;;) {
|
||||
// Draw the interfaces and infos
|
||||
Bootloader::Interface::draw();
|
||||
Bootloader::Interface::drawFlasher();
|
||||
|
||||
// Enable USB
|
||||
Ion::USB::enable();
|
||||
@@ -70,13 +155,34 @@ __attribute__ ((noreturn)) void Boot::bootloader() {
|
||||
// If we pressed back while waiting, reset.
|
||||
uint64_t scan = Ion::Keyboard::scan();
|
||||
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
|
||||
Ion::Device::Reset::core();
|
||||
// Disable USB, redraw the menu and return
|
||||
Ion::USB::disable();
|
||||
return;
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
|
||||
Ion::Power::standby(); // Force a core reset to exit
|
||||
}
|
||||
} while (!Ion::USB::isEnumerated());
|
||||
|
||||
// Launch the DFU stack, allowing to press Back to quit and reset
|
||||
Ion::USB::DFU(true);
|
||||
Ion::USB::DFU(true, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void Boot::jumpToInternalBootloader() {
|
||||
Ion::Device::Board::jumpToInternalBootloader();
|
||||
}
|
||||
|
||||
void Boot::lockInternal() {
|
||||
Ion::Device::Flash::DisableInternalProtection();
|
||||
Ion::Device::Flash::SetInternalSectorProtection(0, true);
|
||||
Ion::Device::Flash::SetInternalSectorProtection(1, true);
|
||||
Ion::Device::Flash::SetInternalSectorProtection(2, true);
|
||||
Ion::Device::Flash::SetInternalSectorProtection(3, true);
|
||||
Ion::Device::Flash::EnableInternalProtection();
|
||||
}
|
||||
|
||||
void Boot::enableFlashIntr() {
|
||||
Ion::Device::Flash::EnableInternalFlashInterrupt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,9 +2,25 @@
|
||||
#define BOOTLOADER_BOOT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class BootConfig {
|
||||
public:
|
||||
BootConfig() : m_slot(nullptr), m_booting(false) {};
|
||||
|
||||
void setSlot(Slot * slot) { m_slot = slot; }
|
||||
Slot * slot() const { return m_slot; }
|
||||
void clearSlot() { m_slot = nullptr; }
|
||||
|
||||
void setBooting(bool booting) { m_booting = booting; }
|
||||
bool isBooting() const { return m_booting; }
|
||||
private:
|
||||
Bootloader::Slot * m_slot;
|
||||
bool m_booting;
|
||||
};
|
||||
|
||||
enum BootMode: uint8_t {
|
||||
SlotA = 0,
|
||||
SlotB = 1,
|
||||
@@ -19,10 +35,26 @@ class Boot {
|
||||
public:
|
||||
static BootMode mode();
|
||||
static void setMode(BootMode mode);
|
||||
static BootConfig * config();
|
||||
|
||||
static bool isKernelPatched(const Slot & slot);
|
||||
static void patchKernel(const Slot & slot);
|
||||
|
||||
static void busError();
|
||||
|
||||
__attribute__ ((noreturn)) static void boot();
|
||||
__attribute__ ((noreturn)) static void bootloader();
|
||||
|
||||
static void bootSlot(Bootloader::Slot slot);
|
||||
static void bootSelectedSlot();
|
||||
__attribute__ ((noreturn)) static void jumpToInternalBootloader();
|
||||
__attribute((section(".fake_isr_function"))) __attribute__((used)) static void flash_interrupt();
|
||||
|
||||
static void bootloader();
|
||||
static void lockInternal();
|
||||
static void enableFlashIntr();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
129
bootloader/boot/isr.c
Normal file
129
bootloader/boot/isr.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "isr.h"
|
||||
extern const void * _stack_start;
|
||||
|
||||
/* Interrupt Service Routines are void->void functions */
|
||||
typedef void(*ISR)(void);
|
||||
|
||||
/* Notice: The Cortex-M4 expects all jumps to be made at an odd address when
|
||||
* jumping to Thumb code. For example, if you want to execute Thumb code at
|
||||
* address 0x100, you'll have to jump to 0x101. Luckily, this idiosyncrasy is
|
||||
* properly handled by the C compiler that will generate proper addresses when
|
||||
* using function pointers. */
|
||||
|
||||
#define INITIALISATION_VECTOR_SIZE 0x71
|
||||
|
||||
ISR InitialisationVector[INITIALISATION_VECTOR_SIZE]
|
||||
__attribute__((section(".isr_vector_table")))
|
||||
__attribute__((used))
|
||||
= {
|
||||
(ISR)&_stack_start, // Stack start
|
||||
start, // Reset service routine,
|
||||
0, // NMI service routine,
|
||||
hard_fault_handler, // HardFault service routine,
|
||||
mem_fault_handler, // MemManage service routine,
|
||||
bus_fault_handler, // BusFault service routine,
|
||||
usage_fault_handler, // UsageFault service routine,
|
||||
0, 0, 0, 0, // Reserved
|
||||
0, // SVCall service routine,
|
||||
0, // DebugMonitor service routine,
|
||||
0, // Reserved
|
||||
0, // PendSV service routine,
|
||||
isr_systick, // SysTick service routine
|
||||
0, // WWDG service routine
|
||||
0, // PVD service routine
|
||||
0, // TampStamp service routine
|
||||
0, // RtcWakeup service routine
|
||||
0, // Flash service routine
|
||||
0, // RCC service routine
|
||||
0, // EXTI0 service routine
|
||||
0, // EXTI1 service routine
|
||||
0, // EXTI2 service routine
|
||||
0, // EXTI3 service routine
|
||||
0, // EXTI4 service routine
|
||||
0, // DMA1Stream0 service routine
|
||||
0, // DMA1Stream1 service routine
|
||||
0, // DMA1Stream2 service routine
|
||||
0, // DMA1Stream3 service routine
|
||||
0, // DMA1Stream4 service routine
|
||||
0, // DMA1Stream5 service routine
|
||||
0, // DMA1Stream6 service routine
|
||||
0, // ADC1 global interrupt
|
||||
0, // CAN1 TX interrupt
|
||||
0, // CAN1 RX0 interrupt
|
||||
0, // CAN1 RX1 interrupt
|
||||
0, // CAN1 SCE interrupt
|
||||
0, // EXTI Line[9:5] interrupts
|
||||
0, // TIM1 Break interrupt and TIM9 global interrupt
|
||||
0, // TIM1 update interrupt and TIM10 global interrupt
|
||||
0, // TIM1 Trigger & Commutation interrupts and TIM11 global interrupt
|
||||
0, // TIM1 Capture Compare interrupt
|
||||
0, // TIM2 global interrupt
|
||||
0, // TIM3 global interrupt
|
||||
0, // TIM4 global interrupt
|
||||
0, // I2C1 global event interrupt
|
||||
0, // I2C1 global error interrupt
|
||||
0, // I2C2 global event interrupt
|
||||
0, // I2C2 global error interrupt
|
||||
0, // SPI1 global interrupt
|
||||
0, // SPI2 global interrupt
|
||||
0, // USART1 global interrupt
|
||||
0, // USART2 global interrupt
|
||||
0, // USART3 global interrupt
|
||||
0, // EXTI Line[15:10] interrupts
|
||||
0, // EXTI Line 17 interrupt RTC Alarms (A and B) through EXTI line interrupt
|
||||
0, // EXTI Line 18 interrupt / USB On-The-Go FS Wakeup through EXTI line interrupt
|
||||
0, // TIM8 Break interrupt TIM12 global interrupt
|
||||
0, // TIM8 Update interrupt TIM13 global interrupt
|
||||
0, // TIM8 Trigger & Commutation interrupt TIM14 global interrupt
|
||||
0, // TIM8 Cap/Com interrupt
|
||||
0, // DMA1 global interrupt Channel 7
|
||||
0, // FSMC global interrupt
|
||||
0, // SDIO global interrupt
|
||||
0, // TIM5 global interrupt
|
||||
0, // SPI3 global interrupt
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // TIM6 global interrupt
|
||||
0, // TIM7 global interrupt
|
||||
0, // DMA2 Stream0 global interrupt
|
||||
0, // DMA2 Stream1 global interrupt
|
||||
0, // DMA2 Stream2 global interrupt
|
||||
0, // DMA2 Stream3 global interrupt
|
||||
0, // DMA2 Stream4 global interrupt
|
||||
0, // SD filter0 global interrupt
|
||||
0, // SD filter1 global interrupt
|
||||
0, // CAN2 TX interrupt
|
||||
0, // BXCAN2 RX0 interrupt
|
||||
0, // BXCAN2 RX1 interrupt
|
||||
0, // CAN2 SCE interrupt
|
||||
0, // USB On The Go FS global interrupt
|
||||
0, // DMA2 Stream5 global interrupts
|
||||
0, // DMA2 Stream6 global interrupt
|
||||
0, // DMA2 Stream7 global interrupt
|
||||
0, // USART6 global interrupt
|
||||
0, // I2C3 event interrupt
|
||||
0, // I2C3 error interrupt
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // RNG global interrupt
|
||||
0, // FPU global interrupt
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // SPI4 global interrupt
|
||||
0, // SPI5 global interrupt
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // Quad-SPI global interrupt
|
||||
0, // ?
|
||||
0, // ?
|
||||
0, // I2CFMP1 event interrupt
|
||||
0 // I2CFMP1 error interrupt
|
||||
};
|
||||
23
bootloader/boot/isr.h
Normal file
23
bootloader/boot/isr.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef ION_DEVICE_BOOT_ISR_H
|
||||
#define ION_DEVICE_BOOT_ISR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void start();
|
||||
void abort();
|
||||
void isr_systick();
|
||||
|
||||
// Fault handlers
|
||||
|
||||
void hard_fault_handler();
|
||||
void mem_fault_handler();
|
||||
void usage_fault_handler();
|
||||
void bus_fault_handler();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
147
bootloader/boot/rt0.cpp
Normal file
147
bootloader/boot/rt0.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ion.h>
|
||||
#include <bootloader/boot/isr.h>
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/rtc.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/timing.h>
|
||||
#include <bootloader/recovery.h>
|
||||
#include <bootloader/boot.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
|
||||
}
|
||||
|
||||
|
||||
void __attribute__((interrupt, noinline)) isr_systick() {
|
||||
auto t = Ion::Device::Timing::MillisElapsed;
|
||||
t++;
|
||||
Ion::Device::Timing::MillisElapsed = t;
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) hard_fault_handler() {
|
||||
Bootloader::Recovery::crash_handler("HardFault");
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) mem_fault_handler() {
|
||||
Bootloader::Recovery::crash_handler("MemoryFault");
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) usage_fault_handler() {
|
||||
Bootloader::Recovery::crash_handler("UsageFault");
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) bus_fault_handler() {
|
||||
Bootloader::Boot::busError();
|
||||
}
|
||||
|
||||
/* 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
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* This additional function call 'jump_to_external_flash' serves two purposes:
|
||||
* - By default, the compiler is free to inline any function call he wants. If
|
||||
* the compiler decides to inline some functions that make use of VFP
|
||||
* registers, it will need to push VFP them onto the stack in calling
|
||||
* function's prologue.
|
||||
* Problem: in start()'s prologue, we would never had a chance to enable the
|
||||
* FPU since this function is the first thing called after reset.
|
||||
* We can safely assume that neither memcpy, memset, nor any Ion::Device::init*
|
||||
* method will use floating-point numbers, but ion_main very well can.
|
||||
* To make sure ion_main's potential usage of VFP registers doesn't bubble-up to
|
||||
* start(), we isolate it in its very own non-inlined function call.
|
||||
* - To avoid jumping on the external flash when it is shut down, we ensure
|
||||
* there is no symbol references from the internal flash to the external
|
||||
* flash except this jump. In order to do that, we isolate this
|
||||
* jump in a symbol that we link in a special section separated from the
|
||||
* internal flash section. We can than forbid cross references from the
|
||||
* internal flash to the external flash. */
|
||||
|
||||
static void __attribute__((noinline)) jump_to_external_flash() {
|
||||
external_flash_start();
|
||||
}
|
||||
|
||||
/* When 'start' is executed, the external flash is supposed to be shutdown. We
|
||||
* thus forbid inlining to prevent executing this code from 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.
|
||||
* Many things are not initialized yet so the code here has to pay attention. */
|
||||
|
||||
/* Initialize the FPU as early as possible.
|
||||
* For example, static C++ objects are very likely to manipulate float values */
|
||||
Ion::Device::Board::initFPU();
|
||||
|
||||
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
/* 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++
|
||||
* static objects we just have to iterate between theses two addresses and
|
||||
* 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)();
|
||||
}
|
||||
#else
|
||||
/* 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();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* At this point, we initialized clocks and the external flash but no other
|
||||
* peripherals. */
|
||||
Ion::Device::Board::init();
|
||||
|
||||
jump_to_external_flash();
|
||||
|
||||
abort();
|
||||
}
|
||||
462
bootloader/drivers/board.cpp
Normal file
462
bootloader/drivers/board.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
#include <drivers/board.h>
|
||||
#include <drivers/cache.h>
|
||||
#include <drivers/internal_flash.h>
|
||||
#include <drivers/config/clocks.h>
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/external_flash.h>
|
||||
#include <drivers/timing.h>
|
||||
#include <regs/regs.h>
|
||||
#include <ion.h>
|
||||
|
||||
#include <bootloader/drivers/stm32_drivers.h>
|
||||
|
||||
using namespace STM32;
|
||||
typedef void(*ISR)(void);
|
||||
extern ISR InitialisationVector[];
|
||||
|
||||
// Public Ion methods
|
||||
|
||||
const char * Ion::fccId() {
|
||||
return "2ALWP-N0110";
|
||||
}
|
||||
|
||||
// Private Ion::Device methods
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Board {
|
||||
|
||||
using namespace Regs;
|
||||
|
||||
void bootloaderMPU() {
|
||||
// 1. Disable the MPU
|
||||
// 1.1 Memory barrier
|
||||
Cache::dmb();
|
||||
|
||||
// 1.3 Disable the MPU and clear the control register
|
||||
MPU.CTRL()->setENABLE(false);
|
||||
|
||||
MPU.RNR()->setREGION(7);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
MPU.RASR()->setXN(false);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
// 2.3 Enable MPU
|
||||
MPU.CTRL()->setENABLE(true);
|
||||
|
||||
// 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions.
|
||||
Cache::disable();
|
||||
Cache::dsb();
|
||||
Cache::isb();
|
||||
}
|
||||
|
||||
void initMPU() {
|
||||
// 1. Disable the MPU
|
||||
// 1.1 Memory barrier
|
||||
Cache::dmb();
|
||||
|
||||
// 1.2 Disable fault exceptions
|
||||
CORTEX.SHCRS()->setMEMFAULTENA(false);
|
||||
|
||||
// 1.3 Disable the MPU and clear the control register
|
||||
MPU.CTRL()->setENABLE(false);
|
||||
|
||||
// 2. MPU settings
|
||||
// 2.1 Configure a MPU region for the FMC memory area
|
||||
/* This is needed for interfacing with the LCD
|
||||
* We define the whole FMC memory bank 1 as strongly ordered, non-executable
|
||||
* and not accessible. We define the FMC command and data addresses as
|
||||
* writeable non-cachable, non-buffereable and non shareable. */
|
||||
int sector = 0;
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setTEX(2);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
MPU.RASR()->setTEX(2);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000+0x20000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
MPU.RASR()->setTEX(2);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
// 2.2 Configure MPU regions for the QUADSPI peripheral
|
||||
/* L1 Cache can issue speculative reads to any memory address. But, when the
|
||||
* Quad-SPI is in memory-mapped mode, if an access is made to an address
|
||||
* outside of the range defined by FSIZE but still within the 256Mbytes range,
|
||||
* then an AHB error is given (AN4760). To prevent this to happen, we
|
||||
* configure the MPU to define the whole Quad-SPI addressable space as
|
||||
* strongly ordered, non-executable and not accessible. Plus, we define the
|
||||
* Quad-SPI region corresponding to the Expternal Chip as executable and
|
||||
* fully accessible (AN4861). */
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setTEX(0);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_8MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
MPU.RASR()->setXN(false);
|
||||
MPU.RASR()->setTEX(0);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(1);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
// 2.3 Enable MPU
|
||||
MPU.CTRL()->setPRIVDEFENA(true);
|
||||
MPU.CTRL()->setENABLE(true);
|
||||
|
||||
//2.4 Enable fault exceptions
|
||||
CORTEX.SHCRS()->setMEMFAULTENA(true);
|
||||
CORTEX.SHCRS()->setBUSFAULTENA(true);
|
||||
CORTEX.SHCRS()->setUSGFAULTENA(true);
|
||||
|
||||
// 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions.
|
||||
Cache::dsb();
|
||||
Cache::isb();
|
||||
}
|
||||
|
||||
void init() {
|
||||
initFPU();
|
||||
initMPU();
|
||||
initClocks();
|
||||
|
||||
// The bootloader leaves its own after flashing
|
||||
//SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory);
|
||||
// Ensure right location of interrupt vectors
|
||||
CORTEX.VTOR()->setVTOR((void*)&InitialisationVector);
|
||||
|
||||
// Put all inputs as Analog Input, No pull-up nor pull-down
|
||||
// Except for the SWD port (PB3, PA13, PA14)
|
||||
GPIOA.MODER()->set(0xEBFFFFFF);
|
||||
GPIOA.PUPDR()->set(0x24000000);
|
||||
GPIOB.MODER()->set(0xFFFFFFBF);
|
||||
GPIOB.PUPDR()->set(0x00000000);
|
||||
for (int g=2; g<5; g++) {
|
||||
GPIO(g).MODER()->set(0xFFFFFFFF); // All to "Analog"
|
||||
GPIO(g).PUPDR()->set(0x00000000); // All to "None"
|
||||
}
|
||||
|
||||
ExternalFlash::init();
|
||||
// Initiate L1 cache after initiating the external flash
|
||||
Cache::enable();
|
||||
}
|
||||
|
||||
void initClocks() {
|
||||
/* System clock
|
||||
* Configure the CPU at 192 MHz and USB at 48 MHz. */
|
||||
|
||||
/* After reset, the device is using the high-speed internal oscillator (HSI)
|
||||
* as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not
|
||||
* accurate enough for reliable USB operation, so we need to use the external
|
||||
* high-speed oscillator (HSE). */
|
||||
|
||||
// Enable the HSI and wait for it to be ready
|
||||
RCC.CR()->setHSION(true);
|
||||
while(!RCC.CR()->getHSIRDY()) {
|
||||
}
|
||||
|
||||
// Enable the HSE and wait for it to be ready
|
||||
RCC.CR()->setHSEON(true);
|
||||
while(!RCC.CR()->getHSERDY()) {
|
||||
}
|
||||
|
||||
// Enable PWR peripheral clock
|
||||
RCC.APB1ENR()->setPWREN(true);
|
||||
|
||||
/* To pass electromagnetic compatibility tests, we activate the Spread
|
||||
* Spectrum clock generation, which adds jitter to the PLL clock in order to
|
||||
* "lower peak-energy on the central frequency" and its harmonics.
|
||||
* It must be done before enabling the PLL. */
|
||||
class RCC::SSCGR sscgr(0); // Reset value
|
||||
sscgr.setMODPER(Clocks::Config::SSCG_MODPER);
|
||||
sscgr.setINCSTEP(Clocks::Config::SSCG_INCSTEP);
|
||||
sscgr.setSPREADSEL(RCC::SSCGR::SPREADSEL::CenterSpread);
|
||||
sscgr.setSSCGEN(true);
|
||||
RCC.SSCGR()->set(sscgr);
|
||||
|
||||
/* Given the crystal used on our device, the HSE will oscillate at 8 MHz. By
|
||||
* piping it through a phase-locked loop (PLL) we can derive other frequencies
|
||||
* for use in different parts of the system. */
|
||||
|
||||
// Configure the PLL ratios and use HSE as a PLL input
|
||||
RCC.PLLCFGR()->setPLLM(Clocks::Config::PLL_M);
|
||||
RCC.PLLCFGR()->setPLLN(Clocks::Config::PLL_N);
|
||||
RCC.PLLCFGR()->setPLLQ(Clocks::Config::PLL_Q);
|
||||
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE);
|
||||
|
||||
// Enable the PLL and wait for it to be ready
|
||||
RCC.CR()->setPLLON(true);
|
||||
|
||||
// Enable Over-drive
|
||||
PWR.CR()->setODEN(true);
|
||||
while(!PWR.CSR()->getODRDY()) {
|
||||
}
|
||||
|
||||
PWR.CR()->setODSWEN(true);
|
||||
while(!PWR.CSR()->getODSWRDY()) {
|
||||
}
|
||||
|
||||
// Choose Voltage scale 1
|
||||
PWR.CR()->setVOS(PWR::CR::Voltage::Scale1);
|
||||
while (!PWR.CSR()->getVOSRDY()) {}
|
||||
|
||||
/* After reset the Flash runs as fast as the CPU. When we clock the CPU faster
|
||||
* the flash memory cannot follow and therefore flash memory accesses need to
|
||||
* wait a little bit.
|
||||
* The spec tells us that at 2.8V and over 210MHz the flash expects 7 WS. */
|
||||
FLASH.ACR()->setLATENCY(7);
|
||||
|
||||
/* Enable prefetching flash instructions */
|
||||
/* Fetching instructions increases slightly the power consumption but the
|
||||
* increase is negligible compared to the screen consumption. */
|
||||
FLASH.ACR()->setPRFTEN(true);
|
||||
|
||||
/* Enable the ART */
|
||||
FLASH.ACR()->setARTEN(true);
|
||||
|
||||
// 192 MHz is too fast for APB1. Divide it by four to reach 48 MHz
|
||||
RCC.CFGR()->setPPRE1(Clocks::Config::APB1PrescalerReg);
|
||||
// 192 MHz is too fast for APB2. Divide it by two to reach 96 MHz
|
||||
RCC.CFGR()->setPPRE2(Clocks::Config::APB2PrescalerReg);
|
||||
|
||||
while(!RCC.CR()->getPLLRDY()) {
|
||||
}
|
||||
|
||||
// Use the PLL output as a SYSCLK source
|
||||
RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
|
||||
while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
|
||||
}
|
||||
|
||||
// Now that we don't need use it anymore, turn the HSI off
|
||||
RCC.CR()->setHSION(false);
|
||||
|
||||
// Peripheral clocks
|
||||
|
||||
// AHB1 bus
|
||||
// Our peripherals are using GPIO A, B, C, D and E.
|
||||
// We're not using the CRC nor DMA engines.
|
||||
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
|
||||
ahb1enr.setGPIOAEN(true);
|
||||
ahb1enr.setGPIOBEN(true);
|
||||
ahb1enr.setGPIOCEN(true);
|
||||
ahb1enr.setGPIODEN(true);
|
||||
ahb1enr.setGPIOEEN(true);
|
||||
ahb1enr.setDMA2EN(true);
|
||||
RCC.AHB1ENR()->set(ahb1enr);
|
||||
|
||||
// AHB2 bus
|
||||
RCC.AHB2ENR()->setOTGFSEN(true);
|
||||
|
||||
// AHB3 bus
|
||||
RCC.AHB3ENR()->setFSMCEN(true);
|
||||
|
||||
// APB1 bus
|
||||
// We're using TIM3 for the LEDs
|
||||
RCC.APB1ENR()->setTIM3EN(true);
|
||||
RCC.APB1ENR()->setPWREN(true);
|
||||
RCC.APB1ENR()->setRTCAPB(true);
|
||||
|
||||
// APB2 bus
|
||||
class RCC::APB2ENR apb2enr(0); // Reset value
|
||||
apb2enr.setADC1EN(true);
|
||||
apb2enr.setSYSCFGEN(true);
|
||||
apb2enr.setUSART6EN(true); // TODO required if building bench target only?
|
||||
RCC.APB2ENR()->set(apb2enr);
|
||||
|
||||
// Configure clocks in sleep mode
|
||||
// AHB1 peripheral clock enable in low-power mode register
|
||||
class RCC::AHB1LPENR ahb1lpenr(0x7EF7B7FF); // Reset value
|
||||
ahb1lpenr.setGPIOALPEN(true); // Enable IO port A for Charging/USB plug/Keyboard pins
|
||||
ahb1lpenr.setGPIOBLPEN(true); // Enable IO port B for LED pins
|
||||
ahb1lpenr.setGPIOCLPEN(true); // Enable IO port C for LED/Keyboard pins
|
||||
ahb1lpenr.setGPIODLPEN(false); // Disable IO port D (LCD...)
|
||||
ahb1lpenr.setGPIOELPEN(true); // Enable IO port E for Keyboard/Battery pins
|
||||
ahb1lpenr.setGPIOFLPEN(false); // Disable IO port F
|
||||
ahb1lpenr.setGPIOGLPEN(false); // Disable IO port G
|
||||
ahb1lpenr.setGPIOHLPEN(false); // Disable IO port H
|
||||
ahb1lpenr.setGPIOILPEN(false); // Disable IO port I
|
||||
ahb1lpenr.setCRCLPEN(false);
|
||||
ahb1lpenr.setFLITFLPEN(false);
|
||||
ahb1lpenr.setSRAM1LPEN(false);
|
||||
ahb1lpenr.setDMA1LPEN(false);
|
||||
ahb1lpenr.setDMA2LPEN(false);
|
||||
ahb1lpenr.setAXILPEN(false);
|
||||
ahb1lpenr.setSRAM2LPEN(false);
|
||||
ahb1lpenr.setBKPSRAMLPEN(false);
|
||||
ahb1lpenr.setDTCMLPEN(false);
|
||||
ahb1lpenr.setOTGHSLPEN(false);
|
||||
ahb1lpenr.setOTGHSULPILPEN(false);
|
||||
RCC.AHB1LPENR()->set(ahb1lpenr);
|
||||
|
||||
// AHB2 peripheral clock enable in low-power mode register
|
||||
class RCC::AHB2LPENR ahb2lpenr(0x000000F1); // Reset value
|
||||
ahb2lpenr.setOTGFSLPEN(false);
|
||||
ahb2lpenr.setRNGLPEN(false);
|
||||
ahb2lpenr.setAESLPEN(false);
|
||||
RCC.AHB2LPENR()->set(ahb2lpenr);
|
||||
|
||||
// AHB3 peripheral clock enable in low-power mode register
|
||||
class RCC::AHB3LPENR ahb3lpenr(0x00000003); // Reset value
|
||||
ahb3lpenr.setFMCLPEN(false);
|
||||
ahb3lpenr.setQSPILPEN(false);
|
||||
RCC.AHB3LPENR()->set(ahb3lpenr);
|
||||
|
||||
// APB1 peripheral clock enable in low-power mode register
|
||||
class RCC::APB1LPENR apb1lpenr(0xFFFFCBFF); // Reset value
|
||||
apb1lpenr.setTIM2LPEN(false);
|
||||
apb1lpenr.setTIM3LPEN(true); // Enable TIM3 in sleep mode for LEDs
|
||||
apb1lpenr.setTIM4LPEN(false);
|
||||
apb1lpenr.setTIM5LPEN(false);
|
||||
apb1lpenr.setTIM6LPEN(false);
|
||||
apb1lpenr.setTIM7LPEN(false);
|
||||
apb1lpenr.setTIM12LPEN(false);
|
||||
apb1lpenr.setTIM13LPEN(false);
|
||||
apb1lpenr.setTIM14LPEN(false);
|
||||
apb1lpenr.setRTCAPBLPEN(false);
|
||||
apb1lpenr.setWWDGLPEN(false);
|
||||
apb1lpenr.setSPI2LPEN(false);
|
||||
apb1lpenr.setSPI3LPEN(false);
|
||||
apb1lpenr.setUSART2LPEN(false);
|
||||
apb1lpenr.setUSART3LPEN(false);
|
||||
apb1lpenr.setI2C1LPEN(false);
|
||||
apb1lpenr.setI2C2LPEN(false);
|
||||
apb1lpenr.setI2C3LPEN(false);
|
||||
apb1lpenr.setCAN1LPEN(false);
|
||||
apb1lpenr.setPWRLPEN(false);
|
||||
apb1lpenr.setLPTIM1LPEN(false);
|
||||
apb1lpenr.setUSART4LPEN(false);
|
||||
apb1lpenr.setUSART5LPEN(false);
|
||||
apb1lpenr.setOTGHSLPEN(false);
|
||||
apb1lpenr.setOTGHSULPILPEN(false);
|
||||
RCC.APB1LPENR()->set(apb1lpenr);
|
||||
|
||||
// APB2 peripheral clock enable in low-power mode register
|
||||
class RCC::APB2LPENR apb2lpenr(0x04F77F33); // Reset value
|
||||
apb2lpenr.setTIM1LPEN(false);
|
||||
apb2lpenr.setTIM8LPEN(false);
|
||||
apb2lpenr.setUSART1LPEN(false);
|
||||
apb2lpenr.setUSART6LPEN(false);
|
||||
apb2lpenr.setADC1LPEN(false);
|
||||
apb2lpenr.setSPI1LPEN(false);
|
||||
apb2lpenr.setSPI4LPEN(false);
|
||||
apb2lpenr.setSYSCFGLPEN(false);
|
||||
apb2lpenr.setTIM9LPEN(false);
|
||||
apb2lpenr.setTIM10LPEN(false);
|
||||
apb2lpenr.setTIM11LPEN(false);
|
||||
apb2lpenr.setSPI5LPEN(false);
|
||||
apb2lpenr.setSDMMC2LPEN(false);
|
||||
apb2lpenr.setADC2LPEN(false);
|
||||
apb2lpenr.setADC3LPEN(false);
|
||||
apb2lpenr.setSAI1LPEN(false);
|
||||
apb2lpenr.setSAI2LPEN(false);
|
||||
RCC.APB2LPENR()->set(apb2lpenr);
|
||||
}
|
||||
|
||||
void shutdownClocks(bool keepLEDAwake) {
|
||||
// APB2 bus
|
||||
RCC.APB2ENR()->set(0); // Reset value
|
||||
|
||||
// AHB2 bus
|
||||
RCC.AHB2ENR()->set(0); // Reset value
|
||||
|
||||
// AHB3 bus
|
||||
RCC.AHB3ENR()->set(0); // Reset value
|
||||
|
||||
// APB1
|
||||
class RCC::APB1ENR apb1enr(0); // Reset value
|
||||
// AHB1 bus
|
||||
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
|
||||
if (keepLEDAwake) {
|
||||
apb1enr.setTIM3EN(true);
|
||||
ahb1enr.setGPIOBEN(true);
|
||||
}
|
||||
RCC.APB1ENR()->set(apb1enr);
|
||||
RCC.AHB1ENR()->set(ahb1enr);
|
||||
}
|
||||
|
||||
constexpr int k_pcbVersionOTPIndex = 0;
|
||||
|
||||
/* As we want the PCB versions to be in ascending order chronologically, and
|
||||
* because the OTP are initialized with 1s, we store the bitwise-not of the
|
||||
* version number. This way, devices with blank OTP are considered version 0. */
|
||||
|
||||
PCBVersion pcbVersion() {
|
||||
#if IN_FACTORY
|
||||
/* When flashing for the first time, we want all systems that depend on the
|
||||
* PCB version to function correctly before flashing the PCB version. This
|
||||
* way, flashing the PCB version can be done last. */
|
||||
return PCB_LATEST;
|
||||
#else
|
||||
PCBVersion version = readPCBVersionInMemory();
|
||||
return (version == k_alternateBlankVersion ? 0 : version);
|
||||
#endif
|
||||
}
|
||||
|
||||
PCBVersion readPCBVersionInMemory() {
|
||||
return ~(*reinterpret_cast<const PCBVersion *>(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex)));
|
||||
}
|
||||
|
||||
void writePCBVersion(PCBVersion version) {
|
||||
uint8_t * destination = reinterpret_cast<uint8_t *>(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex));
|
||||
PCBVersion formattedVersion = ~version;
|
||||
InternalFlash::WriteMemory(destination, reinterpret_cast<uint8_t *>(&formattedVersion), sizeof(formattedVersion));
|
||||
}
|
||||
|
||||
void lockPCBVersion() {
|
||||
uint8_t * destination = reinterpret_cast<uint8_t *>(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex));
|
||||
uint8_t zero = 0;
|
||||
InternalFlash::WriteMemory(destination, &zero, sizeof(zero));
|
||||
}
|
||||
|
||||
bool pcbVersionIsLocked() {
|
||||
return *reinterpret_cast<const uint8_t *>(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0;
|
||||
}
|
||||
|
||||
void jumpToInternalBootloader() {
|
||||
asm volatile ("cpsie i" : : : "memory");
|
||||
|
||||
STM32::rcc_deinit();
|
||||
STM32::hal_deinit();
|
||||
STM32::systick_deinit();
|
||||
|
||||
const uint32_t p = (*((uint32_t *) 0x1FF00000));
|
||||
asm volatile ("MSR msp, %0" : : "r" (p) : );
|
||||
void (*SysMemBootJump)(void);
|
||||
SysMemBootJump = (void (*)(void)) (*((uint32_t *) 0x1FF00004));
|
||||
SysMemBootJump();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
71
bootloader/drivers/stm32_drivers.cpp
Normal file
71
bootloader/drivers/stm32_drivers.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "stm32_drivers.h"
|
||||
|
||||
/**
|
||||
* THIS CODE COMES FROM THE STM32_HAL LIBRARY (LICENSE ABOVE) AND HAVE BEEN MODIFIED
|
||||
* WE USE ONLY THE HAL_deinit, RCC_deinit and systick_deninit FUNCTIONS AND ONLY COPIED THE CODE NEEDED.
|
||||
* WE NEEDED THIS CODE TO BE ABLE TO BOOT THE STM32 BOOTLOADER
|
||||
*/
|
||||
|
||||
/*
|
||||
This software component is provided to you as part of a software package and
|
||||
applicable license terms are in the Package_license file. If you received this
|
||||
software component outside of a package or without applicable license terms,
|
||||
the terms of the BSD-3-Clause license shall apply.
|
||||
You may obtain a copy of the BSD-3-Clause at:
|
||||
https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
void STM32::rcc_deinit() {
|
||||
SET_BIT(STM_32_RCC->CR, (0x1UL << (0U)));
|
||||
while (READ_BIT(STM_32_RCC->CR, (0x1UL << (1U))) == 0) {}
|
||||
SET_BIT(STM_32_RCC->CR, (0x10UL << (3U)));
|
||||
CLEAR_REG(STM_32_RCC->CFGR);
|
||||
while (READ_BIT(STM_32_RCC->CFGR, (0x3UL << (2U))) != 0) {}
|
||||
CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (16U)) | (0x1UL << (18U)) | (0x1UL << (19U)));
|
||||
while (READ_BIT(STM_32_RCC->CR, (0x1UL << (17U))) != 0) {}
|
||||
CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (24U)));
|
||||
while (READ_BIT(STM_32_RCC->CR, (0x1UL << (25U))) != 0) {}
|
||||
CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (26U)));
|
||||
while (READ_BIT(STM_32_RCC->CR, (0x1UL << (27U))) != 0) {}
|
||||
CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (28U)));
|
||||
while (READ_BIT(STM_32_RCC->CR, (0x1UL << (29U))) != 0) {}
|
||||
STM_32_RCC->PLLCFGR = ((0x10UL << (0x0U)) | (0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | 0x20000000U);
|
||||
STM_32_RCC->PLLI2SCFGR = ((0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | (0x2UL << (28U)));
|
||||
STM_32_RCC->PLLSAICFGR = ((0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | 0x20000000U);
|
||||
CLEAR_BIT(STM_32_RCC->CIR, ((0x1UL << (8U)) | (0x1UL << (9U)) | (0x1UL << (10U)) | (0x1UL << (11U)) | (0x1UL << (12U)) | (0x1UL << (13U)) | (0x1UL << (14U))));
|
||||
SET_BIT(STM_32_RCC->CIR, ((0x1UL << (16U)) | (0x1UL << (17U)) | (0x1UL << (18U)) | (0x1UL << (19U)) | (0x1UL << (20U)) | (0x1UL << (21U)) | (0x1UL << (22U)) | (0x1UL << (23U))));
|
||||
CLEAR_BIT(STM_32_RCC->CSR, ((0x1UL << (0U))));
|
||||
SET_BIT(STM_32_RCC->CSR, ((0x1UL << (24U))));
|
||||
uint32_t sysclock = ((uint32_t)16000000U);
|
||||
uint32_t a = ((sysclock / 1000U));
|
||||
uint32_t b = 15U;
|
||||
STM_32_SysTick->LOAD = (uint32_t)(a - 1UL);
|
||||
STM_32_SCB->SHPR[(((uint32_t)(-1))&0xFUL)-4UL] = (uint8_t)((((1UL << 4U)-1UL) << (8U - 4UL)) & (uint32_t)0xFFUL);
|
||||
STM_32_SysTick->VAL = 0U;
|
||||
STM_32_SysTick->CTRL = (1UL << 2U) | (1UL << 1U) | (1UL);
|
||||
uint32_t c = ((uint32_t)((STM_32_SCB->AIRCR & (7UL << 8U)) >> 8U));
|
||||
uint32_t d = (c & (uint32_t)0x07UL);
|
||||
uint32_t e;
|
||||
uint32_t f;
|
||||
e = ((7UL - d) > (uint32_t)(4UL)) ? (uint32_t)(4UL) : (7UL - d);
|
||||
f = ((d + (uint32_t)(4UL)) < (uint32_t)(7UL)) ? (uint32_t)(0UL) : (uint32_t)((d - 7UL) + (uint32_t)(4UL));
|
||||
uint32_t g = (((b & (uint32_t)((1UL << (e)) - 1UL)) << f) | ((0UL & (uint32_t)((1UL << (f)) - 1UL))));
|
||||
STM_32_SCB->SHPR[(((uint32_t)(-1))&0xFUL)-4UL] = (uint8_t)((g << (8U - 4UL)) & (uint32_t)0xFFUL);
|
||||
}
|
||||
|
||||
void STM32::hal_deinit() {
|
||||
STM_32_RCC->APB1RSTR = 0xFFFFFFFFU;
|
||||
STM_32_RCC->APB1RSTR = 0x00U;
|
||||
STM_32_RCC->APB2RSTR = 0xFFFFFFFFU;
|
||||
STM_32_RCC->APB2RSTR = 0x00U;
|
||||
STM_32_RCC->AHB1RSTR = 0xFFFFFFFFU;
|
||||
STM_32_RCC->AHB1RSTR = 0x00U;
|
||||
STM_32_RCC->AHB2RSTR = 0xFFFFFFFFU;
|
||||
STM_32_RCC->AHB2RSTR = 0x00U;
|
||||
STM_32_RCC->AHB3RSTR = 0xFFFFFFFFU;
|
||||
STM_32_RCC->AHB3RSTR = 0x00U;
|
||||
}
|
||||
|
||||
void STM32::systick_deinit() {
|
||||
STM_32_SysTick->CTRL = STM_32_SysTick->LOAD = STM_32_SysTick->VAL = 0;
|
||||
}
|
||||
159
bootloader/drivers/stm32_drivers.h
Normal file
159
bootloader/drivers/stm32_drivers.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
Here we implement a very little part of the code from the default stm32 libs because we only need the unload function.
|
||||
Now we include the license of the original code as required.
|
||||
*/
|
||||
|
||||
/**
|
||||
* THIS CODE COMES FROM THE STM32_HAL LIBRARY (LICENSE ABOVE) AND HAVE BEEN MODIFIED
|
||||
* WE USE ONLY THE HAL_deinit, RCC_deinit and systick_deninit FUNCTIONS AND ONLY COPIED THE CODE NEEDED.
|
||||
* WE NEEDED THIS CODE TO BE ABLE TO BOOT THE STM32 BOOTLOADER
|
||||
*/
|
||||
|
||||
/*
|
||||
This software component is provided to you as part of a software package and
|
||||
applicable license terms are in the Package_license file. If you received this
|
||||
software component outside of a package or without applicable license terms,
|
||||
the terms of the BSD-3-Clause license shall apply.
|
||||
You may obtain a copy of the BSD-3-Clause at:
|
||||
https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
namespace STM32 {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile uint32_t CR;
|
||||
volatile uint32_t PLLCFGR;
|
||||
volatile uint32_t CFGR;
|
||||
volatile uint32_t CIR;
|
||||
volatile uint32_t AHB1RSTR;
|
||||
volatile uint32_t AHB2RSTR;
|
||||
volatile uint32_t AHB3RSTR;
|
||||
uint32_t RESERVED0;
|
||||
volatile uint32_t APB1RSTR;
|
||||
volatile uint32_t APB2RSTR;
|
||||
uint32_t RESERVED1[2];
|
||||
volatile uint32_t AHB1ENR;
|
||||
volatile uint32_t AHB2ENR;
|
||||
volatile uint32_t AHB3ENR;
|
||||
uint32_t RESERVED2;
|
||||
volatile uint32_t APB1ENR;
|
||||
volatile uint32_t APB2ENR;
|
||||
uint32_t RESERVED3[2];
|
||||
volatile uint32_t AHB1LPENR;
|
||||
volatile uint32_t AHB2LPENR;
|
||||
volatile uint32_t AHB3LPENR;
|
||||
uint32_t RESERVED4;
|
||||
volatile uint32_t APB1LPENR;
|
||||
volatile uint32_t APB2LPENR;
|
||||
uint32_t RESERVED5[2];
|
||||
volatile uint32_t BDCR;
|
||||
volatile uint32_t CSR;
|
||||
uint32_t RESERVED6[2];
|
||||
volatile uint32_t SSCGR;
|
||||
volatile uint32_t PLLI2SCFGR;
|
||||
volatile uint32_t PLLSAICFGR;
|
||||
volatile uint32_t DCKCFGR1;
|
||||
volatile uint32_t DCKCFGR2;
|
||||
} STM32_RCC_TypeDef;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile uint32_t CTRL;
|
||||
volatile uint32_t LOAD;
|
||||
volatile uint32_t VAL;
|
||||
volatile const uint32_t CALIB;
|
||||
} STM32_SysTick_Type;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
volatile uint32_t ISER[8U];
|
||||
uint32_t RESERVED0[24U];
|
||||
volatile uint32_t ICER[8U];
|
||||
uint32_t RSERVED1[24U];
|
||||
volatile uint32_t ISPR[8U];
|
||||
uint32_t RESERVED2[24U];
|
||||
volatile uint32_t ICPR[8U];
|
||||
uint32_t RESERVED3[24U];
|
||||
volatile uint32_t IABR[8U];
|
||||
uint32_t RESERVED4[56U];
|
||||
volatile uint8_t IP[240U];
|
||||
uint32_t RESERVED5[644U];
|
||||
volatile uint32_t STIR;
|
||||
} STM32_NVIC_Type;
|
||||
|
||||
typedef struct {
|
||||
volatile const uint32_t CPUID;
|
||||
volatile uint32_t ICSR;
|
||||
volatile uint32_t VTOR;
|
||||
volatile uint32_t AIRCR;
|
||||
volatile uint32_t SCR;
|
||||
volatile uint32_t CCR;
|
||||
volatile uint8_t SHPR[12U];
|
||||
volatile uint32_t SHCSR;
|
||||
volatile uint32_t CFSR;
|
||||
volatile uint32_t HFSR;
|
||||
volatile uint32_t DFSR;
|
||||
volatile uint32_t MMFAR;
|
||||
volatile uint32_t BFAR;
|
||||
volatile uint32_t AFSR;
|
||||
volatile const uint32_t ID_PFR[2U];
|
||||
volatile const uint32_t ID_DFR;
|
||||
volatile const uint32_t ID_AFR;
|
||||
volatile const uint32_t ID_MFR[4U];
|
||||
volatile const uint32_t ID_ISAR[5U];
|
||||
uint32_t RESERVED0[1U];
|
||||
volatile const uint32_t CLIDR;
|
||||
volatile const uint32_t CTR;
|
||||
volatile const uint32_t CCSIDR;
|
||||
volatile uint32_t CSSELR;
|
||||
volatile uint32_t CPACR;
|
||||
uint32_t RESERVED3[93U];
|
||||
volatile uint32_t STIR;
|
||||
uint32_t RESERVED4[15U];
|
||||
volatile const uint32_t MVFR0;
|
||||
volatile const uint32_t MVFR1;
|
||||
volatile const uint32_t MVFR2;
|
||||
uint32_t RESERVED5[1U];
|
||||
volatile uint32_t ICIALLU;
|
||||
uint32_t RESERVED6[1U];
|
||||
volatile uint32_t ICIMVAU;
|
||||
volatile uint32_t DCIMVAC;
|
||||
volatile uint32_t DCISW;
|
||||
volatile uint32_t DCCMVAU;
|
||||
volatile uint32_t DCCMVAC;
|
||||
volatile uint32_t DCCSW;
|
||||
volatile uint32_t DCCIMVAC;
|
||||
volatile uint32_t DCCISW;
|
||||
uint32_t RESERVED7[6U];
|
||||
volatile uint32_t ITCMCR;
|
||||
volatile uint32_t DTCMCR;
|
||||
volatile uint32_t AHBPCR;
|
||||
volatile uint32_t CACR;
|
||||
volatile uint32_t AHBSCR;
|
||||
uint32_t RESERVED8[1U];
|
||||
volatile uint32_t ABFSR;
|
||||
} STM32_SCB_Type;
|
||||
|
||||
#define RCC_BASE 0x40023800UL
|
||||
#define SysTick_BASE 0xE000E010UL
|
||||
#define NVIC_BASE 0xE000E100UL
|
||||
#define SCB_BASE 0xE000ED00UL
|
||||
|
||||
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
|
||||
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
|
||||
#define READ_BIT(REG, BIT) ((REG) & (BIT))
|
||||
#define CLEAR_REG(REG) ((REG) = (0x0))
|
||||
#define READ_REG(REG) ((REG))
|
||||
|
||||
#define STM_32_RCC ((STM32::STM32_RCC_TypeDef *) RCC_BASE)
|
||||
#define STM_32_SysTick ((STM32::STM32_SysTick_Type *) SysTick_BASE)
|
||||
#define STM_32_NVIC ((STM32::STM32_NVIC_Type *) NVIC_BASE)
|
||||
#define STM_32_SCB ((STM32_SCB_Type *) SCB_BASE)
|
||||
|
||||
extern void rcc_deinit();
|
||||
extern void hal_deinit();
|
||||
extern void systick_deinit();
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <ion.h>
|
||||
|
||||
#include <bootloader/interface.h>
|
||||
#include <bootloader/slot.h>
|
||||
#include <bootloader/boot.h>
|
||||
|
||||
#include "computer.h"
|
||||
#include "cable.h"
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
void Interface::drawImage(KDContext* ctx, const Image* image, int offset) {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
size_t pixelBufferSize;
|
||||
if (image != nullptr) {
|
||||
data = image->compressedPixelData();
|
||||
size = image->compressedPixelDataSize();
|
||||
pixelBufferSize = image->width() * image->height();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
KDColor pixelBuffer[4000];
|
||||
assert(pixelBufferSize <= 4000);
|
||||
assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack
|
||||
|
||||
Ion::decompress(
|
||||
data,
|
||||
reinterpret_cast<uint8_t *>(pixelBuffer),
|
||||
size,
|
||||
pixelBufferSize * sizeof(KDColor)
|
||||
);
|
||||
|
||||
KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height());
|
||||
|
||||
ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr);
|
||||
}
|
||||
|
||||
void Interface::draw() {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->fillRect(KDRect(0,0,320,240), KDColorBlack);
|
||||
drawImage(ctx, ImageStore::Computer, 70);
|
||||
drawImage(ctx, ImageStore::Cable, 172);
|
||||
|
||||
ctx->drawString("Slot A:", KDPoint(0, 0), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
ctx->drawString("Slot B:", KDPoint(0, 13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
ctx->drawString("Current:", KDPoint(0, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
|
||||
if (Boot::mode() == BootMode::SlotA) {
|
||||
ctx->drawString("Slot A", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
} else if (Boot::mode() == BootMode::SlotB) {
|
||||
ctx->drawString("Slot B", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
}
|
||||
|
||||
Slot slots[2] = {Slot::A(), Slot::B()};
|
||||
|
||||
for(uint8_t i = 0; i < 2; i++) {
|
||||
Slot slot = slots[i];
|
||||
|
||||
if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) {
|
||||
if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) {
|
||||
ctx->drawString("Upsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
ctx->drawString(slot.userlandHeader()->upsilonVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
} else if (slot.userlandHeader()->isOmega()) {
|
||||
ctx->drawString("Omega", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
ctx->drawString(slot.userlandHeader()->omegaVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
} else {
|
||||
ctx->drawString("Epsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
ctx->drawString(slot.userlandHeader()->version(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
}
|
||||
ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(168, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
} else {
|
||||
ctx->drawString("Invalid", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef BOOTLOADER_INTERFACE
|
||||
#define BOOTLOADER_INTERFACE
|
||||
|
||||
#include <stdint.h>
|
||||
#include <kandinsky/context.h>
|
||||
#include <escher/image.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class Interface {
|
||||
|
||||
private:
|
||||
static void drawImage(KDContext* ctx, const Image* image, int offset);
|
||||
|
||||
public:
|
||||
static void draw();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
20
bootloader/interface/menus/about.cpp
Normal file
20
bootloader/interface/menus/about.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "about.h"
|
||||
#include <bootloader/interface/static/messages.h>
|
||||
|
||||
Bootloader::AboutMenu::AboutMenu() : Menu(KDColorBlack, KDColorWhite, Messages::aboutMenuTitle, Messages::bootloaderVersion) {
|
||||
setup();
|
||||
}
|
||||
|
||||
void Bootloader::AboutMenu::setup() {
|
||||
m_default_columns[0] = Column(Messages::aboutMessage1, k_small_font, 0, true);
|
||||
m_default_columns[1] = Column(Messages::aboutMessage2, k_small_font, 0, true);
|
||||
m_default_columns[2] = Column(Messages::aboutMessage3, k_small_font, 0, true);
|
||||
m_default_columns[3] = Column(Messages::aboutMessage4, k_small_font, 0, true);
|
||||
m_default_columns[4] = Column(Messages::aboutMessage5, k_small_font, 0, true);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_default_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_default_columns[2]);
|
||||
m_columns[3] = ColumnBinder(&m_default_columns[3]);
|
||||
m_columns[4] = ColumnBinder(&m_default_columns[4]);
|
||||
}
|
||||
17
bootloader/interface/menus/about.h
Normal file
17
bootloader/interface/menus/about.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_ABOUT_H_
|
||||
#define _BOOTLOADER_INTERFACE_ABOUT_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class AboutMenu : public Menu {
|
||||
public:
|
||||
AboutMenu();
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override {};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
22
bootloader/interface/menus/crash.cpp
Normal file
22
bootloader/interface/menus/crash.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "crash.h"
|
||||
|
||||
Bootloader::CrashMenu::CrashMenu(const char * err) : Menu(KDColorBlack, KDColorWhite, Bootloader::Messages::bootloaderCrashTitle, Bootloader::Messages::mainTitle), m_error(err) {
|
||||
setup();
|
||||
}
|
||||
|
||||
void Bootloader::CrashMenu::setup() {
|
||||
m_default_columns[0] = Column(m_error, k_large_font, 0, true);
|
||||
m_default_columns[1] = Column(Bootloader::Messages::bootloaderCrashMessage1, k_small_font, 0, true);
|
||||
m_default_columns[2] = Column(Bootloader::Messages::bootloaderCrashMessage2, k_small_font, 0, true);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_default_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_default_columns[2]);
|
||||
}
|
||||
|
||||
void Bootloader::CrashMenu::postOpen() {
|
||||
// We override the open method
|
||||
for (;;) {
|
||||
// Infinite loop
|
||||
}
|
||||
}
|
||||
19
bootloader/interface/menus/crash.h
Normal file
19
bootloader/interface/menus/crash.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENUS_CRASH_H_
|
||||
#define _BOOTLOADER_INTERFACE_MENUS_CRASH_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class CrashMenu : public Menu {
|
||||
public:
|
||||
CrashMenu(const char * error);
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override;
|
||||
|
||||
private:
|
||||
const char * m_error;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
37
bootloader/interface/menus/dfu.cpp
Normal file
37
bootloader/interface/menus/dfu.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "dfu.h"
|
||||
#include <bootloader/boot.h>
|
||||
#include <ion.h>
|
||||
|
||||
Bootloader::DfuMenu::DfuMenu(const char * text, const USBData * data) : Menu(KDColorBlack, KDColorWhite, Messages::dfuTitle, Messages::mainTitle), m_submenuText(text), m_data(data) {
|
||||
setup();
|
||||
}
|
||||
|
||||
void Bootloader::DfuMenu::setup() {
|
||||
m_default_columns[0] = Column(m_submenuText, k_small_font, 0, true);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
}
|
||||
|
||||
void Bootloader::DfuMenu::postOpen() {
|
||||
// We override the open method
|
||||
if (!m_data->getData().isProtectedInternal() && m_data->getData().isProtectedExternal()) {
|
||||
// Because we want to flash the internal, we will jump into the stm32 bootloader
|
||||
Bootloader::Boot::jumpToInternalBootloader();
|
||||
return; // We never reach this point
|
||||
}
|
||||
for (;;) {
|
||||
Ion::USB::enable();
|
||||
do {
|
||||
uint64_t scan = Ion::Keyboard::scan();
|
||||
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
|
||||
Ion::USB::disable();
|
||||
forceExit();
|
||||
return;
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
|
||||
Ion::Power::standby();
|
||||
return;
|
||||
}
|
||||
} while (!Ion::USB::isEnumerated());
|
||||
Ion::USB::DFU(true, (void *)m_data);
|
||||
}
|
||||
}
|
||||
21
bootloader/interface/menus/dfu.h
Normal file
21
bootloader/interface/menus/dfu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENUS_DFU_H_
|
||||
#define _BOOTLOADER_INTERFACE_MENUS_DFU_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class DfuMenu : public Menu {
|
||||
public:
|
||||
DfuMenu(const char * submenu, const USBData * usbData);
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override;
|
||||
|
||||
private:
|
||||
const char * m_submenuText;
|
||||
const USBData * m_data;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
109
bootloader/interface/menus/home.cpp
Normal file
109
bootloader/interface/menus/home.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "home.h"
|
||||
#include <bootloader/boot.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <bootloader/interface/menus/about.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Bootloader::AboutMenu * Bootloader::HomeMenu::aboutMenu() {
|
||||
static AboutMenu * aboutMenu = new AboutMenu();
|
||||
return aboutMenu;
|
||||
}
|
||||
|
||||
Bootloader::InstallerMenu * Bootloader::HomeMenu::installerMenu() {
|
||||
static InstallerMenu * installerMenu = new InstallerMenu();
|
||||
return installerMenu;
|
||||
}
|
||||
|
||||
Bootloader::HomeMenu::HomeMenu() : Menu(KDColorBlack, KDColorWhite, Messages::homeTitle, Messages::mainTitle) {
|
||||
setup();
|
||||
}
|
||||
|
||||
bool slotA_submenu() {
|
||||
if (Bootloader::Slot::isFullyValid(Bootloader::Slot::A())) {
|
||||
Bootloader::Boot::bootSlot(Bootloader::Slot::A());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slotKhi_submenu() {
|
||||
if (Bootloader::Slot::isFullyValid(Bootloader::Slot::Khi())) {
|
||||
Bootloader::Boot::bootSlot(Bootloader::Slot::Khi());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slotB_submenu() {
|
||||
if (Bootloader::Slot::isFullyValid(Bootloader::Slot::B())) {
|
||||
Bootloader::Boot::bootSlot(Bootloader::Slot::B());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool installer_submenu() {
|
||||
Bootloader::HomeMenu::installerMenu()->open();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool about_submenu() {
|
||||
Bootloader::HomeMenu::aboutMenu()->open();
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * Bootloader::HomeMenu::getSlotOsText(Slot slot) {
|
||||
if (Slot::isFullyValid(slot)) {
|
||||
if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) {
|
||||
return Messages::upsilonSlot;
|
||||
} else if (slot.userlandHeader()->isOmega() && slot.kernelHeader()->patchLevel()[0] != '\0') {
|
||||
return Messages::omegaSlot;
|
||||
} else if (slot.userlandHeader()->isOmega()) {
|
||||
return Messages::khiSlot;
|
||||
} else {
|
||||
return Messages::epsilonSlot;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * Bootloader::HomeMenu::getSlotText(Slot slot) {
|
||||
if(Slot::isFullyValid(slot)) {
|
||||
if (slot.address() == Slot::A().address()) {
|
||||
return Messages::homeSlotASubmenu;
|
||||
} else if (slot.address() == Slot::Khi().address()) {
|
||||
return Messages::homeSlotKhiSubmenu;
|
||||
} else if (slot.address() == Slot::B().address()) {
|
||||
return Messages::homeSlotBSubmenu;
|
||||
}
|
||||
}
|
||||
return Messages::invalidSlot;
|
||||
}
|
||||
|
||||
const char * Bootloader::HomeMenu::getKernelText(Slot slot) {
|
||||
return Slot::isFullyValid(slot) ? slot.kernelHeader()->patchLevel() : nullptr;
|
||||
}
|
||||
|
||||
const char * Bootloader::HomeMenu::getVersionText(Slot slot) {
|
||||
return Slot::isFullyValid(slot) ? slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon() ? slot.userlandHeader()->upsilonVersion() : slot.userlandHeader()->isOmega() ? slot.userlandHeader()->omegaVersion() : slot.kernelHeader()->version() : nullptr;
|
||||
}
|
||||
|
||||
void Bootloader::HomeMenu::setup() {
|
||||
Slot A = Slot::A();
|
||||
Slot Khi = Slot::Khi();
|
||||
Slot B = Slot::B();
|
||||
|
||||
m_slot_columns[0] = SlotColumn(getSlotText(A), getKernelText(A), getSlotOsText(A), getVersionText(A), Ion::Keyboard::Key::One, k_small_font, 10, false, &slotA_submenu);
|
||||
m_slot_columns[1] = SlotColumn(getSlotText(Khi), getKernelText(Khi), getSlotOsText(Khi), getVersionText(Khi), Ion::Keyboard::Key::Two, k_small_font, 10, false, &slotKhi_submenu);
|
||||
m_slot_columns[2] = SlotColumn(getSlotText(B), getKernelText(B), getSlotOsText(B), getVersionText(B), Ion::Keyboard::Key::Three, k_small_font, 10, false, &slotB_submenu);
|
||||
m_default_columns[0] = Column(Messages::homeInstallerSubmenu, Ion::Keyboard::Key::Four, k_small_font, 10, false, &installer_submenu);
|
||||
m_default_columns[1] = Column(Messages::homeAboutSubmenu, Ion::Keyboard::Key::Five, k_small_font, 10, false, &about_submenu);
|
||||
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_slot_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_slot_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_slot_columns[2]);
|
||||
m_columns[3] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[4] = ColumnBinder(&m_default_columns[1]);
|
||||
|
||||
}
|
||||
29
bootloader/interface/menus/home.h
Normal file
29
bootloader/interface/menus/home.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENUS_HOME_H_
|
||||
#define _BOOTLOADER_INTERFACE_MENUS_HOME_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/interface/menus/about.h>
|
||||
#include <bootloader/interface/menus/installer.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class HomeMenu : public Menu {
|
||||
public:
|
||||
HomeMenu();
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override {};
|
||||
|
||||
static AboutMenu * aboutMenu();
|
||||
static InstallerMenu * installerMenu();
|
||||
|
||||
private:
|
||||
const char * getSlotOsText(Slot slot);
|
||||
const char * getSlotText(Slot slot);
|
||||
const char * getKernelText(Slot slot);
|
||||
const char * getVersionText(Slot slot);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
41
bootloader/interface/menus/installer.cpp
Normal file
41
bootloader/interface/menus/installer.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "installer.h"
|
||||
#include <bootloader/interface/static/messages.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
#include <bootloader/interface/menus/dfu.h>
|
||||
|
||||
Bootloader::DfuMenu * Bootloader::InstallerMenu::SlotsDFU() {
|
||||
USBData data = USBData::DEFAULT();
|
||||
static DfuMenu * slotsDfu = new DfuMenu(Messages::dfuSlotsUpdate, &data);
|
||||
return slotsDfu;
|
||||
}
|
||||
|
||||
Bootloader::DfuMenu * Bootloader::InstallerMenu::BootloaderDFU() {
|
||||
USBData data = USBData::BOOTLOADER_UPDATE();
|
||||
static DfuMenu * bootloaderDfu = new DfuMenu(Messages::dfuBootloaderUpdate, &data);
|
||||
return bootloaderDfu;
|
||||
}
|
||||
|
||||
Bootloader::InstallerMenu::InstallerMenu() : Menu(KDColorBlack, KDColorWhite, Messages::installerTitle, Messages::mainTitle) {
|
||||
setup();
|
||||
}
|
||||
|
||||
bool slotsSubmenu() {
|
||||
Bootloader::InstallerMenu::SlotsDFU()->open();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bootloaderSubmenu() {
|
||||
Bootloader::InstallerMenu::BootloaderDFU()->open();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bootloader::InstallerMenu::setup() {
|
||||
m_default_columns[0] = Column(Messages::installerText1, k_large_font, 0, true);
|
||||
m_default_columns[1] = Column(Messages::installerSlotsSubmenu, Ion::Keyboard::Key::One, k_small_font, 30, false, &slotsSubmenu);
|
||||
m_default_columns[2] = Column(Messages::installerBootloaderSubmenu, Ion::Keyboard::Key::Two, k_small_font, 30, false, &bootloaderSubmenu);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_default_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_default_columns[2]);
|
||||
}
|
||||
20
bootloader/interface/menus/installer.h
Normal file
20
bootloader/interface/menus/installer.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENUS_INSTALLER_H_
|
||||
#define _BOOTLOADER_INTERFACE_MENUS_INSTALLER_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/interface/menus/dfu.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class InstallerMenu : public Menu {
|
||||
public:
|
||||
InstallerMenu();
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override {};
|
||||
|
||||
static DfuMenu * SlotsDFU();
|
||||
static DfuMenu * BootloaderDFU();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
39
bootloader/interface/menus/slot_recovery.cpp
Normal file
39
bootloader/interface/menus/slot_recovery.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "slot_recovery.h"
|
||||
#include <ion.h>
|
||||
|
||||
Bootloader::SlotRecoveryMenu::SlotRecoveryMenu(USBData * usb) : Menu(KDColorBlack, KDColorWhite, Messages::recoveryTitle, Messages::mainTitle), m_data(usb) {
|
||||
setup();
|
||||
}
|
||||
|
||||
void Bootloader::SlotRecoveryMenu::setup() {
|
||||
m_default_columns[0] = Column(Messages::recoveryMessage1, k_small_font, 0, true);
|
||||
m_default_columns[1] = Column(Messages::recoveryMessage2, k_small_font, 0, true);
|
||||
m_default_columns[2] = Column(Messages::recoveryMessage3, k_small_font, 0, true);
|
||||
m_default_columns[3] = Column(Messages::recoveryMessage4, k_small_font, 0, true);
|
||||
m_default_columns[4] = Column(Messages::recoveryMessage5, k_small_font, 0, true);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_default_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_default_columns[2]);
|
||||
m_columns[3] = ColumnBinder(&m_default_columns[3]);
|
||||
m_columns[4] = ColumnBinder(&m_default_columns[4]);
|
||||
}
|
||||
|
||||
void Bootloader::SlotRecoveryMenu::postOpen() {
|
||||
// We override the open method
|
||||
for (;;) {
|
||||
Ion::USB::enable();
|
||||
do {
|
||||
uint64_t scan = Ion::Keyboard::scan();
|
||||
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
|
||||
Ion::USB::disable();
|
||||
forceExit();
|
||||
return;
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
|
||||
Ion::Power::standby();
|
||||
return;
|
||||
}
|
||||
} while (!Ion::USB::isEnumerated());
|
||||
Ion::USB::DFU(true, (void *)m_data);
|
||||
}
|
||||
}
|
||||
19
bootloader/interface/menus/slot_recovery.h
Normal file
19
bootloader/interface/menus/slot_recovery.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENU_SLOT_RECOVERY_H
|
||||
#define _BOOTLOADER_INTERFACE_MENU_SLOT_RECOVERY_H
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class SlotRecoveryMenu : public Menu {
|
||||
public:
|
||||
SlotRecoveryMenu(USBData * usbData);
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override;
|
||||
private:
|
||||
const USBData * m_data;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
35
bootloader/interface/menus/warning.cpp
Normal file
35
bootloader/interface/menus/warning.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "warning.h"
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <bootloader/boot.h>
|
||||
|
||||
Bootloader::WarningMenu::WarningMenu() : Menu(KDColorWhite, KDColorRed, Messages::epsilonWarningTitle, Messages::mainTitle, false, 3) {
|
||||
setup();
|
||||
}
|
||||
|
||||
bool proceed() {
|
||||
Bootloader::Boot::bootSelectedSlot();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backoff() {
|
||||
if (Bootloader::Boot::config()->slot() != nullptr) {
|
||||
Bootloader::Boot::config()->clearSlot();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bootloader::WarningMenu::setup() {
|
||||
m_default_columns[0] = Column(Messages::epsilonWarningMessage1, k_small_font, 0, true);
|
||||
m_default_columns[1] = Column(Messages::epsilonWarningMessage2, k_small_font, 0, true);
|
||||
m_default_columns[2] = Column(Messages::epsilonWarningMessage3, k_small_font, 0, true);
|
||||
m_default_columns[3] = Column(Messages::epsilonWarningMessage4, k_small_font, 0, true);
|
||||
m_default_columns[4] = Column(Messages::epsilonWarningMessage5, Ion::Keyboard::Key::EXE, k_small_font, 0, true, &proceed);
|
||||
m_default_columns[5] = Column(Messages::epsilonWarningMessage6, Ion::Keyboard::Key::Back, k_small_font, 0, true, &backoff);
|
||||
|
||||
m_columns[0] = ColumnBinder(&m_default_columns[0]);
|
||||
m_columns[1] = ColumnBinder(&m_default_columns[1]);
|
||||
m_columns[2] = ColumnBinder(&m_default_columns[2]);
|
||||
m_columns[3] = ColumnBinder(&m_default_columns[3]);
|
||||
m_columns[4] = ColumnBinder(&m_default_columns[4]);
|
||||
m_columns[5] = ColumnBinder(&m_default_columns[5]);
|
||||
}
|
||||
17
bootloader/interface/menus/warning.h
Normal file
17
bootloader/interface/menus/warning.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BOOTLOADER_INTERFACE_MENUS_WARNING_H_
|
||||
#define _BOOTLOADER_INTERFACE_MENUS_WARNING_H_
|
||||
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class WarningMenu : public Menu {
|
||||
public:
|
||||
WarningMenu();
|
||||
|
||||
void setup() override;
|
||||
void postOpen() override {};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
136
bootloader/interface/src/menu.cpp
Normal file
136
bootloader/interface/src/menu.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <bootloader/interface/src/menu.h>
|
||||
#include <bootloader/interface/static/interface.h>
|
||||
#include <ion.h>
|
||||
#include <kandinsky/context.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bootloader/computer.h>
|
||||
|
||||
const Ion::Keyboard::Key Bootloader::Menu::k_breaking_keys[];
|
||||
|
||||
void Bootloader::Menu::setup() {
|
||||
// Here we add the columns to the menu.
|
||||
}
|
||||
|
||||
void Bootloader::Menu::open(bool noreturn) {
|
||||
showMenu();
|
||||
|
||||
uint64_t scan = 0;
|
||||
bool exit = false;
|
||||
|
||||
postOpen();
|
||||
|
||||
while(!exit && !m_forced_exit) {
|
||||
scan = Ion::Keyboard::scan();
|
||||
exit = !handleKey(scan);
|
||||
if (noreturn) {
|
||||
exit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Bootloader::Menu::calculateCenterX(const char * text, int fontWidth) {
|
||||
return (getScreen().width() - fontWidth * strlen(text)) / 2;
|
||||
}
|
||||
|
||||
void Bootloader::Menu::showMenu() {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->fillRect(getScreen(), m_background);
|
||||
Interface::drawImage(ctx, ImageStore::Computer, 25);
|
||||
int y = ImageStore::Computer->height() + 25 + 10;
|
||||
int x = calculateCenterX(m_title, largeFontWidth());
|
||||
ctx->drawString(m_title, KDPoint(x, y), k_large_font, m_foreground, m_background);
|
||||
y += largeFontHeight() + 10;
|
||||
|
||||
//TODO: center the columns if m_centerY is true
|
||||
|
||||
for (ColumnBinder column : m_columns) {
|
||||
if (column.isNull()) {
|
||||
continue;
|
||||
}
|
||||
if (column.type() == ColumnType::SLOT) {
|
||||
y += ((SlotColumn *)column.getColumn())->draw(ctx, y, m_background, m_foreground) + m_margin;
|
||||
} else if (column.type() == ColumnType::DEFAULT) {
|
||||
y += ((Column *)column.getColumn())->draw(ctx, y, m_background, m_foreground) + m_margin;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bottom != nullptr) {
|
||||
y = getScreen().height() - smallFontHeight() - 10;
|
||||
x = calculateCenterX(m_bottom, smallFontWidth());
|
||||
ctx->drawString(m_bottom, KDPoint(x, y), k_small_font, m_foreground, m_background);
|
||||
}
|
||||
}
|
||||
|
||||
bool Bootloader::Menu::handleKey(uint64_t key) {
|
||||
for (Ion::Keyboard::Key breaking : this->k_breaking_keys) {
|
||||
if (Ion::Keyboard::State(breaking) == key) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (key == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
|
||||
Ion::Power::standby();
|
||||
return false;
|
||||
}
|
||||
for (ColumnBinder column : m_columns) {
|
||||
if (column.isNull()) {
|
||||
continue;
|
||||
} else {
|
||||
if (column.type() == ColumnType::SLOT) {
|
||||
if (((SlotColumn *)column.getColumn())->didHandledEvent(key)) {
|
||||
redraw();
|
||||
}
|
||||
} else if (column.type() == ColumnType::DEFAULT) {
|
||||
if (((Column *)column.getColumn())->didHandledEvent(key)) {
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bootloader::Menu::Column::didHandledEvent(uint64_t key) {
|
||||
if (isMyKey(key) && isClickable()) {
|
||||
return m_callback();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Bootloader::Menu::Column::draw(KDContext * ctx, int y, KDColor background, KDColor foreground) {
|
||||
int x = m_extraX;
|
||||
if (m_center) {
|
||||
x += Bootloader::Menu::calculateCenterX(m_text, m_font->glyphSize().width());
|
||||
}
|
||||
ctx->drawString(m_text, KDPoint(x, y), m_font, foreground, background);
|
||||
return m_font->glyphSize().height();
|
||||
}
|
||||
|
||||
int Bootloader::Menu::SlotColumn::draw(KDContext * ctx, int y, KDColor background, KDColor foreground) {
|
||||
int x = m_extraX;
|
||||
|
||||
int width = strlen(m_text);
|
||||
if (m_kernalPatch != nullptr) {
|
||||
width += strlen(m_kernalPatch) + m_font->glyphSize().width();
|
||||
}
|
||||
if (m_osType != nullptr) {
|
||||
width += strlen(m_osType) + m_font->glyphSize().width();
|
||||
}
|
||||
if (m_center) {
|
||||
x += Bootloader::Menu::getScreen().width() - width * m_font->glyphSize().width();
|
||||
}
|
||||
ctx->drawString(m_text, KDPoint(x, y), m_font, foreground, background);
|
||||
x += strlen(m_text) * m_font->glyphSize().width() + m_font->glyphSize().width();
|
||||
if (m_kernalPatch != nullptr) {
|
||||
ctx->drawString(m_kernalPatch, KDPoint(x, y), m_font, foreground, background);
|
||||
}
|
||||
x += strlen(m_kernalPatch) * m_font->glyphSize().width() + m_font->glyphSize().width();
|
||||
if (m_osType != nullptr) {
|
||||
ctx->drawString(m_osType, KDPoint(x, y), m_font, foreground, background);
|
||||
}
|
||||
x += strlen(m_osType) * m_font->glyphSize().width() + m_font->glyphSize().width();
|
||||
if (m_kernelVersion != nullptr) {
|
||||
ctx->drawString(m_kernelVersion, KDPoint(x, y), m_font, foreground, background);
|
||||
}
|
||||
return m_font->glyphSize().height();
|
||||
}
|
||||
126
bootloader/interface/src/menu.h
Normal file
126
bootloader/interface/src/menu.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef _BOOTLOADER_MENU_H_
|
||||
#define _BOOTLOADER_MENU_H_
|
||||
|
||||
#include <ion/keyboard.h>
|
||||
#include <bootloader/interface/static/messages.h>
|
||||
#include <kandinsky/context.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class Menu {
|
||||
public:
|
||||
Menu() : Menu(KDColorBlack, KDColorWhite, Messages::mainTitle) { };
|
||||
Menu(KDColor forground, KDColor background, const char * title) : Menu(forground, background, title, nullptr) {};
|
||||
Menu(KDColor forground, KDColor background, const char * title, const char * bottom) : Menu(forground, background, title, bottom, false) {};
|
||||
Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY) : Menu(forground, background, title, bottom, centerY, k_columns_margin) {};
|
||||
Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY, int margin) : m_columns(), m_default_columns(), m_slot_columns(), m_background(background), m_title(title), m_foreground(forground), m_bottom(bottom), m_centerY(centerY), m_forced_exit(false), m_margin(margin) {
|
||||
setup();
|
||||
}
|
||||
static const int k_columns_margin = 5;
|
||||
|
||||
virtual void setup() = 0;
|
||||
virtual void postOpen() = 0;
|
||||
|
||||
enum ColumnType {
|
||||
DEFAULT,
|
||||
SLOT
|
||||
};
|
||||
|
||||
class Column {
|
||||
public:
|
||||
Column() : m_text(nullptr), m_key(Ion::Keyboard::Key::None), m_font(KDFont::SmallFont), m_extraX(0), m_center(false), m_callback(nullptr), m_clickable(false) {};
|
||||
|
||||
Column(const char * t, Ion::Keyboard::Key k, const KDFont * font, int extraX, bool center, bool(*pointer)()) : m_text(t), m_key(k), m_font(font), m_extraX(extraX), m_center(center), m_callback(pointer), m_clickable(true) {};
|
||||
Column(const char * t, const KDFont * font, int extraX, bool center) : m_text(t), m_key(Ion::Keyboard::Key::None), m_font(font), m_extraX(extraX), m_center(center), m_callback(nullptr), m_clickable(false) {};
|
||||
|
||||
bool isNull() const { return m_text == nullptr; };
|
||||
bool isClickable() const { return m_clickable; };
|
||||
bool didHandledEvent(uint64_t key);
|
||||
virtual int draw(KDContext * ctx, int y, KDColor background, KDColor foreground);
|
||||
virtual int columnType() { return ColumnType::DEFAULT; };
|
||||
|
||||
private:
|
||||
bool isMyKey(uint64_t key) const { return Ion::Keyboard::State(m_key) == key; };
|
||||
protected:
|
||||
const char * m_text;
|
||||
Ion::Keyboard::Key m_key;
|
||||
const KDFont * m_font;
|
||||
int m_extraX;
|
||||
bool m_center;
|
||||
bool (*m_callback)();
|
||||
bool m_clickable;
|
||||
};
|
||||
|
||||
class SlotColumn : public Column {
|
||||
public:
|
||||
SlotColumn() : Column(), m_kernalPatch(nullptr), m_osType(nullptr), m_kernelVersion(nullptr) {};
|
||||
|
||||
SlotColumn(const char * t, Ion::Keyboard::Key k, const KDFont * font, int extraX, bool center, bool(*pointer)()) : Column(t, k, font, extraX, center, pointer), m_kernalPatch(nullptr), m_osType(nullptr), m_kernelVersion(nullptr) {};
|
||||
SlotColumn(const char * t, const char * k, const char * o, const char * kernelV, Ion::Keyboard::Key key, const KDFont * font, int extraX, bool center, bool(*pointer)()) : Column(t, key, font, extraX, center, pointer), m_kernalPatch(k), m_osType(o), m_kernelVersion(kernelV) {};
|
||||
|
||||
int draw(KDContext * ctx, int y, KDColor background, KDColor foreground) override;
|
||||
virtual int columnType() { return ColumnType::SLOT; };
|
||||
|
||||
private:
|
||||
const char * m_kernalPatch;
|
||||
const char * m_osType;
|
||||
const char * m_kernelVersion;
|
||||
};
|
||||
|
||||
class ColumnBinder {
|
||||
public:
|
||||
ColumnBinder() : m_pointer(nullptr), m_type(ColumnType::DEFAULT) {};
|
||||
ColumnBinder(Column * pointer) : m_pointer(pointer), m_type(ColumnType::DEFAULT) {};
|
||||
ColumnBinder(SlotColumn * pointer) : m_pointer(pointer), m_type(ColumnType::SLOT) {};
|
||||
|
||||
bool isNull() const { return m_pointer == nullptr; };
|
||||
void * getColumn() const { return m_pointer; };
|
||||
ColumnType type() const { return m_type; };
|
||||
private:
|
||||
void * m_pointer;
|
||||
ColumnType m_type;
|
||||
};
|
||||
|
||||
void open(bool noreturn = false);
|
||||
void redraw() { showMenu(); };
|
||||
|
||||
static int calculateCenterX(const char * text, int fontWidth);
|
||||
|
||||
static constexpr const KDFont * k_small_font = KDFont::SmallFont;
|
||||
static constexpr const KDFont * k_large_font = KDFont::LargeFont;
|
||||
|
||||
static const KDRect getScreen() { return KDRect(0, 0, 320, 240); };
|
||||
|
||||
protected:
|
||||
void forceExit() { m_forced_exit = true; };
|
||||
|
||||
private:
|
||||
static const int k_max_columns = 6;
|
||||
|
||||
static constexpr Ion::Keyboard::Key k_breaking_keys[] = {Ion::Keyboard::Key::Back, Ion::Keyboard::Key::Home};
|
||||
|
||||
int smallFontHeight() const { return k_small_font->glyphSize().height(); };
|
||||
int largeFontHeight() const { return k_large_font->glyphSize().height(); };
|
||||
|
||||
int smallFontWidth() const { return k_small_font->glyphSize().width(); };
|
||||
int largeFontWidth() const { return k_large_font->glyphSize().width(); };
|
||||
|
||||
bool handleKey(uint64_t key);
|
||||
void showMenu();
|
||||
|
||||
protected:
|
||||
ColumnBinder m_columns[k_max_columns];
|
||||
// Columns Storage
|
||||
Column m_default_columns[k_max_columns];
|
||||
SlotColumn m_slot_columns[k_max_columns];
|
||||
KDColor m_background;
|
||||
KDColor m_foreground;
|
||||
const char * m_title;
|
||||
const char * m_bottom;
|
||||
bool m_centerY;
|
||||
int m_margin;
|
||||
private:
|
||||
bool m_forced_exit;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // _BOOTLOADER_MENU_H_
|
||||
67
bootloader/interface/static/interface.cpp
Normal file
67
bootloader/interface/static/interface.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <assert.h>
|
||||
#include <ion.h>
|
||||
#include <ion/timing.h>
|
||||
#include <bootloader/interface/static/interface.h>
|
||||
#include <bootloader/interface/static/messages.h>
|
||||
|
||||
#include <bootloader/computer.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
void Interface::drawImage(KDContext * ctx, const Image * image, int offset) {
|
||||
const uint8_t * data;
|
||||
size_t size;
|
||||
size_t pixelBufferSize;
|
||||
|
||||
if (image != nullptr) {
|
||||
data = image->compressedPixelData();
|
||||
size = image->compressedPixelDataSize();
|
||||
pixelBufferSize = image->width() * image->height();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
KDColor pixelBuffer[4000];
|
||||
assert(pixelBufferSize <= 4000);
|
||||
assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack
|
||||
|
||||
Ion::decompress(
|
||||
data,
|
||||
reinterpret_cast<uint8_t *>(pixelBuffer),
|
||||
size,
|
||||
pixelBufferSize * sizeof(KDColor)
|
||||
);
|
||||
|
||||
KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height());
|
||||
|
||||
ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr);
|
||||
}
|
||||
|
||||
void Interface::drawFlasher() {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
|
||||
drawImage(ctx, ImageStore::Computer, 25);
|
||||
KDSize fontSize = KDFont::LargeFont->glyphSize();
|
||||
int initPos = (320 - fontSize.width() * strlen(Messages::mainTitle)) / 2;
|
||||
ctx->drawString(Messages::mainTitle, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
|
||||
int y = ImageStore::Computer->height() + (KDFont::LargeFont->glyphSize().height() + 10) + (KDFont::SmallFont->glyphSize().height() + 10);
|
||||
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::dfuSlotsUpdate)) / 2;
|
||||
ctx->drawString(Messages::dfuSlotsUpdate, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
|
||||
}
|
||||
|
||||
void Interface::drawLoading() {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
|
||||
drawImage(ctx, ImageStore::Computer, 25);
|
||||
Ion::Timing::msleep(250);
|
||||
KDSize fontSize = KDFont::LargeFont->glyphSize();
|
||||
int initPos = (320 - fontSize.width() * strlen(Messages::mainTitle)) / 2;
|
||||
for (uint8_t i = 0; i < strlen(Messages::mainTitle); i++) {
|
||||
char tmp[2] = {Messages::mainTitle[i], '\0'};
|
||||
ctx->drawString(tmp, KDPoint(initPos + i * (fontSize.width()), ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
|
||||
Ion::Timing::msleep(50);
|
||||
}
|
||||
Ion::Timing::msleep(500);
|
||||
}
|
||||
|
||||
}
|
||||
19
bootloader/interface/static/interface.h
Normal file
19
bootloader/interface/static/interface.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef BOOTLOADER_INTERFACE_STATIC_INTERFACE_H
|
||||
#define BOOTLOADER_INTERFACE_STATIC_INTERFACE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <kandinsky/context.h>
|
||||
#include <escher/image.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class Interface {
|
||||
|
||||
public:
|
||||
static void drawImage(KDContext * ctx, const Image * image, int offset);
|
||||
static void drawLoading();
|
||||
static void drawFlasher();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
86
bootloader/interface/static/messages.h
Normal file
86
bootloader/interface/static/messages.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef BOOTLOADER_INTERFACE_STATIC_MESSAGES_H
|
||||
#define BOOTLOADER_INTERFACE_STATIC_MESSAGES_H
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class Messages {
|
||||
public:
|
||||
constexpr static const char * mainTitle = "Upsilon Calculator";
|
||||
|
||||
// Home menu
|
||||
constexpr static const char * homeTitle = "Select a slot";
|
||||
|
||||
// Slots OS Type
|
||||
constexpr static const char * upsilonSlot = "-- Upsilon ";
|
||||
constexpr static const char * khiSlot = "-- Khi ";
|
||||
constexpr static const char * omegaSlot = "-- Omega ";
|
||||
constexpr static const char * epsilonSlot = "-- Epsilon ";
|
||||
constexpr static const char * invalidSlot = "X - Invalid slot";
|
||||
|
||||
// Home Submenu
|
||||
constexpr static const char * homeSlotASubmenu = "1 - Slot A";
|
||||
constexpr static const char * homeSlotKhiSubmenu = "2 - Slot Khi";
|
||||
constexpr static const char * homeSlotBSubmenu = "3 - Slot B";
|
||||
constexpr static const char * homeInstallerSubmenu = "4 - Installer Mode";
|
||||
constexpr static const char * homeAboutSubmenu = "5 - About";
|
||||
constexpr static const char * homeRebootSubmenu = "6 - Reboot";
|
||||
|
||||
// DFU menu
|
||||
constexpr static const char * dfuTitle = "Installer";
|
||||
|
||||
constexpr static const char * dfuSlotsUpdate = "Waiting for Slots...";
|
||||
constexpr static const char * dfuBootloaderUpdate = "Waiting for Bootloader...";
|
||||
|
||||
// Installer menu
|
||||
constexpr static const char * installerTitle = "Installer mode";
|
||||
|
||||
constexpr static const char * installerText1 = "Please select a mode:";
|
||||
constexpr static const char * installerSlotsSubmenu = "1 - Flash Slots";
|
||||
constexpr static const char * installerBootloaderSubmenu = "2 - Flash Bootloader";
|
||||
|
||||
// Bootloader Crash Handler
|
||||
constexpr static const char * bootloaderCrashTitle = "BOOTLOADER CRASH";
|
||||
|
||||
constexpr static const char * bootloaderCrashMessage1 = "The bootloader has crashed.";
|
||||
constexpr static const char * bootloaderCrashMessage2 = "Please restart the calculator.";
|
||||
|
||||
// Recovery menu
|
||||
constexpr static const char * recoveryTitle = "Recovery mode";
|
||||
|
||||
constexpr static const char * recoveryMessage1 = "The bootloader has detected a crash.";
|
||||
constexpr static const char * recoveryMessage2 = "Plug the calculator to a device capable of";
|
||||
constexpr static const char * recoveryMessage3 = "accessing the internal storage.";
|
||||
constexpr static const char * recoveryMessage4 = "Press Back to continue.";
|
||||
constexpr static const char * recoveryMessage5 = "(you will not be able to recover your data !)";
|
||||
|
||||
// Warning menu
|
||||
constexpr static const char * epsilonWarningTitle = "Epsilon Slot";
|
||||
|
||||
constexpr static const char * epsilonWarningMessage1 = "!! WARNING !! ";
|
||||
constexpr static const char * epsilonWarningMessage2 = "This version of epsilon";
|
||||
constexpr static const char * epsilonWarningMessage3 = "can lock the calculator.";
|
||||
constexpr static const char * epsilonWarningMessage4 = "Proceed the boot ?";
|
||||
constexpr static const char * epsilonWarningMessage5 = "EXE - Yes";
|
||||
constexpr static const char * epsilonWarningMessage6 = "BACK - No";
|
||||
|
||||
// About menu
|
||||
constexpr static const char * aboutMenuTitle = "About";
|
||||
|
||||
constexpr static const char * aboutMessage1 = "This is the bootloader of";
|
||||
constexpr static const char * aboutMessage2 = "the Upsilon Calculator.";
|
||||
constexpr static const char * aboutMessage3 = "It is used to install";
|
||||
constexpr static const char * aboutMessage4 = "and select the OS";
|
||||
constexpr static const char * aboutMessage5 = "to boot.";
|
||||
|
||||
constexpr static const char * bootloaderVersion = "Version 1.0.0 - FREED0M";
|
||||
|
||||
//USB NAMES
|
||||
constexpr static const char * usbUpsilonBootloader = "Upsilon Bootloader";
|
||||
constexpr static const char * usbUpsilonRecovery = "Upsilon Recovery";
|
||||
constexpr static const char * usbBootloaderUpdate = "Bootloader Update";
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,11 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <bootloader/boot.h>
|
||||
#include <bootloader/interface/static/interface.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <bootloader/slots/slot_exam_mode.h>
|
||||
#include <bootloader/recovery.h>
|
||||
#include <ion/src/device/shared/drivers/flash.h>
|
||||
|
||||
__attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) {
|
||||
// Clear the screen
|
||||
@@ -10,33 +15,47 @@ __attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) {
|
||||
// Initialize the backlight
|
||||
Ion::Backlight::init();
|
||||
|
||||
// Set the mode to slot A if undefined
|
||||
if (Bootloader::Boot::mode() == Bootloader::BootMode::Unknown) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
|
||||
// We check if there is a slot in exam_mode
|
||||
|
||||
bool isSlotA = Bootloader::Slot::isFullyValid(Bootloader::Slot::A());
|
||||
|
||||
if (isSlotA) {
|
||||
Bootloader::ExamMode::ExamMode SlotAExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotAExamMode(!Bootloader::Slot::A().userlandHeader()->isOmega());
|
||||
if (SlotAExamMode != Bootloader::ExamMode::ExamMode::Off && SlotAExamMode != Bootloader::ExamMode::ExamMode::Unknown) {
|
||||
// We boot the slot in exam_mode
|
||||
Bootloader::Slot::A().boot();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle rebooting to bootloader
|
||||
if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotABootloader) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
|
||||
Bootloader::Boot::bootloader();
|
||||
} else if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotBBootloader) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotB);
|
||||
Bootloader::Boot::bootloader();
|
||||
bool isSlotB = Bootloader::Slot::isFullyValid(Bootloader::Slot::B());
|
||||
|
||||
if (isSlotB) {
|
||||
Bootloader::ExamMode::ExamMode SlotBExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotBExamMode(!Bootloader::Slot::B().userlandHeader()->isOmega());
|
||||
if (SlotBExamMode != Bootloader::ExamMode::ExamMode::Off && SlotBExamMode != Bootloader::ExamMode::ExamMode::Unknown && isSlotB) {
|
||||
// We boot the slot in exam_mode
|
||||
Bootloader::Slot::B().boot();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint64_t scan = Ion::Keyboard::scan();
|
||||
// I have no idea if this will work, but if Pariss did a good job, it should
|
||||
|
||||
// Reset+4 => Launch bootloader
|
||||
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) {
|
||||
Bootloader::Boot::bootloader();
|
||||
// Reset+1 => Launch slot A
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
|
||||
// Reset+2 => Launch slot B
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotB);
|
||||
bool isKhiSlot = Bootloader::Slot::isFullyValid(Bootloader::Slot::Khi());
|
||||
|
||||
if (isKhiSlot) {
|
||||
Bootloader::ExamMode::ExamMode KhiExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotKhiExamMode();
|
||||
if (KhiExamMode != Bootloader::ExamMode::ExamMode::Off && KhiExamMode != Bootloader::ExamMode::ExamMode::Unknown && isKhiSlot) {
|
||||
// We boot the slot in exam_mode
|
||||
Bootloader::Slot::Khi().boot();
|
||||
}
|
||||
}
|
||||
|
||||
if (Bootloader::Recovery::hasCrashed()) {
|
||||
Bootloader::Recovery::recoverData();
|
||||
}
|
||||
|
||||
Bootloader::Interface::drawLoading();
|
||||
|
||||
// Boot the firmware
|
||||
Bootloader::Boot::boot();
|
||||
}
|
||||
|
||||
87
bootloader/recovery.cpp
Normal file
87
bootloader/recovery.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include <bootloader/recovery.h>
|
||||
#include <ion.h>
|
||||
#include <ion/src/device/n0110/drivers/power.h>
|
||||
#include <ion/src/device/shared/drivers/reset.h>
|
||||
#include <ion/src/device/shared/drivers/board.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <bootloader/interface/static/interface.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
#include <bootloader/interface/menus/slot_recovery.h>
|
||||
#include <bootloader/interface/menus/crash.h>
|
||||
|
||||
constexpr static uint32_t MagicStorage = 0xEE0BDDBA;
|
||||
|
||||
void Bootloader::Recovery::crash_handler(const char *error) {
|
||||
Ion::Device::Board::shutdownPeripherals(true);
|
||||
Ion::Device::Board::initPeripherals(false);
|
||||
Ion::Timing::msleep(100);
|
||||
Ion::Backlight::init();
|
||||
Ion::Backlight::setBrightness(180);
|
||||
|
||||
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite);
|
||||
CrashMenu menu(error);
|
||||
menu.open(true);
|
||||
}
|
||||
|
||||
bool Bootloader::Recovery::hasCrashed() {
|
||||
bool isA = Bootloader::Slot::A().kernelHeader()->isValid() && Bootloader::Slot::A().userlandHeader()->isValid();
|
||||
bool isB = Bootloader::Slot::B().kernelHeader()->isValid() && Bootloader::Slot::B().userlandHeader()->isValid();
|
||||
|
||||
bool isACrashed = false;
|
||||
bool isBCrashed = false;
|
||||
|
||||
if (isA) {
|
||||
const uint32_t * storage = (uint32_t *)Bootloader::Slot::A().userlandHeader()->storageAddress();
|
||||
isACrashed = *storage == MagicStorage;
|
||||
}
|
||||
|
||||
if (isB) {
|
||||
const uint32_t * storage = (uint32_t *)Bootloader::Slot::B().userlandHeader()->storageAddress();
|
||||
isBCrashed = *storage == MagicStorage;
|
||||
}
|
||||
|
||||
return (isACrashed || isBCrashed);
|
||||
}
|
||||
|
||||
Bootloader::Recovery::CrashedSlot Bootloader::Recovery::getSlotConcerned() {
|
||||
bool isA = Bootloader::Slot::A().kernelHeader()->isValid() && Bootloader::Slot::A().userlandHeader()->isValid();
|
||||
bool isB = Bootloader::Slot::B().kernelHeader()->isValid() && Bootloader::Slot::B().userlandHeader()->isValid();
|
||||
|
||||
bool isACrashed = false;
|
||||
bool isBCrashed = false;
|
||||
|
||||
if (isA) {
|
||||
const uint32_t * storage = (uint32_t *)Bootloader::Slot::A().userlandHeader()->storageAddress();
|
||||
isACrashed = *storage == MagicStorage;
|
||||
}
|
||||
|
||||
if (isB) {
|
||||
const uint32_t * storage = (uint32_t *)Bootloader::Slot::B().userlandHeader()->storageAddress();
|
||||
isBCrashed = *storage == MagicStorage;
|
||||
}
|
||||
|
||||
assert(isACrashed || isBCrashed);
|
||||
|
||||
if (isACrashed) {
|
||||
return CrashedSlot(Bootloader::Slot::A().userlandHeader()->storageSize(), Bootloader::Slot::A().userlandHeader()->storageAddress());
|
||||
} else {
|
||||
return CrashedSlot(Bootloader::Slot::B().userlandHeader()->storageSize(), Bootloader::Slot::B().userlandHeader()->storageAddress());
|
||||
}
|
||||
}
|
||||
|
||||
void Bootloader::Recovery::recoverData() {
|
||||
Ion::Device::Board::initPeripherals(false);
|
||||
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite);
|
||||
Ion::Backlight::init();
|
||||
|
||||
USBData udata = USBData::Recovery((uint32_t)getSlotConcerned().getStorageAddress(), (uint32_t)getSlotConcerned().getStorageSize());
|
||||
|
||||
SlotRecoveryMenu menu = SlotRecoveryMenu(&udata);
|
||||
menu.open();
|
||||
|
||||
// Invalidate storage header
|
||||
*(uint32_t *)(getSlotConcerned().getStorageAddress()) = (uint32_t)0x0;
|
||||
|
||||
}
|
||||
31
bootloader/recovery.h
Normal file
31
bootloader/recovery.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef BOOTLOADER_RECOVERY_H_
|
||||
#define BOOTLOADER_RECOVERY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Bootloader {
|
||||
class Recovery {
|
||||
public:
|
||||
class CrashedSlot {
|
||||
public:
|
||||
CrashedSlot(const size_t size, const void * address) : m_storageAddress(address), m_storageSize(size) {}
|
||||
|
||||
const size_t getStorageSize() const { return m_storageSize; }
|
||||
const void * getStorageAddress() const { return m_storageAddress; }
|
||||
|
||||
private:
|
||||
const void * m_storageAddress;
|
||||
const size_t m_storageSize;
|
||||
};
|
||||
|
||||
static CrashedSlot getSlotConcerned();
|
||||
|
||||
static void crash_handler(const char * error);
|
||||
static void recoverData();
|
||||
static bool hasCrashed();
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif //BOOTLOADER_RECOVERY_H_
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <bootloader/kernel_header.h>
|
||||
#include <bootloader/slots/kernel_header.h>
|
||||
#include <bootloader/utility.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
@@ -22,4 +23,12 @@ const void(*KernelHeader::startPointer() const)() {
|
||||
return m_startPointer;
|
||||
}
|
||||
|
||||
const bool KernelHeader::isAboveVersion16 () const {
|
||||
int sum = Utility::versionSum(m_version, 2);
|
||||
char newVersion[] = "16";
|
||||
int min = Utility::versionSum(newVersion, 2);
|
||||
return sum >= min;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef BOOTLOADER_KERNEL_HEADER_H
|
||||
#define BOOTLOADER_KERNEL_HEADER_H
|
||||
#ifndef BOOTLOADER_SLOTS_KERNEL_HEADER_H
|
||||
#define BOOTLOADER_SLOTS_KERNEL_HEADER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <bootloader/utility.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
@@ -10,6 +11,7 @@ public:
|
||||
const char * version() const;
|
||||
const char * patchLevel() const;
|
||||
const bool isValid() const;
|
||||
const bool isAboveVersion16() const;
|
||||
|
||||
const uint32_t* stackPointer() const;
|
||||
const void(*startPointer() const)();
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <bootloader/slot.h>
|
||||
#include <bootloader/slots/slot.h>
|
||||
#include <ion/src/device/shared/drivers/board.h>
|
||||
#include <ion/src/device/shared/drivers/flash.h>
|
||||
#include <bootloader/boot.h>
|
||||
|
||||
extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void));
|
||||
|
||||
@@ -13,6 +15,10 @@ const Slot Slot::B() {
|
||||
return Slot(0x90400000);
|
||||
}
|
||||
|
||||
const Slot Slot::Khi() {
|
||||
return Slot(0x90180000);
|
||||
}
|
||||
|
||||
const KernelHeader* Slot::kernelHeader() const {
|
||||
return m_kernelHeader;
|
||||
}
|
||||
@@ -22,6 +28,15 @@ const UserlandHeader* Slot::userlandHeader() const {
|
||||
}
|
||||
|
||||
[[ noreturn ]] void Slot::boot() const {
|
||||
|
||||
if (m_address == 0x90000000) {
|
||||
// If we are booting from slot A, we need to lock the slot B
|
||||
Ion::Device::Flash::LockSlotB();
|
||||
} else {
|
||||
// If we are booting from slot B, we need to lock the slot A (and Khi)
|
||||
Ion::Device::Flash::LockSlotA();
|
||||
}
|
||||
|
||||
// Configure the MPU for the booted firmware
|
||||
Ion::Device::Board::bootloaderMPU();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef BOOTLOADER_SLOT_H
|
||||
#define BOOTLOADER_SLOT_H
|
||||
#ifndef BOOTLOADER_SLOTS_SLOT_H
|
||||
#define BOOTLOADER_SLOTS_SLOT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -13,18 +13,26 @@ class Slot {
|
||||
public:
|
||||
Slot(uint32_t address) :
|
||||
m_kernelHeader(reinterpret_cast<KernelHeader*>(address)),
|
||||
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)) { }
|
||||
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)),
|
||||
m_address(address) { }
|
||||
|
||||
const KernelHeader* kernelHeader() const;
|
||||
const UserlandHeader* userlandHeader() const;
|
||||
[[ noreturn ]] void boot() const;
|
||||
const uint32_t address() const { return m_address; }
|
||||
|
||||
static const Slot A();
|
||||
static const Slot B();
|
||||
static const Slot Khi();
|
||||
|
||||
static bool isFullyValid(const Slot& slot) {
|
||||
return slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid();
|
||||
}
|
||||
|
||||
private:
|
||||
const KernelHeader* m_kernelHeader;
|
||||
const UserlandHeader* m_userlandHeader;
|
||||
const uint32_t m_address;
|
||||
|
||||
};
|
||||
|
||||
209
bootloader/slots/slot_exam_mode.cpp
Normal file
209
bootloader/slots/slot_exam_mode.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include <bootloader/slots/slot_exam_mode.h>
|
||||
#include <assert.h>
|
||||
#include <ion.h>
|
||||
#include <ion/src/device/shared/drivers/flash.h>
|
||||
|
||||
namespace Bootloader {
|
||||
namespace ExamMode {
|
||||
|
||||
/* The exam mode is written in flash so that it is resilient to resets.
|
||||
* We erase the dedicated flash sector (all bits written to 1) and, upon
|
||||
* deactivating or activating standard, nosym or Dutch exam mode we write one, two or tree
|
||||
* bits to 0. To determine in which exam mode we are, we count the number of
|
||||
* leading 0 bits. If it is equal to:
|
||||
* - 0[3]: the exam mode is off;
|
||||
* - 1[3]: the standard exam mode is activated;
|
||||
* - 2[3]: the NoSym exam mode is activated;
|
||||
* - 3[3]: the Dutch exam mode is activated;
|
||||
* - 4[3]: the NoSymNoText exam mode is activated. */
|
||||
|
||||
/* significantExamModeAddress returns the first uint32_t * in the exam mode
|
||||
* flash sector that does not point to 0. If this flash sector has only 0s or
|
||||
* if it has only one 1, it is erased (to 1) and significantExamModeAddress
|
||||
* returns the start of the sector. */
|
||||
|
||||
constexpr static size_t numberOfBitsInByte = 8;
|
||||
|
||||
// if i = 0b000011101, firstOneBitInByte(i) returns 5
|
||||
size_t numberOfBitsAfterLeadingZeroes(int i) {
|
||||
int minShift = 0;
|
||||
int maxShift = numberOfBitsInByte;
|
||||
while (maxShift > minShift+1) {
|
||||
int shift = (minShift + maxShift)/2;
|
||||
int shifted = i >> shift;
|
||||
if (shifted == 0) {
|
||||
maxShift = shift;
|
||||
} else {
|
||||
minShift = shift;
|
||||
}
|
||||
}
|
||||
return maxShift;
|
||||
}
|
||||
|
||||
uint8_t * SignificantSlotAExamModeAddress(bool newVersion) {
|
||||
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotAStartExamAddress(newVersion);
|
||||
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotAEndExamAddress(newVersion);
|
||||
if (!newVersion) {
|
||||
assert((persitence_end_32 - persitence_start_32) % 4 == 0);
|
||||
while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) {
|
||||
// Scan by groups of 32 bits to reach first non-zero bit
|
||||
persitence_start_32++;
|
||||
}
|
||||
uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32;
|
||||
uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32;
|
||||
while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) {
|
||||
// Scan by groups of 8 bits to reach first non-zero bit
|
||||
persitence_start_8++;
|
||||
}
|
||||
if (persitence_start_8 == persitence_end_8
|
||||
// we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector
|
||||
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
|
||||
assert(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotAStartExamAddress(newVersion)) >= 0);
|
||||
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotAStartExamAddress(newVersion)));
|
||||
return (uint8_t *)SlotsExamMode::getSlotAStartExamAddress(newVersion);
|
||||
}
|
||||
|
||||
return persitence_start_8;
|
||||
} else {
|
||||
persitence_end_32 = persitence_end_32 - 1;
|
||||
while (persitence_end_32 - (uint32_t)(10 / 8) >= persitence_end_32 && *persitence_end_32 == 0xFFFFFFFF) {
|
||||
persitence_end_32 -= 1;
|
||||
}
|
||||
uint8_t * start = reinterpret_cast<uint8_t *>(persitence_start_32);
|
||||
uint8_t * end = reinterpret_cast<uint8_t *>(persitence_end_32 + 1) - 1;
|
||||
while (end >= start + 2 && *end == 0xFF) {
|
||||
end -= 1;
|
||||
}
|
||||
return end - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t * SignificantSlotBExamModeAddress(bool newVersion) {
|
||||
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotBStartExamAddress(newVersion);
|
||||
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotBEndExamAddress(newVersion);
|
||||
if (!newVersion) {
|
||||
assert((persitence_end_32 - persitence_start_32) % 4 == 0);
|
||||
while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) {
|
||||
// Scan by groups of 32 bits to reach first non-zero bit
|
||||
persitence_start_32++;
|
||||
}
|
||||
uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32;
|
||||
uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32;
|
||||
while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) {
|
||||
// Scan by groups of 8 bits to reach first non-zero bit
|
||||
persitence_start_8++;
|
||||
}
|
||||
if (persitence_start_8 == persitence_end_8
|
||||
// we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector
|
||||
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
|
||||
assert(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotBStartExamAddress(newVersion)) >= 0);
|
||||
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotBStartExamAddress(newVersion)));
|
||||
return (uint8_t *)SlotsExamMode::getSlotBStartExamAddress(newVersion);
|
||||
}
|
||||
|
||||
return persitence_start_8;
|
||||
} else {
|
||||
persitence_end_32 = persitence_end_32 - 1;
|
||||
while (persitence_end_32 - (uint32_t)(10 / 8) >= persitence_end_32 && *persitence_end_32 == 0xFFFFFFFF) {
|
||||
persitence_end_32 -= 1;
|
||||
}
|
||||
uint8_t * start = reinterpret_cast<uint8_t *>(persitence_start_32);
|
||||
uint8_t * end = reinterpret_cast<uint8_t *>(persitence_end_32 + 1) - 1;
|
||||
while (end >= start + 2 && *end == 0xFF) {
|
||||
end -= 1;
|
||||
}
|
||||
return end - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t * SignificantSlotKhiExamModeAddress() {
|
||||
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotKhiStartExamAddress();
|
||||
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotKhiEndExamAddress();
|
||||
|
||||
assert((persitence_end_32 - persitence_start_32) % 4 == 0);
|
||||
while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) {
|
||||
// Scan by groups of 32 bits to reach first non-zero bit
|
||||
persitence_start_32++;
|
||||
}
|
||||
uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32;
|
||||
uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32;
|
||||
while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) {
|
||||
// Scan by groups of 8 bits to reach first non-zero bit
|
||||
persitence_start_8++;
|
||||
}
|
||||
if (persitence_start_8 == persitence_end_8
|
||||
// we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector
|
||||
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
|
||||
assert(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotKhiStartExamAddress()) >= 0);
|
||||
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotKhiStartExamAddress()));
|
||||
return (uint8_t *)SlotsExamMode::getSlotKhiStartExamAddress();
|
||||
}
|
||||
|
||||
return persitence_start_8;
|
||||
}
|
||||
|
||||
uint8_t SlotsExamMode::FetchSlotAExamMode(bool newVersion) {
|
||||
uint8_t * readingAddress = SignificantSlotAExamModeAddress(newVersion);
|
||||
if (!newVersion) {
|
||||
// Count the number of 0[3] before reading address
|
||||
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotAStartExamAddress(newVersion)) * numberOfBitsInByte) % 4;
|
||||
// Count the number of 0[3] at reading address
|
||||
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
|
||||
return (nbOfZerosBefore + numberOfLeading0) % 4;
|
||||
} else {
|
||||
return *((uint8_t *)readingAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t SlotsExamMode::FetchSlotBExamMode(bool newVersion) {
|
||||
uint8_t * readingAddress = SignificantSlotBExamModeAddress(newVersion);
|
||||
if (!newVersion) {
|
||||
// Count the number of 0[3] before reading address
|
||||
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotBStartExamAddress(newVersion)) * numberOfBitsInByte) % 4;
|
||||
// Count the number of 0[3] at reading address
|
||||
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
|
||||
return (nbOfZerosBefore + numberOfLeading0) % 4;
|
||||
} else {
|
||||
return *((uint8_t *)readingAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t SlotsExamMode::FetchSlotKhiExamMode() {
|
||||
uint8_t * readingAddress = SignificantSlotKhiExamModeAddress();
|
||||
// Count the number of 0[3] before reading address
|
||||
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotKhiStartExamAddress()) * numberOfBitsInByte) % 4;
|
||||
// Count the number of 0[3] at reading address
|
||||
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
|
||||
return (nbOfZerosBefore + numberOfLeading0) % 4;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotAStartExamAddress(bool newVersion) {
|
||||
return newVersion ? SlotAExamModeBufferStartNewVersions : SlotAExamModeBufferStartOldVersions;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotAEndExamAddress(bool newVersion) {
|
||||
return newVersion ? SlotAExamModeBufferEndNewVersions : SlotAExamModeBufferEndOldVersions;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotBStartExamAddress(bool newVersion) {
|
||||
return newVersion ? SlotBExamModeBufferStartNewVersions : SlotBExamModeBufferStartOldVersions;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotBEndExamAddress(bool newVersion) {
|
||||
return newVersion ? SlotBExamModeBufferEndNewVersions : SlotBExamModeBufferEndOldVersions;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotKhiStartExamAddress() {
|
||||
return SlotKhiExamModeBufferStart;
|
||||
}
|
||||
|
||||
uint32_t SlotsExamMode::getSlotKhiEndExamAddress() {
|
||||
return SlotKhiExamModeBufferEnd;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
53
bootloader/slots/slot_exam_mode.h
Normal file
53
bootloader/slots/slot_exam_mode.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef BOOTLOADER_SLOTS_EXAM_MODE_H
|
||||
#define BOOTLOADER_SLOTS_EXAM_MODE_H
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
}
|
||||
|
||||
namespace Bootloader {
|
||||
namespace ExamMode {
|
||||
|
||||
static const uint32_t SlotAExamModeBufferStartOldVersions = 0x90001000;
|
||||
static const uint32_t SlotAExamModeBufferEndOldVersions = 0x90003000;
|
||||
|
||||
static const uint32_t SlotAExamModeBufferStartNewVersions = 0x903f0000;
|
||||
static const uint32_t SlotAExamModeBufferEndNewVersions = 0x90400000;
|
||||
|
||||
static const uint32_t SlotBExamModeBufferStartOldVersions = 0x90401000;
|
||||
static const uint32_t SlotBExamModeBufferEndOldVersions = 0x90403000;
|
||||
|
||||
static const uint32_t SlotBExamModeBufferStartNewVersions = 0x907f0000;
|
||||
static const uint32_t SlotBExamModeBufferEndNewVersions = 0x90800000;
|
||||
|
||||
static const uint32_t SlotKhiExamModeBufferStart = 0x90181000;
|
||||
static const uint32_t SlotKhiExamModeBufferEnd = 0x90183000;
|
||||
|
||||
class SlotsExamMode{
|
||||
public:
|
||||
static uint8_t FetchSlotAExamMode(bool newVersion);
|
||||
static uint8_t FetchSlotBExamMode(bool newVerion);
|
||||
static uint8_t FetchSlotKhiExamMode();
|
||||
|
||||
static uint32_t getSlotAStartExamAddress(bool newVersion);
|
||||
static uint32_t getSlotAEndExamAddress(bool newVersion);
|
||||
static uint32_t getSlotBStartExamAddress(bool newVersion);
|
||||
static uint32_t getSlotBEndExamAddress(bool newVersion);
|
||||
static uint32_t getSlotKhiStartExamAddress();
|
||||
static uint32_t getSlotKhiEndExamAddress();
|
||||
|
||||
};
|
||||
|
||||
enum class ExamMode : int8_t {
|
||||
Unknown = -1,
|
||||
Off = 0,
|
||||
Standard = 1,
|
||||
NoSym = 2,
|
||||
NoSymNoText = 3,
|
||||
Dutch = 4,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <bootloader/userland_header.h>
|
||||
#include <bootloader/slots/userland_header.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
@@ -23,11 +23,19 @@ const char * UserlandHeader::omegaVersion() const {
|
||||
}
|
||||
|
||||
const bool UserlandHeader::isUpsilon() const {
|
||||
return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicFooter == UpsilonMagic;
|
||||
return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicHeader == UpsilonMagic;
|
||||
}
|
||||
|
||||
const char * UserlandHeader::upsilonVersion() const {
|
||||
return m_UpsilonVersion;
|
||||
}
|
||||
|
||||
const void * UserlandHeader::storageAddress() const {
|
||||
return m_storageAddressRAM;
|
||||
}
|
||||
|
||||
const size_t UserlandHeader::storageSize() const {
|
||||
return m_storageSizeRAM;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef BOOTLOADER_USERLAND_HEADER_H
|
||||
#define BOOTLOADER_USERLAND_HEADER_H
|
||||
#ifndef BOOTLOADER_SLOTS_USERLAND_HEADER_H
|
||||
#define BOOTLOADER_SLOTS_USERLAND_HEADER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -15,6 +15,8 @@ public:
|
||||
const char * omegaVersion() const;
|
||||
const bool isUpsilon() const;
|
||||
const char * upsilonVersion() const;
|
||||
const void * storageAddress() const;
|
||||
const size_t storageSize() const;
|
||||
|
||||
private:
|
||||
UserlandHeader();
|
||||
@@ -36,6 +36,7 @@ void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT]
|
||||
__attribute__((used))
|
||||
= {
|
||||
(void*) Bootloader::Boot::mode,
|
||||
// This function doesn't do anything ...
|
||||
(void*) Bootloader::Boot::setMode
|
||||
};
|
||||
|
||||
|
||||
39
bootloader/usb_data.cpp
Normal file
39
bootloader/usb_data.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <bootloader/usb_data.h>
|
||||
#include <bootloader/interface/static/messages.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern "C" {
|
||||
}
|
||||
|
||||
static char data[255];
|
||||
|
||||
const char * Bootloader::USBData::buildStringDescriptor(StringHeader header, uint32_t startAddress, uint32_t size) {
|
||||
strlcpy(data, header.getString(), sizeof(data));
|
||||
itoa((int32_t)startAddress, &data[strlen(header.getString())], 16);
|
||||
data[strlen(header.getString()) + 8] = '/';
|
||||
data[strlen(header.getString()) + 8 + 1] = '0';
|
||||
data[strlen(header.getString()) + 8 + 2] = '1';
|
||||
data[strlen(header.getString()) + 8 + 3] = '*';
|
||||
data[strlen(header.getString()) + 8 + 4] = '0';
|
||||
itoa((int32_t)size/1024, &data[strlen(header.getString()) + 8 + 5], 10);
|
||||
data[strlen(header.getString()) + 8 + 5 + 2] = 'K';
|
||||
data[strlen(header.getString()) + 8 + 5 + 2 + 1] = 'g';
|
||||
data[strlen(header.getString()) + 8 + 5 + 2 + 2] = '\0';
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
const Bootloader::USBData Bootloader::USBData::DEFAULT() {
|
||||
return USBData("@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg", Messages::usbUpsilonBootloader, ProtectionState(false, true));
|
||||
}
|
||||
|
||||
const Bootloader::USBData Bootloader::USBData::BOOTLOADER_UPDATE() {
|
||||
return USBData("@Flash/0x08000000/04*016Kg", Messages::usbBootloaderUpdate, ProtectionState(true, false));
|
||||
}
|
||||
|
||||
Bootloader::USBData Bootloader::USBData::Recovery(uint32_t startAddress, uint32_t size) {
|
||||
return USBData(buildStringDescriptor(StringHeader::SRAM(), startAddress, size), Messages::usbUpsilonRecovery, ProtectionState(false, false));
|
||||
}
|
||||
56
bootloader/usb_data.h
Normal file
56
bootloader/usb_data.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef BOOTLOADER_USB_DATA_H_
|
||||
#define BOOTLOADER_USB_DATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
class ProtectionState {
|
||||
public:
|
||||
ProtectionState(bool unlockInternal = false, bool unlockExternal = true) : m_protectInternal(!unlockInternal), m_protectExternal(!unlockExternal) {};
|
||||
|
||||
bool isProtectedInternal() const { return m_protectInternal; }
|
||||
bool isProtectedExternal() const { return m_protectExternal; }
|
||||
|
||||
private:
|
||||
bool m_protectInternal;
|
||||
bool m_protectExternal;
|
||||
};
|
||||
|
||||
class USBData {
|
||||
public:
|
||||
class StringHeader{
|
||||
public:
|
||||
StringHeader(const char * string) : m_string(string) {};
|
||||
|
||||
const char * getString() const { return m_string; }
|
||||
|
||||
static const StringHeader Flash() { return StringHeader("@Flash/0x"); }
|
||||
static const StringHeader SRAM() { return StringHeader("@SRAM/0x"); }
|
||||
|
||||
private:
|
||||
const char * m_string;
|
||||
};
|
||||
|
||||
USBData(const char * desc, const char * name, ProtectionState data = ProtectionState()) : m_stringDescriptor(desc), m_name(name), m_data(data) {};
|
||||
|
||||
const char * stringDescriptor() const { return m_stringDescriptor; }
|
||||
const char * getName() const { return m_name; }
|
||||
ProtectionState getData() const { return m_data; }
|
||||
|
||||
static const char * buildStringDescriptor(StringHeader header, uint32_t startAddress, uint32_t size);
|
||||
|
||||
static const USBData DEFAULT();
|
||||
static const USBData BOOTLOADER_UPDATE();
|
||||
static USBData Recovery(uint32_t startAddress, uint32_t size);
|
||||
|
||||
private:
|
||||
const char * m_stringDescriptor;
|
||||
const char * m_name;
|
||||
ProtectionState m_data;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BOOTLOADER_USB_DATA_H_
|
||||
10
bootloader/utility.cpp
Normal file
10
bootloader/utility.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <bootloader/utility.h>
|
||||
#include <string.h>
|
||||
|
||||
int Utility::versionSum(const char * version, int length) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
sum += version[i] * (strlen(version) * 100 - i * 10);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
8
bootloader/utility.h
Normal file
8
bootloader/utility.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _BOOTLOADER_ITOA_H_
|
||||
#define _BOOTLOADER_ITOA_H_
|
||||
|
||||
namespace Utility {
|
||||
extern int versionSum(const char * version, int length);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
|
||||
MAGIK_CODE = [0x32, 0x30, 0x30, 0x36]
|
||||
MAGIK_POS = 0x44F
|
||||
# Disable Script
|
||||
sys.exit(0)
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
ext_path = os.path.join(os.getcwd(), sys.argv[1])
|
||||
|
||||
@@ -35,7 +35,6 @@ HANDY_TARGETS += epsilon.A epsilon.B
|
||||
|
||||
.PHONY: epsilon
|
||||
epsilon: $(BUILD_DIR)/epsilon.onboarding.bin
|
||||
$(PYTHON) build/device/secure_ext.py $(BUILD_DIR)/epsilon.onboarding.bin
|
||||
.DEFAULT_GOAL := epsilon
|
||||
|
||||
.PHONY: %_flash
|
||||
@@ -54,4 +53,3 @@ binpack: $(BUILD_DIR)/epsilon.onboarding.bin
|
||||
cp $(BUILD_DIR)/epsilon.onboarding.bin $(BUILD_DIR)/binpack
|
||||
cd $(BUILD_DIR) && for binary in epsilon.onboarding.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.bin
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
HANDY_TARGETS += test.external_flash.write test.external_flash.read bootloader
|
||||
|
||||
$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld
|
||||
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relegated_src) $(runner_src)
|
||||
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src)
|
||||
$(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src))
|
||||
$(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src))
|
||||
|
||||
.PHONY: bootloader
|
||||
bootloader: $(BUILD_DIR)/bootloader.bin
|
||||
$(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src),usbxip)
|
||||
$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/n0110/internal_flash.ld
|
||||
$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/bootloader/internal_flash.ld
|
||||
|
||||
.PHONY: %_flash
|
||||
%_flash: $(BUILD_DIR)/%.dfu
|
||||
@@ -36,4 +36,3 @@ 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
|
||||
|
||||
@@ -161,9 +161,6 @@ protected:
|
||||
};
|
||||
RecordIterator end() const { return RecordIterator(nullptr); }
|
||||
|
||||
mutable Record m_lastRecordRetrieved;
|
||||
mutable char * m_lastRecordRetrievedPointer;
|
||||
MetadataMapHeader * m_metadataMapHeader;
|
||||
private:
|
||||
constexpr static uint32_t Magic = 0xEE0BDDBA;
|
||||
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
|
||||
@@ -196,6 +193,10 @@ private:
|
||||
char m_buffer[k_storageSize];
|
||||
uint32_t m_magicFooter;
|
||||
StorageDelegate * m_delegate;
|
||||
protected:
|
||||
mutable Record m_lastRecordRetrieved;
|
||||
mutable char * m_lastRecordRetrievedPointer;
|
||||
MetadataMapHeader * m_metadataMapHeader;
|
||||
};
|
||||
|
||||
/* Some apps memoize records and need to be notified when a record might have
|
||||
|
||||
@@ -8,7 +8,7 @@ bool isPlugged();
|
||||
bool isEnumerated(); // Speed-enumerated, to be accurate
|
||||
void clearEnumerationInterrupt();
|
||||
|
||||
void DFU(bool exitWithKeyboard = true, bool unlocked = false, int level = 0);
|
||||
void DFU(bool exitWithKeyboard = true, void * data = nullptr);
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ ifeq ($(EPSILON_TELEMETRY),1)
|
||||
ion_src += ion/src/shared/telemetry_console.cpp
|
||||
endif
|
||||
|
||||
ion_device_src += ion/src/shared/collect_registers.cpp
|
||||
ion_src += ion/src/shared/collect_registers.cpp
|
||||
|
||||
IN_FACTORY ?= 0
|
||||
|
||||
|
||||
@@ -6,15 +6,6 @@
|
||||
#include <drivers/rtc.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <drivers/timing.h>
|
||||
#include <drivers/power.h>
|
||||
#include <drivers/wakeup.h>
|
||||
#include <drivers/battery.h>
|
||||
#include <drivers/usb.h>
|
||||
#include <drivers/led.h>
|
||||
#include <kandinsky.h>
|
||||
#include <regs/config/pwr.h>
|
||||
#include <regs/config/rcc.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
typedef void (*cxx_constructor)();
|
||||
|
||||
@@ -32,6 +23,15 @@ extern "C" {
|
||||
extern char _isr_vector_table_end_ram;
|
||||
}
|
||||
|
||||
void __attribute__((noinline)) abort() {
|
||||
#ifdef NDEBUG
|
||||
Ion::Device::Reset::core();
|
||||
#else
|
||||
while (1) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* In order to ensure that this method is execute from the external flash, we
|
||||
* forbid inlining it.*/
|
||||
|
||||
@@ -68,153 +68,6 @@ static void __attribute__((noinline)) jump_to_external_flash() {
|
||||
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
|
||||
* thus forbid inlining to prevent executing this code from external flash
|
||||
* (just in case 'start' was to be called from the external flash). */
|
||||
@@ -225,6 +78,8 @@ void __attribute__((noinline)) start() {
|
||||
|
||||
/* Initialize the FPU as early as possible.
|
||||
* For example, static C++ objects are very likely to manipulate float values */
|
||||
Ion::Device::Board::initFPU();
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -418,6 +418,9 @@ bool pcbVersionIsLocked() {
|
||||
return *reinterpret_cast<const uint8_t *>(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0;
|
||||
}
|
||||
|
||||
|
||||
void jumpToInternalBootloader() {}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +89,15 @@ public:
|
||||
public:
|
||||
using Register8::Register8;
|
||||
REGS_BOOL_FIELD_R(BUSY, 0);
|
||||
REGS_BOOL_FIELD(BP, 2);
|
||||
REGS_BOOL_FIELD(BP1, 3);
|
||||
REGS_BOOL_FIELD(BP2, 4);
|
||||
REGS_BOOL_FIELD(TB, 5);
|
||||
};
|
||||
class StatusRegister2 : public Register8 {
|
||||
public:
|
||||
using Register8::Register8;
|
||||
REGS_BOOL_FIELD(SRP1, 0);
|
||||
REGS_BOOL_FIELD(QE, 1);
|
||||
};
|
||||
};
|
||||
@@ -428,6 +433,46 @@ void unlockFlash() {
|
||||
wait();
|
||||
}
|
||||
|
||||
void LockSlotA() {
|
||||
unset_memory_mapped_mode();
|
||||
unlockFlash();
|
||||
send_command(Command::WriteEnable);
|
||||
wait();
|
||||
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
|
||||
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
|
||||
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
|
||||
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(¤tStatusRegister2), sizeof(currentStatusRegister2));
|
||||
statusRegister2.setQE(currentStatusRegister2.getQE());
|
||||
statusRegister2.setSRP1(true);
|
||||
statusRegister1.setTB(true);
|
||||
statusRegister1.setBP2(true);
|
||||
statusRegister1.setBP1(true);
|
||||
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
|
||||
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
|
||||
wait();
|
||||
set_as_memory_mapped();
|
||||
}
|
||||
|
||||
void LockSlotB() {
|
||||
unset_memory_mapped_mode();
|
||||
unlockFlash();
|
||||
send_command(Command::WriteEnable);
|
||||
wait();
|
||||
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
|
||||
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
|
||||
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
|
||||
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(¤tStatusRegister2), sizeof(currentStatusRegister2));
|
||||
statusRegister2.setQE(currentStatusRegister2.getQE());
|
||||
statusRegister2.setSRP1(true);
|
||||
statusRegister1.setTB(false);
|
||||
statusRegister1.setBP2(true);
|
||||
statusRegister1.setBP1(true);
|
||||
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
|
||||
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
|
||||
wait();
|
||||
set_as_memory_mapped();
|
||||
}
|
||||
|
||||
void MassErase() {
|
||||
if (Config::NumberOfSectors == 0) {
|
||||
return;
|
||||
|
||||
@@ -378,6 +378,13 @@ void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) {
|
||||
void EraseSector(int i) {
|
||||
asm("cpsid if");
|
||||
(*reinterpret_cast<void(**)(int)>(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector)))(i);
|
||||
asm("cpsie if");
|
||||
}
|
||||
|
||||
void LockSlotA() {
|
||||
}
|
||||
|
||||
void LockSlotB() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
131
ion/src/device/bootloader/internal_flash.ld
Normal file
131
ion/src/device/bootloader/internal_flash.ld
Normal file
@@ -0,0 +1,131 @@
|
||||
/* Same as flash.ld but everything is linked in internal flash */
|
||||
|
||||
MEMORY {
|
||||
INTERNAL_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
|
||||
SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
STACK_SIZE = 32K;
|
||||
TRAMPOLINES_OFFSET = 0xE000;
|
||||
CUSTOM_TRAMPOLINES_OFFSET = 64K - 64;
|
||||
|
||||
SECTIONS {
|
||||
.isr_vector_table ORIGIN(INTERNAL_FLASH) : {
|
||||
/* When booting, the STM32F412 fetches the content of address 0x0, and
|
||||
* extracts from it various key infos: the initial value of the PC register
|
||||
* (program counter), the initial value of the stack pointer, and various
|
||||
* entry points to interrupt service routines. This data is called the ISR
|
||||
* vector table.
|
||||
*
|
||||
* Note that address 0x0 is always an alias. It points to the beginning of
|
||||
* Flash, SRAM, or integrated bootloader depending on the boot mode chosen.
|
||||
* (This mode is chosen by setting the BOOTn pins on the chip).
|
||||
*
|
||||
* We're generating the ISR vector table in code because it's very
|
||||
* convenient: using function pointers, we can easily point to the service
|
||||
* routine for each interrupt. */
|
||||
KEEP(*(.isr_vector_table))
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.header : {
|
||||
KEEP(*(.header))
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.exam_mode_buffer : {
|
||||
_exam_mode_buffer_start = .;
|
||||
KEEP(*(.exam_mode_buffer))
|
||||
/* We don't set it because we will not use it */
|
||||
/* . = ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE; */
|
||||
_exam_mode_buffer_end = .;
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.fake_isr_function : {
|
||||
. = ALIGN(4);
|
||||
_fake_isr_function_start = .;
|
||||
KEEP(*(.fake_isr_function))
|
||||
KEEP(*(.fake_isr_function.*))
|
||||
_fake_isr_function_end = .;
|
||||
}
|
||||
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.init_array : {
|
||||
. = ALIGN(4);
|
||||
_init_array_start = .;
|
||||
KEEP (*(.init_array*))
|
||||
_init_array_end = .;
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.data : {
|
||||
/* The data section is written to Flash but linked as if it were in RAM.
|
||||
*
|
||||
* This is required because its initial value matters (so it has to be in
|
||||
* persistant memory in the first place), but it is a R/W area of memory
|
||||
* so it will have to live in RAM upon execution (in linker lingo, that
|
||||
* translates to the data section having a LMA in Flash and a VMA in RAM).
|
||||
*
|
||||
* This means we'll have to copy it from Flash to RAM on initialization.
|
||||
* To do this, we'll need to know the source location of the data section
|
||||
* (in Flash), the target location (in RAM), and the size of the section.
|
||||
* That's why we're defining three symbols that we'll use in the initial-
|
||||
* -ization routine. */
|
||||
. = ALIGN(4);
|
||||
_data_section_start_flash = LOADADDR(.data);
|
||||
_data_section_start_ram = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
_data_section_end_ram = .;
|
||||
} >SRAM AT> INTERNAL_FLASH
|
||||
|
||||
.trampolines_table : {
|
||||
. = ORIGIN(INTERNAL_FLASH) + TRAMPOLINES_OFFSET;
|
||||
KEEP(*(.trampolines_table));
|
||||
} > INTERNAL_FLASH
|
||||
|
||||
.custom_trampolines_table : {
|
||||
. = ORIGIN(INTERNAL_FLASH) + CUSTOM_TRAMPOLINES_OFFSET;
|
||||
KEEP(*(.custom_trampolines_table));
|
||||
} > INTERNAL_FLASH
|
||||
|
||||
.bss : {
|
||||
/* The bss section contains data for all uninitialized variables
|
||||
* So like the .data section, it will go in RAM, but unlike the data section
|
||||
* we don't care at all about an initial value.
|
||||
*
|
||||
* Before execution, crt0 will erase that section of memory though, so we'll
|
||||
* need pointers to the beginning and end of this section. */
|
||||
. = ALIGN(4);
|
||||
_bss_section_start_ram = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
/* The compiler may choose to allocate uninitialized global variables as
|
||||
* COMMON blocks. This can be disabled with -fno-common if needed. */
|
||||
*(COMMON)
|
||||
_bss_section_end_ram = .;
|
||||
} >SRAM
|
||||
|
||||
.heap : {
|
||||
_heap_start = .;
|
||||
/* Note: We don't increment "." here, we set it. */
|
||||
. = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE);
|
||||
_heap_end = .;
|
||||
} >SRAM
|
||||
|
||||
.stack : {
|
||||
. = ALIGN(8);
|
||||
_stack_end = .;
|
||||
. += (STACK_SIZE - 8);
|
||||
. = ALIGN(8);
|
||||
_stack_start = .;
|
||||
} >SRAM
|
||||
}
|
||||
99
ion/src/device/bootloader/usb/Makefile
Normal file
99
ion/src/device/bootloader/usb/Makefile
Normal file
@@ -0,0 +1,99 @@
|
||||
# USB code
|
||||
|
||||
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/, \
|
||||
calculator.cpp \
|
||||
dfu_interface.cpp\
|
||||
)
|
||||
|
||||
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/stack/, \
|
||||
device.cpp\
|
||||
endpoint0.cpp \
|
||||
interface.cpp\
|
||||
request_recipient.cpp\
|
||||
setup_packet.cpp\
|
||||
streamable.cpp\
|
||||
)
|
||||
|
||||
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/stack/descriptor/, \
|
||||
bos_descriptor.cpp\
|
||||
configuration_descriptor.cpp \
|
||||
descriptor.cpp\
|
||||
device_descriptor.cpp\
|
||||
device_capability_descriptor.cpp\
|
||||
dfu_functional_descriptor.cpp\
|
||||
extended_compat_id_descriptor.cpp \
|
||||
interface_descriptor.cpp\
|
||||
language_id_string_descriptor.cpp \
|
||||
microsoft_os_string_descriptor.cpp\
|
||||
platform_device_capability_descriptor.cpp\
|
||||
string_descriptor.cpp\
|
||||
url_descriptor.cpp\
|
||||
webusb_platform_descriptor.cpp\
|
||||
)
|
||||
|
||||
# DFU code
|
||||
|
||||
ion_device_dfu_src += liba/src/abs.c
|
||||
ion_device_dfu_src += liba/src/assert.c
|
||||
ion_device_dfu_src += liba/src/strlen.c
|
||||
ion_device_dfu_src += liba/src/strlcpy.c
|
||||
ion_device_dfu_src += liba/src/memset.c
|
||||
ion_device_dfu_src += liba/src/memcpy.c
|
||||
ion_device_dfu_src += libaxx/src/cxxabi/pure_virtual.cpp
|
||||
ion_device_dfu_src += ion/src/device/bootloader/usb/boot.cpp
|
||||
ion_device_dfu_src += ion/src/device/n0110/drivers/board.cpp
|
||||
ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp
|
||||
ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp
|
||||
ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp
|
||||
ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp
|
||||
ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \
|
||||
backlight.cpp \
|
||||
battery.cpp \
|
||||
base64.cpp \
|
||||
board.cpp \
|
||||
console_uart.cpp \
|
||||
crc32.cpp \
|
||||
display.cpp \
|
||||
events_keyboard_platform.cpp \
|
||||
flash.cpp \
|
||||
internal_flash.cpp \
|
||||
keyboard.cpp \
|
||||
led.cpp \
|
||||
power.cpp\
|
||||
random.cpp\
|
||||
reset.cpp \
|
||||
serial_number.cpp \
|
||||
swd.cpp \
|
||||
timing.cpp \
|
||||
usb.cpp \
|
||||
usb_desc.cpp \
|
||||
wakeup.cpp \
|
||||
)
|
||||
|
||||
# Sources required to execute DFU in place
|
||||
ion_device_src += ion/src/device/bootloader/usb/dfu_xip.cpp:+usbxip
|
||||
ion_device_src += $(addsuffix :+usbxip,$(ion_device_usb_src))
|
||||
|
||||
# Sources required to execute DFU in RAM
|
||||
|
||||
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.elf: LDSCRIPT = ion/src/device/bootloader/usb/dfu.ld
|
||||
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.elf: $(call object_for,$(ion_device_usb_src) $(ion_device_dfu_src))
|
||||
|
||||
# In order to link the dfu bootloader inside the epsilon firmware, we need to
|
||||
# turn the dfu binary (dfu.bin) into an elf object.
|
||||
# By default, 'objcpy' generates a section 'data' and two symbols to the
|
||||
# start and the end of the binary input respectively named:
|
||||
# - '_binary_[file name]_[file extension]_start'
|
||||
# - '_binary_[file name]_[file extension]_end'.
|
||||
# For our purpose, dfu.o can go in rodata section and we rename the start and
|
||||
# end of binary symbols: _dfu_bootloader_flash_[start/end]
|
||||
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.o: $(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.bin
|
||||
$(call rule_label,OBJCOPY)
|
||||
$(Q) cd $(dir $<) ; $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata.dfu_bootloader --redefine-sym _binary_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_dfu_bin_end=_dfu_bootloader_flash_end $(notdir $<) $(notdir $@)
|
||||
|
||||
ion_device_src += ion/src/device/bootloader/usb/dfu.cpp:-usbxip
|
||||
ion_device_src += ion/src/device/bootloader/usb/dfu_relocated.cpp:-usbxip
|
||||
|
||||
ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \
|
||||
usb_desc.cpp \
|
||||
)
|
||||
2
ion/src/device/bootloader/usb/boot.cpp
Normal file
2
ion/src/device/bootloader/usb/boot.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
extern "C" void abort() {
|
||||
}
|
||||
94
ion/src/device/bootloader/usb/calculator.cpp
Normal file
94
ion/src/device/bootloader/usb/calculator.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "calculator.h"
|
||||
#include <ion/usb.h>
|
||||
#include <drivers/keyboard.h>
|
||||
#include <drivers/serial_number.h>
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void Calculator::PollAndReset(bool exitWithKeyboard, void * data) {
|
||||
char serialNumber[Ion::Device::SerialNumber::Length+1];
|
||||
Ion::Device::SerialNumber::copy(serialNumber);
|
||||
|
||||
Calculator c(serialNumber, data == nullptr ? stringDescriptor() : static_cast<Bootloader::USBData *>(data)->stringDescriptor(), data == nullptr ? "Upsilon Bootloader" : static_cast<Bootloader::USBData *>(data)->getName());
|
||||
|
||||
if (data != nullptr) {
|
||||
c.setConfigData(static_cast<Bootloader::USBData *>(data));
|
||||
}
|
||||
|
||||
/* Leave DFU mode if the Back key is pressed, the calculator unplugged or the
|
||||
* USB core soft-disconnected. */
|
||||
Ion::Keyboard::Key exitKey = Ion::Keyboard::Key::Back;
|
||||
uint8_t exitKeyRow = Ion::Device::Keyboard::rowForKey(exitKey);
|
||||
uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey);
|
||||
|
||||
Ion::Device::Keyboard::activateRow(exitKeyRow);
|
||||
|
||||
while (!(exitWithKeyboard && !c.isErasingAndWriting() && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) &&
|
||||
Ion::USB::isPlugged() &&
|
||||
!c.isSoftDisconnected()) {
|
||||
c.poll();
|
||||
}
|
||||
if (!c.isSoftDisconnected()) {
|
||||
c.detach();
|
||||
}
|
||||
if (c.resetOnDisconnect()) {
|
||||
c.leave(c.addressPointer());
|
||||
}
|
||||
}
|
||||
|
||||
Descriptor * Calculator::descriptor(uint8_t type, uint8_t index) {
|
||||
/* Special case: Microsoft OS String Descriptor should be returned when
|
||||
* searching for string descriptor at index 0xEE. */
|
||||
if (type == m_microsoftOSStringDescriptor.type() && index == 0xEE) {
|
||||
return &m_microsoftOSStringDescriptor;
|
||||
}
|
||||
int typeCount = 0;
|
||||
for (size_t i=0; i<sizeof(m_descriptors)/sizeof(m_descriptors[0]); i++) {
|
||||
Descriptor * descriptor = m_descriptors[i];
|
||||
if (descriptor->type() != type) {
|
||||
continue;
|
||||
}
|
||||
if (typeCount == index) {
|
||||
return descriptor;
|
||||
} else {
|
||||
typeCount++;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Calculator::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (Device::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
|
||||
return true;
|
||||
}
|
||||
if (request->requestType() == SetupPacket::RequestType::Vendor) {
|
||||
if (request->bRequest() == k_webUSBVendorCode && request->wIndex() == 2) {
|
||||
// This is a WebUSB, GET_URL request
|
||||
assert(request->wValue() == k_webUSBLandingPageIndex);
|
||||
return getURLCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
}
|
||||
if (request->bRequest() == k_microsoftOSVendorCode && request->wIndex() == 0x0004) {
|
||||
// This is a Microsoft OS descriptor, Extended Compat ID request
|
||||
assert(request->wValue() == 0);
|
||||
return getExtendedCompatIDCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Calculator::getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = m_workshopURLDescriptor.copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Calculator::getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = m_extendedCompatIdDescriptor.copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
176
ion/src/device/bootloader/usb/calculator.h
Normal file
176
ion/src/device/bootloader/usb/calculator.h
Normal file
@@ -0,0 +1,176 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_CALCULATOR_H
|
||||
#define ION_DEVICE_SHARED_USB_CALCULATOR_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <drivers/usb.h>
|
||||
#include <drivers/config/usb.h>
|
||||
#include "dfu_interface.h"
|
||||
#include "stack/device.h"
|
||||
#include "stack/descriptor/bos_descriptor.h"
|
||||
#include "stack/descriptor/configuration_descriptor.h"
|
||||
#include "stack/descriptor/descriptor.h"
|
||||
#include "stack/descriptor/device_descriptor.h"
|
||||
#include "stack/descriptor/dfu_functional_descriptor.h"
|
||||
#include "stack/descriptor/extended_compat_id_descriptor.h"
|
||||
#include "stack/descriptor/interface_descriptor.h"
|
||||
#include "stack/descriptor/language_id_string_descriptor.h"
|
||||
#include "stack/descriptor/microsoft_os_string_descriptor.h"
|
||||
#include "stack/descriptor/string_descriptor.h"
|
||||
#include "stack/descriptor/url_descriptor.h"
|
||||
#include "stack/descriptor/webusb_platform_descriptor.h"
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class Calculator : public Device {
|
||||
public:
|
||||
static void PollAndReset(bool exitWithKeyboard, void * data)
|
||||
__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
|
||||
Calculator(const char * serialNumber, const char * desc, const char * product) :
|
||||
Device(&m_dfuInterface),
|
||||
m_usbConfig(nullptr),
|
||||
m_deviceDescriptor(
|
||||
0x0210, /* bcdUSB: USB Specification Number which the device complies
|
||||
* to. Must be greater than 0x0200 to use the BOS. */
|
||||
0, // bDeviceClass: The class is defined by the interface.
|
||||
0, // bDeviceSUBClass: The subclass is defined by the interface.
|
||||
0, // bDeviceProtocol: The protocol is defined by the interface.
|
||||
64, // bMaxPacketSize0: Maximum packet size for endpoint 0
|
||||
0x0483, // idVendor
|
||||
0xA291, // idProduct
|
||||
0x0100, // bcdDevice: Device Release Number
|
||||
1, // iManufacturer: Index of the manufacturer name string, see m_descriptor
|
||||
2, // iProduct: Index of the product name string, see m_descriptor
|
||||
3, // iSerialNumber: Index of the SerialNumber string, see m_descriptor
|
||||
1), // bNumConfigurations
|
||||
m_dfuFunctionalDescriptor(
|
||||
0b0011, /* bmAttributes:
|
||||
* - bitWillDetach: If true, the device will perform a bus
|
||||
* detach-attach sequence when it receives a DFU_DETACH
|
||||
* request. The host must not issue a USB Reset.
|
||||
* - bitManifestationTolerant: if true, the device is able to
|
||||
* communicate via USB after Manifestation phase. The
|
||||
* manifestation phase implies a reset in the calculator, so,
|
||||
* even if the device is still plugged, it needs to be
|
||||
* re-enumerated to communicate.
|
||||
* - bitCanUpload
|
||||
* - bitCanDnload */
|
||||
0, /* wDetachTimeOut: Time, in milliseconds, that the device in APP
|
||||
* mode will wait after receipt of the DFU_DETACH request before
|
||||
* switching to DFU mode. It does not apply to the calculator.*/
|
||||
2048, // wTransferSize: Maximum number of bytes that the device can accept per control-write transaction
|
||||
0x0100),// bcdDFUVersion
|
||||
m_interfaceDescriptor(
|
||||
0, // bInterfaceNumber
|
||||
k_dfuInterfaceAlternateSetting, // bAlternateSetting
|
||||
0, // bNumEndpoints: Other than endpoint 0
|
||||
0xFE, // bInterfaceClass: DFU (https://www.usb.org/defined-class-codes)
|
||||
1, // bInterfaceSubClass: DFU
|
||||
2, // bInterfaceProtocol: DFU Mode (not DFU Runtime, which would be 1)
|
||||
4, // iInterface: Index of the Interface string, see m_descriptor
|
||||
&m_dfuFunctionalDescriptor),
|
||||
m_configurationDescriptor(
|
||||
9 + 9 + 9, // wTotalLength: configuration descriptor + interface descriptor + dfu functional descriptor lengths
|
||||
1, // bNumInterfaces
|
||||
k_bConfigurationValue, // bConfigurationValue
|
||||
0, // iConfiguration: No string descriptor for the configuration
|
||||
0x80, /* bmAttributes:
|
||||
* Bit 7: Reserved, set to 1
|
||||
* Bit 6: Self Powered
|
||||
* Bit 5: Remote Wakeup (allows the device to wake up the host when the host is in suspend)
|
||||
* Bit 4..0: Reserved, set to 0 */
|
||||
0x32, // bMaxPower: half of the Maximum Power Consumption
|
||||
&m_interfaceDescriptor),
|
||||
m_webUSBPlatformDescriptor(
|
||||
k_webUSBVendorCode,
|
||||
k_webUSBLandingPageIndex),
|
||||
m_bosDescriptor(
|
||||
5 + 24, // wTotalLength: BOS descriptor + webusb platform descriptor lengths
|
||||
1, // bNumDeviceCapabilities
|
||||
&m_webUSBPlatformDescriptor),
|
||||
m_languageStringDescriptor(),
|
||||
m_manufacturerStringDescriptor("NumWorks"),
|
||||
m_productStringDescriptor(product),
|
||||
m_serialNumberStringDescriptor(serialNumber),
|
||||
m_interfaceStringDescriptor(desc),
|
||||
//m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"),
|
||||
/* Switch to this descriptor to use dfu-util to write in the SRAM.
|
||||
* FIXME Should be an alternate Interface. */
|
||||
m_microsoftOSStringDescriptor(k_microsoftOSVendorCode),
|
||||
m_workshopURLDescriptor(URLDescriptor::Scheme::HTTPS, "getupsilon.web.app"),
|
||||
m_extendedCompatIdDescriptor("WINUSB"),
|
||||
m_descriptors{
|
||||
&m_deviceDescriptor, // Type = Device, Index = 0
|
||||
&m_configurationDescriptor, // Type = Configuration, Index = 0
|
||||
&m_languageStringDescriptor, // Type = String, Index = 0
|
||||
&m_manufacturerStringDescriptor, // Type = String, Index = 1
|
||||
&m_productStringDescriptor, // Type = String, Index = 2
|
||||
&m_serialNumberStringDescriptor, // Type = String, Index = 3
|
||||
&m_interfaceStringDescriptor, // Type = String, Index = 4
|
||||
&m_bosDescriptor // Type = BOS, Index = 0
|
||||
},
|
||||
m_dfuInterface(this, &m_ep0, k_dfuInterfaceAlternateSetting)
|
||||
{
|
||||
}
|
||||
uint32_t addressPointer() const { return m_dfuInterface.addressPointer(); }
|
||||
bool isErasingAndWriting() const { return m_dfuInterface.isErasingAndWriting(); }
|
||||
|
||||
void setConfigData(Bootloader::USBData * data) { m_usbConfig = data; m_dfuInterface.setDfuConfig(data->getData()); }
|
||||
Bootloader::USBData * getConfigData() const { return m_usbConfig; }
|
||||
protected:
|
||||
Descriptor * descriptor(uint8_t type, uint8_t index) override;
|
||||
void setActiveConfiguration(uint8_t configurationIndex) override {
|
||||
assert(configurationIndex == k_bConfigurationValue);
|
||||
}
|
||||
uint8_t getActiveConfiguration() override {
|
||||
return k_bConfigurationValue;
|
||||
}
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t k_bConfigurationValue = 1;
|
||||
static constexpr uint8_t k_dfuInterfaceAlternateSetting = 0;
|
||||
static constexpr uint8_t k_webUSBVendorCode = 1;
|
||||
static constexpr uint8_t k_webUSBLandingPageIndex = 1;
|
||||
static constexpr uint8_t k_microsoftOSVendorCode = 2;
|
||||
|
||||
// WebUSB and MicrosoftOSDescriptor commands
|
||||
bool getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
|
||||
// Descriptors
|
||||
Bootloader::USBData * m_usbConfig;
|
||||
DeviceDescriptor m_deviceDescriptor;
|
||||
DFUFunctionalDescriptor m_dfuFunctionalDescriptor;
|
||||
InterfaceDescriptor m_interfaceDescriptor;
|
||||
ConfigurationDescriptor m_configurationDescriptor;
|
||||
WebUSBPlatformDescriptor m_webUSBPlatformDescriptor;
|
||||
BOSDescriptor m_bosDescriptor;
|
||||
LanguageIDStringDescriptor m_languageStringDescriptor;
|
||||
StringDescriptor m_manufacturerStringDescriptor;
|
||||
StringDescriptor m_productStringDescriptor;
|
||||
StringDescriptor m_serialNumberStringDescriptor;
|
||||
StringDescriptor m_interfaceStringDescriptor;
|
||||
MicrosoftOSStringDescriptor m_microsoftOSStringDescriptor;
|
||||
URLDescriptor m_workshopURLDescriptor;
|
||||
ExtendedCompatIDDescriptor m_extendedCompatIdDescriptor;
|
||||
|
||||
Descriptor * m_descriptors[8];
|
||||
/* m_descriptors contains only descriptors that should be returned via the
|
||||
* method descriptor(uint8_t type, uint8_t index), so do not count descriptors
|
||||
* included in other descriptors or returned by other functions. */
|
||||
|
||||
// Interface
|
||||
DFUInterface m_dfuInterface;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
57
ion/src/device/bootloader/usb/dfu.ld
Normal file
57
ion/src/device/bootloader/usb/dfu.ld
Normal file
@@ -0,0 +1,57 @@
|
||||
/* DFU transfers can serve two purposes:
|
||||
* - Transfering RAM data between the machine and the host, e.g. Python scripts
|
||||
* - Upgrading the flash memory to perform a software update
|
||||
*
|
||||
* The second case raises a huge issue: code cannot be executed from memory that
|
||||
* is being modified. We're solving this issue by copying the DFU code in RAM.
|
||||
*
|
||||
* This linker script will generate some code that expects to be executed from a
|
||||
* fixed address in RAM. The corresponding instructions will be embedded in the
|
||||
* main Epsilon ELF file, and copied to that address before execution.
|
||||
*
|
||||
* This address needs to live in RAM, and needs to be temporarily overwriteable
|
||||
* when the program is being run. Epsilon has a large stack to allow deeply
|
||||
* recursive code to run. But when doing DFU transfers it is safe to assume we
|
||||
* will need very little stack space. We're therefore using the topmost 8K of
|
||||
* the stack reserved by Epsilon.
|
||||
*
|
||||
* Last but not least, we'll want to jump to a known entry point when running
|
||||
* the DFU code (namely, Ion::USB::Device::Calculator::Poll). We're simply
|
||||
* making sure this is the first symbol output. */
|
||||
|
||||
EPSILON_STACK_END = 0x20000000 + 256K - 32K;
|
||||
|
||||
MEMORY {
|
||||
RAM_BUFFER (rw) : ORIGIN = EPSILON_STACK_END, LENGTH = 9K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.dfu_entry_point))
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >RAM_BUFFER
|
||||
|
||||
.data : {
|
||||
/* We need to keep these symbols. */
|
||||
*(.data._ZN3Ion6Device13ExternalFlashL14sOperatingModeE)
|
||||
*(.data._ZN3Ion6Device5BoardL18sStandardFrequencyE)
|
||||
} >RAM_BUFFER
|
||||
|
||||
/DISCARD/ : {
|
||||
/* For now, we do not need .bss and .data sections. This allows us to simply
|
||||
* skip any rt0-style initialization and jump straight into the PollAndReset
|
||||
* routine. */
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
}
|
||||
}
|
||||
|
||||
301
ion/src/device/bootloader/usb/dfu_interface.cpp
Normal file
301
ion/src/device/bootloader/usb/dfu_interface.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
#include "dfu_interface.h"
|
||||
#include <string.h>
|
||||
#include <drivers/flash.h>
|
||||
#include <ion/timing.h>
|
||||
|
||||
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);
|
||||
|
||||
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) {
|
||||
// There was no erase waiting.
|
||||
return;
|
||||
}
|
||||
|
||||
willErase();
|
||||
|
||||
Bootloader::ProtectionState config = getDfuConfig();
|
||||
|
||||
// More simple to read
|
||||
if ((0x08000000 <= m_eraseAddress && m_eraseAddress <= 0x08010000)&& !m_dfuData.isProtectedInternal()) {
|
||||
Flash::EraseSector(m_erasePage);
|
||||
} else if ((0x90000000 <= m_eraseAddress && m_eraseAddress <= 0x90800000)&& !m_dfuData.isProtectedExternal()) {
|
||||
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) {
|
||||
|
||||
Bootloader::ProtectionState config = getDfuConfig();
|
||||
|
||||
if (m_writeAddress >= 0x08000000 && m_writeAddress <= 0x08010000 && !m_dfuData.isProtectedInternal()) {
|
||||
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
|
||||
} else if (m_writeAddress >= 0x90000000 && m_writeAddress <= 0x90800000 && !m_dfuData.isProtectedExternal()) {
|
||||
Flash::WriteMemory(reinterpret_cast<uint8_t *>(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();
|
||||
}
|
||||
|
||||
void DFUInterface::copyDfuData() {
|
||||
m_dfuData = Bootloader::ProtectionState(!m_dfuConfig.isProtectedInternal(), !m_dfuConfig.isProtectedExternal());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
192
ion/src/device/bootloader/usb/dfu_interface.h
Normal file
192
ion/src/device/bootloader/usb/dfu_interface.h
Normal file
@@ -0,0 +1,192 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
|
||||
#define ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include "stack/device.h"
|
||||
#include "stack/interface.h"
|
||||
#include "stack/endpoint0.h"
|
||||
#include "stack/setup_packet.h"
|
||||
#include "stack/streamable.h"
|
||||
#include <bootloader/usb_data.h>
|
||||
|
||||
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),
|
||||
m_dfuConfig(),
|
||||
m_eraseAddress(0),
|
||||
m_dfuData()
|
||||
{
|
||||
}
|
||||
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 setDfuConfig(Bootloader::ProtectionState data) { m_dfuConfig = data; copyDfuData(); }
|
||||
Bootloader::ProtectionState getDfuConfig() const { return m_dfuConfig; }
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
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;
|
||||
|
||||
// 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
|
||||
* 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 copyDfuData();
|
||||
|
||||
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;
|
||||
Bootloader::ProtectionState m_dfuConfig;
|
||||
uint32_t m_eraseAddress;
|
||||
Bootloader::ProtectionState m_dfuData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
89
ion/src/device/bootloader/usb/dfu_relocated.cpp
Normal file
89
ion/src/device/bootloader/usb/dfu_relocated.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <ion.h>
|
||||
#include <ion/usb.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <drivers/cache.h>
|
||||
#include "../drivers/timing.h"
|
||||
|
||||
extern const void * _stack_end;
|
||||
extern char _dfu_bootloader_flash_start;
|
||||
extern char _dfu_bootloader_flash_end;
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
typedef void (*PollFunctionPointer)(bool exitWithKeyboard, void * data);
|
||||
|
||||
void DFU(bool exitWithKeyboard, void * data) {
|
||||
Ion::updateSlotInfo();
|
||||
|
||||
/* DFU transfers can serve two purposes:
|
||||
* - Transfering RAM data between the machine and a host, e.g. Python scripts
|
||||
* - Upgrading the flash memory to perform a software update
|
||||
*
|
||||
* The second case raises a huge issue: code cannot be executed from memory
|
||||
* that is being modified. We're solving this issue by copying the DFU code in
|
||||
* RAM.
|
||||
*
|
||||
* The new DFU address in RAM needs to be temporarily overwriteable when the
|
||||
* program is being run. Epsilon has a large stack to allow deeply recursive
|
||||
* code to run, but when doing DFU transfers it is safe to assume we will need
|
||||
* very little stack space. We're therefore using the topmost 8K of the stack
|
||||
* reserved by Epsilon. */
|
||||
|
||||
/* 1- The stack being in reverse order, the end of the stack will be the
|
||||
* beginning of the DFU bootloader copied in RAM. */
|
||||
|
||||
size_t dfu_bootloader_size = &_dfu_bootloader_flash_end - &_dfu_bootloader_flash_start;
|
||||
char * dfu_bootloader_ram_start = reinterpret_cast<char *>(&_stack_end);
|
||||
assert(&_stack_end == (void *)(0x20000000 + 256*1024 - 32*1024));
|
||||
|
||||
/* 2- Verify there is enough free space on the stack to copy the DFU code. */
|
||||
|
||||
char foo;
|
||||
char * stackPointer = &foo;
|
||||
if (dfu_bootloader_ram_start + dfu_bootloader_size > stackPointer) {
|
||||
// There is not enough room on the stack to copy the DFU bootloader.
|
||||
return;
|
||||
}
|
||||
|
||||
/* 3- Copy the DFU bootloader from Flash to RAM. */
|
||||
|
||||
memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size);
|
||||
/* The DFU bootloader might have been copied in the DCache. However, when we
|
||||
* run the instructions from the DFU bootloader, the CPU looks for
|
||||
* instructions in the ICache and then in the RAM. We thus need to flush the
|
||||
* DCache to update the RAM. */
|
||||
// Flush data cache
|
||||
Device::Cache::cleanDCache();
|
||||
|
||||
/* 4- Disable all interrupts
|
||||
* The interrupt service routines live in the Flash and could be overwritten
|
||||
* by garbage during a firmware upgrade opration, so we disable them. */
|
||||
Device::Timing::shutdown();
|
||||
|
||||
/* 5- Jump to DFU bootloader code. We made sure in the linker script that the
|
||||
* first function we want to call is at the beginning of the DFU code. */
|
||||
|
||||
PollFunctionPointer dfu_bootloader_entry = reinterpret_cast<PollFunctionPointer>(dfu_bootloader_ram_start);
|
||||
|
||||
/* To have the right debug symbols for the reallocated code, break here and:
|
||||
* - Get the address of the new .text section
|
||||
* In a terminal: arm-none-eabi-readelf -a ion/src/device/usb/dfu.elf
|
||||
* - Delete the current symbol table
|
||||
* symbol-file
|
||||
* - Add the new symbol table, with the address of the new .text section
|
||||
* add-symbol-file ion/src/device/usb/dfu.elf 0x20038000
|
||||
*/
|
||||
|
||||
dfu_bootloader_entry(exitWithKeyboard, data);
|
||||
|
||||
/* 5- Restore interrupts */
|
||||
Device::Timing::init();
|
||||
|
||||
/* 6- That's all. The DFU bootloader on the stack is now dead code that will
|
||||
* be overwritten when the stack grows. */
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
13
ion/src/device/bootloader/usb/dfu_xip.cpp
Normal file
13
ion/src/device/bootloader/usb/dfu_xip.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <ion.h>
|
||||
#include "calculator.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
void DFU(bool exitWithKeyboard, void * data) {
|
||||
Ion::updateSlotInfo();
|
||||
Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "bos_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void BOSDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_wTotalLength);
|
||||
c->push(m_bNumDeviceCaps);
|
||||
for (uint8_t i = 0; i < m_bNumDeviceCaps; i++) {
|
||||
m_deviceCapabilities[i].push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BOSDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class BOSDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr BOSDescriptor(
|
||||
uint16_t wTotalLength,
|
||||
uint8_t bNumDeviceCapabilities,
|
||||
const DeviceCapabilityDescriptor * deviceCapabilities) :
|
||||
Descriptor(0x0F),
|
||||
m_wTotalLength(wTotalLength),
|
||||
m_bNumDeviceCaps(bNumDeviceCapabilities),
|
||||
m_deviceCapabilities(deviceCapabilities)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_wTotalLength;
|
||||
uint8_t m_bNumDeviceCaps;
|
||||
const DeviceCapabilityDescriptor * m_deviceCapabilities;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "configuration_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void ConfigurationDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_wTotalLength);
|
||||
c->push(m_bNumInterfaces);
|
||||
c->push(m_bConfigurationValue);
|
||||
c->push(m_iConfiguration);
|
||||
c->push(m_bmAttributes);
|
||||
c->push(m_bMaxPower);
|
||||
for (uint8_t i = 0; i < m_bNumInterfaces; i++) {
|
||||
m_interfaces[i].push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ConfigurationDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + 5*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
#include "interface_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class ConfigurationDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr ConfigurationDescriptor(
|
||||
uint16_t wTotalLength,
|
||||
uint8_t bNumInterfaces,
|
||||
uint8_t bConfigurationValue,
|
||||
uint8_t iConfiguration,
|
||||
uint8_t bmAttributes,
|
||||
uint8_t bMaxPower,
|
||||
const InterfaceDescriptor * interfaces) :
|
||||
Descriptor(0x02),
|
||||
m_wTotalLength(wTotalLength),
|
||||
m_bNumInterfaces(bNumInterfaces),
|
||||
m_bConfigurationValue(bConfigurationValue),
|
||||
m_iConfiguration(iConfiguration),
|
||||
m_bmAttributes(bmAttributes),
|
||||
m_bMaxPower(bMaxPower),
|
||||
m_interfaces(interfaces)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_wTotalLength;
|
||||
uint8_t m_bNumInterfaces;
|
||||
uint8_t m_bConfigurationValue;
|
||||
uint8_t m_iConfiguration;
|
||||
uint8_t m_bmAttributes;
|
||||
uint8_t m_bMaxPower;
|
||||
const InterfaceDescriptor * m_interfaces;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void Descriptor::push(Channel * c) const {
|
||||
c->push(bLength());
|
||||
c->push(m_bDescriptorType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
32
ion/src/device/bootloader/usb/stack/descriptor/descriptor.h
Normal file
32
ion/src/device/bootloader/usb/stack/descriptor/descriptor.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DESCRIPTOR_H
|
||||
|
||||
#include "../streamable.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class InterfaceDescriptor;
|
||||
|
||||
class Descriptor : public Streamable {
|
||||
friend class InterfaceDescriptor;
|
||||
public:
|
||||
constexpr Descriptor(uint8_t bDescriptorType) :
|
||||
m_bDescriptorType(bDescriptorType)
|
||||
{
|
||||
}
|
||||
uint8_t type() const { return m_bDescriptorType; }
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
virtual uint8_t bLength() const { return 2*sizeof(uint8_t); }
|
||||
private:
|
||||
uint8_t m_bDescriptorType;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void DeviceCapabilityDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bDeviceCapabilityType);
|
||||
}
|
||||
|
||||
uint8_t DeviceCapabilityDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class BOSDescriptor;
|
||||
|
||||
class DeviceCapabilityDescriptor : public Descriptor {
|
||||
friend class BOSDescriptor;
|
||||
public:
|
||||
constexpr DeviceCapabilityDescriptor(uint8_t bDeviceCapabilityType) :
|
||||
Descriptor(0x10),
|
||||
m_bDeviceCapabilityType(bDeviceCapabilityType)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bDeviceCapabilityType;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "device_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void DeviceDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bcdUSB);
|
||||
c->push(m_bDeviceClass);
|
||||
c->push(m_bDeviceSubClass);
|
||||
c->push(m_bDeviceProtocol);
|
||||
c->push(m_bMaxPacketSize0);
|
||||
c->push(m_idVendor);
|
||||
c->push(m_idProduct);
|
||||
c->push(m_bcdDevice);
|
||||
c->push(m_iManufacturer);
|
||||
c->push(m_iProduct);
|
||||
c->push(m_iSerialNumber);
|
||||
c->push(m_bNumConfigurations);
|
||||
}
|
||||
|
||||
uint8_t DeviceDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t) + 4*sizeof(uint8_t) + 3*sizeof(uint16_t) + 4*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class DeviceDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr DeviceDescriptor(
|
||||
uint16_t bcdUSB,
|
||||
uint8_t bDeviceClass,
|
||||
uint8_t bDeviceSubClass,
|
||||
uint8_t bDeviceProtocol,
|
||||
uint8_t bMaxPacketSize0,
|
||||
uint16_t idVendor,
|
||||
uint16_t idProduct,
|
||||
uint16_t bcdDevice,
|
||||
uint8_t iManufacturer,
|
||||
uint8_t iProduct,
|
||||
uint8_t iSerialNumber,
|
||||
uint8_t bNumConfigurations) :
|
||||
Descriptor(0x01),
|
||||
m_bcdUSB(bcdUSB),
|
||||
m_bDeviceClass(bDeviceClass),
|
||||
m_bDeviceSubClass(bDeviceSubClass),
|
||||
m_bDeviceProtocol(bDeviceProtocol),
|
||||
m_bMaxPacketSize0(bMaxPacketSize0),
|
||||
m_idVendor(idVendor),
|
||||
m_idProduct(idProduct),
|
||||
m_bcdDevice(bcdDevice),
|
||||
m_iManufacturer(iManufacturer),
|
||||
m_iProduct(iProduct),
|
||||
m_iSerialNumber(iSerialNumber),
|
||||
m_bNumConfigurations(bNumConfigurations)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint16_t m_bcdUSB;
|
||||
uint8_t m_bDeviceClass;
|
||||
uint8_t m_bDeviceSubClass;
|
||||
uint8_t m_bDeviceProtocol;
|
||||
uint8_t m_bMaxPacketSize0;
|
||||
uint16_t m_idVendor;
|
||||
uint16_t m_idProduct;
|
||||
uint16_t m_bcdDevice;
|
||||
uint8_t m_iManufacturer;
|
||||
uint8_t m_iProduct;
|
||||
uint8_t m_iSerialNumber;
|
||||
uint8_t m_bNumConfigurations;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "dfu_functional_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void DFUFunctionalDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bmAttributes);
|
||||
c->push(m_wDetachTimeOut);
|
||||
c->push(m_wTransferSize);
|
||||
c->push(m_bcdDFUVersion);
|
||||
}
|
||||
|
||||
uint8_t DFUFunctionalDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint8_t) + 3*sizeof(uint16_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class DFUFunctionalDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr DFUFunctionalDescriptor(
|
||||
uint8_t bmAttributes,
|
||||
uint16_t wDetachTimeOut,
|
||||
uint16_t wTransferSize,
|
||||
uint16_t bcdDFUVersion) :
|
||||
Descriptor(0x21),
|
||||
m_bmAttributes(bmAttributes),
|
||||
m_wDetachTimeOut(wDetachTimeOut),
|
||||
m_wTransferSize(wTransferSize),
|
||||
m_bcdDFUVersion(bcdDFUVersion)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bmAttributes;
|
||||
uint16_t m_wDetachTimeOut;
|
||||
uint16_t m_wTransferSize;
|
||||
uint16_t m_bcdDFUVersion;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "extended_compat_id_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
ExtendedCompatIDDescriptor::ExtendedCompatIDDescriptor(const char * compatibleID) :
|
||||
m_dwLength(sizeof(uint32_t)
|
||||
+ 2*sizeof(uint16_t)
|
||||
+ sizeof(uint8_t)
|
||||
+ k_reserved1Size * sizeof(uint8_t)
|
||||
+ 2*sizeof(uint8_t)
|
||||
+ k_compatibleIDSize * sizeof(uint8_t)
|
||||
+ k_compatibleIDSize * sizeof(uint8_t)
|
||||
+ k_reserved2Size * sizeof(uint8_t)),
|
||||
m_bcdVersion(0x0100), // Microsoft OS Descriptors version 1
|
||||
m_wIndex(Index),
|
||||
m_bCount(1), // We assume one function only.
|
||||
m_reserved1{0, 0, 0, 0, 0, 0, 0},
|
||||
m_bFirstInterfaceNumber(0),
|
||||
m_bReserved(1),
|
||||
m_subCompatibleID{0, 0, 0, 0, 0, 0, 0, 0},
|
||||
m_reserved2{0, 0, 0, 0, 0, 0}
|
||||
{
|
||||
/* Compatible ID has size k_compatibleIDSize, and any unused bytes should be
|
||||
* filled with 0. */
|
||||
size_t compatibleIDSize = strlen(compatibleID);
|
||||
size_t compatibleIDCopySize = k_compatibleIDSize < compatibleIDSize ? k_compatibleIDSize : compatibleIDSize;
|
||||
for (size_t i = 0; i < compatibleIDCopySize; i++) {
|
||||
m_compatibleID[i] = compatibleID[i];
|
||||
}
|
||||
for (size_t i = compatibleIDCopySize; i < k_compatibleIDSize; i++) {
|
||||
m_compatibleID[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtendedCompatIDDescriptor::push(Channel * c) const {
|
||||
c->push(m_dwLength);
|
||||
c->push(m_bcdVersion);
|
||||
c->push(m_wIndex);
|
||||
c->push(m_bCount);
|
||||
for (uint8_t i = 0; i < k_reserved1Size; i++) {
|
||||
c->push(m_reserved1[i]);
|
||||
}
|
||||
c->push(m_bFirstInterfaceNumber);
|
||||
c->push(m_bReserved);
|
||||
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
|
||||
c->push(m_compatibleID[i]);
|
||||
}
|
||||
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
|
||||
c->push(m_subCompatibleID[i]);
|
||||
}
|
||||
for (uint8_t i = 0; i < k_reserved2Size; i++) {
|
||||
c->push(m_reserved2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
|
||||
|
||||
#include "../streamable.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
/* We use this descriptor to tell the Windows OS that the device should be
|
||||
* treated as a WinUSB device. The Extended Compat ID Descriptor can set
|
||||
* differents compat IDs according to the interface and function of the device,
|
||||
* but we assume there is only one. */
|
||||
|
||||
class ExtendedCompatIDDescriptor : public Streamable {
|
||||
public:
|
||||
static constexpr uint8_t Index = 0x0004;
|
||||
ExtendedCompatIDDescriptor(const char * compatibleID);
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
constexpr static uint8_t k_reserved1Size = 7;
|
||||
constexpr static uint8_t k_compatibleIDSize = 8;
|
||||
constexpr static uint8_t k_reserved2Size = 6;
|
||||
// Header
|
||||
uint32_t m_dwLength; // The length, in bytes, of the complete extended compat ID descriptor
|
||||
uint16_t m_bcdVersion; // The descriptor’s version number, in binary coded decimal format
|
||||
uint16_t m_wIndex; // An index that identifies the particular OS feature descriptor
|
||||
uint8_t m_bCount; // The number of function sections
|
||||
uint8_t m_reserved1[k_reserved1Size];
|
||||
// Function
|
||||
uint8_t m_bFirstInterfaceNumber; // The interface or function number
|
||||
uint8_t m_bReserved;
|
||||
uint8_t m_compatibleID[k_compatibleIDSize];
|
||||
uint8_t m_subCompatibleID[k_compatibleIDSize];
|
||||
uint8_t m_reserved2[k_reserved2Size];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "interface_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void InterfaceDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bInterfaceNumber);
|
||||
c->push(m_bAlternateSetting);
|
||||
c->push(m_bNumEndpoints);
|
||||
c->push(m_bInterfaceClass);
|
||||
c->push(m_bInterfaceSubClass);
|
||||
c->push(m_bInterfaceProtocol);
|
||||
c->push(m_iInterface);
|
||||
if (m_additionalDescriptor != nullptr) {
|
||||
m_additionalDescriptor->push(c);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t InterfaceDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + 7*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_INTERFACE_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_INTERFACE_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class ConfigurationDescriptor;
|
||||
|
||||
class InterfaceDescriptor : public Descriptor {
|
||||
friend class ConfigurationDescriptor;
|
||||
public:
|
||||
constexpr InterfaceDescriptor(
|
||||
uint8_t bInterfaceNumber,
|
||||
uint8_t bAlternateSetting,
|
||||
uint8_t bNumEndpoints,
|
||||
uint8_t bInterfaceClass,
|
||||
uint8_t bInterfaceSubClass,
|
||||
uint8_t bInterfaceProtocol,
|
||||
uint8_t iInterface,
|
||||
Descriptor * additionalDescriptor) :
|
||||
Descriptor(0x04),
|
||||
m_bInterfaceNumber(bInterfaceNumber),
|
||||
m_bAlternateSetting(bAlternateSetting),
|
||||
m_bNumEndpoints(bNumEndpoints),
|
||||
m_bInterfaceClass(bInterfaceClass),
|
||||
m_bInterfaceSubClass(bInterfaceSubClass),
|
||||
m_bInterfaceProtocol(bInterfaceProtocol),
|
||||
m_iInterface(iInterface),
|
||||
m_additionalDescriptor(additionalDescriptor)
|
||||
/* There could be more than one additional descriptor, but we do not need
|
||||
* this for now. */
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bInterfaceNumber;
|
||||
uint8_t m_bAlternateSetting;
|
||||
uint8_t m_bNumEndpoints;
|
||||
uint8_t m_bInterfaceClass;
|
||||
uint8_t m_bInterfaceSubClass;
|
||||
uint8_t m_bInterfaceProtocol;
|
||||
uint8_t m_iInterface;
|
||||
const Descriptor * m_additionalDescriptor;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "language_id_string_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void LanguageIDStringDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push((uint16_t)(0x0409));
|
||||
}
|
||||
|
||||
uint8_t LanguageIDStringDescriptor::bLength() const {
|
||||
return Descriptor::bLength() + sizeof(uint16_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
// For now this LanguageIDStringDescriptor only ever returns American English
|
||||
|
||||
class LanguageIDStringDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr LanguageIDStringDescriptor() :
|
||||
Descriptor(0x03) { }
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "microsoft_os_string_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void MicrosoftOSStringDescriptor::push(Channel * c) const {
|
||||
StringDescriptor::push(c);
|
||||
c->push(m_bMSVendorCode);
|
||||
c->push(m_bPad);
|
||||
}
|
||||
|
||||
uint8_t MicrosoftOSStringDescriptor::bLength() const {
|
||||
return StringDescriptor::bLength() + 2 * sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
|
||||
|
||||
#include "string_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class MicrosoftOSStringDescriptor : public StringDescriptor {
|
||||
public:
|
||||
constexpr MicrosoftOSStringDescriptor(uint8_t bMSVendorCode) :
|
||||
StringDescriptor("MSFT100"),
|
||||
m_bMSVendorCode(bMSVendorCode),
|
||||
m_bPad(0)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bMSVendorCode;
|
||||
uint8_t m_bPad;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "platform_device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void PlatformDeviceCapabilityDescriptor::push(Channel * c) const {
|
||||
DeviceCapabilityDescriptor::push(c);
|
||||
c->push(m_bReserved);
|
||||
for (int i = 0; i < k_platformCapabilityUUIDSize; i++) {
|
||||
c->push(m_platformCapabilityUUID[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PlatformDeviceCapabilityDescriptor::bLength() const {
|
||||
return DeviceCapabilityDescriptor::bLength() + sizeof(uint8_t) + k_platformCapabilityUUIDSize*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class PlatformDeviceCapabilityDescriptor : public DeviceCapabilityDescriptor {
|
||||
public:
|
||||
constexpr PlatformDeviceCapabilityDescriptor(const uint8_t platformCapabilityUUID[]) :
|
||||
DeviceCapabilityDescriptor(0x05),
|
||||
m_bReserved(0),
|
||||
m_platformCapabilityUUID{
|
||||
platformCapabilityUUID[0],
|
||||
platformCapabilityUUID[1],
|
||||
platformCapabilityUUID[2],
|
||||
platformCapabilityUUID[3],
|
||||
platformCapabilityUUID[4],
|
||||
platformCapabilityUUID[5],
|
||||
platformCapabilityUUID[6],
|
||||
platformCapabilityUUID[7],
|
||||
platformCapabilityUUID[8],
|
||||
platformCapabilityUUID[9],
|
||||
platformCapabilityUUID[10],
|
||||
platformCapabilityUUID[11],
|
||||
platformCapabilityUUID[12],
|
||||
platformCapabilityUUID[13],
|
||||
platformCapabilityUUID[14],
|
||||
platformCapabilityUUID[15]}
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
constexpr static uint8_t k_platformCapabilityUUIDSize = 16;
|
||||
uint8_t m_bReserved;
|
||||
uint8_t m_platformCapabilityUUID[k_platformCapabilityUUIDSize];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "string_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void StringDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
const char * stringPointer = m_string;
|
||||
while (*stringPointer != 0) {
|
||||
uint16_t stringAsUTF16CodePoint = *stringPointer;
|
||||
c->push(stringAsUTF16CodePoint);
|
||||
stringPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t StringDescriptor::bLength() const {
|
||||
// The script is returned in UTF-16, hence the multiplication.
|
||||
return Descriptor::bLength() + 2*strlen(m_string);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_STRING_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_STRING_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class StringDescriptor : public Descriptor {
|
||||
public:
|
||||
constexpr StringDescriptor(const char * string) :
|
||||
Descriptor(0x03),
|
||||
m_string(string)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
const char * m_string;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "url_descriptor.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void URLDescriptor::push(Channel * c) const {
|
||||
Descriptor::push(c);
|
||||
c->push(m_bScheme);
|
||||
const char * stringPointer = m_string;
|
||||
while (*stringPointer != 0) {
|
||||
c->push(*stringPointer);
|
||||
stringPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t URLDescriptor::bLength() const {
|
||||
// The script is returned in UTF-8.
|
||||
return Descriptor::bLength() + sizeof(uint8_t) + strlen(m_string);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_URL_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_URL_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class URLDescriptor : public Descriptor {
|
||||
public:
|
||||
enum class Scheme {
|
||||
HTTP = 0,
|
||||
HTTPS = 1,
|
||||
IncludedInURL = 255
|
||||
};
|
||||
|
||||
constexpr URLDescriptor(Scheme scheme, const char * url) :
|
||||
Descriptor(0x03),
|
||||
m_bScheme((uint8_t)scheme),
|
||||
m_string(url)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
uint8_t m_bScheme;
|
||||
const char * m_string;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "webusb_platform_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
constexpr uint8_t WebUSBPlatformDescriptor::k_webUSBUUID[];
|
||||
|
||||
void WebUSBPlatformDescriptor::push(Channel * c) const {
|
||||
PlatformDeviceCapabilityDescriptor::push(c);
|
||||
c->push(m_bcdVersion);
|
||||
c->push(m_bVendorCode);
|
||||
c->push(m_iLandingPage);
|
||||
}
|
||||
|
||||
uint8_t WebUSBPlatformDescriptor::bLength() const {
|
||||
return PlatformDeviceCapabilityDescriptor::bLength() + sizeof(uint16_t) + 2*sizeof(uint8_t);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_WEBUSB_PLATFORM_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_WEBUSB_PLATFORM_DESCRIPTOR_H
|
||||
|
||||
#include "platform_device_capability_descriptor.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class WebUSBPlatformDescriptor : public PlatformDeviceCapabilityDescriptor {
|
||||
public:
|
||||
constexpr WebUSBPlatformDescriptor(uint8_t bVendorCode, uint8_t iLandingPage) :
|
||||
PlatformDeviceCapabilityDescriptor(k_webUSBUUID),
|
||||
m_bcdVersion(0x0100),
|
||||
m_bVendorCode(bVendorCode),
|
||||
m_iLandingPage(iLandingPage)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
void push(Channel * c) const override;
|
||||
uint8_t bLength() const override;
|
||||
private:
|
||||
/* Little-endian encoding of {3408B638-09A9-47A0-8BFD-A0768815B665}.
|
||||
* See https://wicg.github.io/webusb/#webusb-platform-capability-descriptor */
|
||||
constexpr static uint8_t k_webUSBUUID[] = {
|
||||
0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
|
||||
0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
|
||||
uint16_t m_bcdVersion;
|
||||
uint8_t m_bVendorCode;
|
||||
uint8_t m_iLandingPage;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
168
ion/src/device/bootloader/usb/stack/device.cpp
Normal file
168
ion/src/device/bootloader/usb/stack/device.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "device.h"
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
using namespace Regs;
|
||||
|
||||
static inline uint16_t minUint16T(uint16_t x, uint16_t y) { return x < y ? x : y; }
|
||||
|
||||
void Device::poll() {
|
||||
// Read the interrupts
|
||||
class OTG::GINTSTS intsts(OTG.GINTSTS()->get());
|
||||
|
||||
/* SETUP or OUT transaction
|
||||
* If the Rx FIFO is not empty, there is a SETUP or OUT transaction.
|
||||
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
|
||||
if (intsts.getRXFLVL()) {
|
||||
class OTG::GRXSTSP grxstsp(OTG.GRXSTSP()->get());
|
||||
|
||||
// Store the packet status
|
||||
OTG::GRXSTSP::PKTSTS pktsts = grxstsp.getPKTSTS();
|
||||
|
||||
// We only use endpoint 0
|
||||
assert(grxstsp.getEPNUM() == 0);
|
||||
|
||||
if (pktsts == OTG::GRXSTSP::PKTSTS::OutTransferCompleted || pktsts == OTG::GRXSTSP::PKTSTS::SetupTransactionCompleted) {
|
||||
// There is no data associated with this interrupt.
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pktsts != OTG::GRXSTSP::PKTSTS::GlobalOutNAK);
|
||||
/* We did not enable the GONAKEFFM (Global OUT NAK effective mask) bit in
|
||||
* GINTSTS, so we should never get this interrupt. */
|
||||
|
||||
assert(pktsts == OTG::GRXSTSP::PKTSTS::OutReceived || pktsts == OTG::GRXSTSP::PKTSTS::SetupReceived);
|
||||
|
||||
TransactionType type = (pktsts == OTG::GRXSTSP::PKTSTS::OutReceived) ? TransactionType::Out : TransactionType::Setup;
|
||||
|
||||
if (type == TransactionType::Setup && OTG.DIEPTSIZ0()->getPKTCNT()) {
|
||||
// SETUP received but there is a packet in the Tx FIFO. Flush it.
|
||||
m_ep0.flushTxFifo();
|
||||
}
|
||||
|
||||
// Save the received packet byte count
|
||||
m_ep0.setReceivedPacketSize(grxstsp.getBCNT());
|
||||
|
||||
if (type == TransactionType::Setup) {
|
||||
m_ep0.readAndDispatchSetupPacket();
|
||||
} else {
|
||||
assert(type == TransactionType::Out);
|
||||
m_ep0.processOUTpacket();
|
||||
}
|
||||
|
||||
m_ep0.discardUnreadData();
|
||||
}
|
||||
|
||||
/* IN transactions.
|
||||
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
|
||||
if (OTG.DIEPINT(0)->getXFRC()) { // We only check endpoint 0.
|
||||
m_ep0.processINpacket();
|
||||
// Clear the Transfer Completed Interrupt
|
||||
OTG.DIEPINT(0)->setXFRC(true);
|
||||
}
|
||||
|
||||
// Handle USB RESET. ENUMDNE = **SPEED** Enumeration Done
|
||||
if (intsts.getENUMDNE()) {
|
||||
// Clear the ENUMDNE bit
|
||||
OTG.GINTSTS()->setENUMDNE(true);
|
||||
/* After a USB reset, the host talks to the device by sending messages to
|
||||
* address 0; */
|
||||
setAddress(0);
|
||||
// Flush the FIFOs
|
||||
m_ep0.reset();
|
||||
m_ep0.setup();
|
||||
/* In setup(), we should set the MPSIZ field in OTG_DIEPCTL0 to the maximum
|
||||
* packet size depending on the enumeration speed (found in OTG_DSTS). We
|
||||
* should always get FullSpeed, so we set the packet size accordingly. */
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::isSoftDisconnected() const {
|
||||
return OTG.DCTL()->getSDIS();
|
||||
}
|
||||
|
||||
void Device::detach() {
|
||||
// Get in soft-disconnected state
|
||||
OTG.DCTL()->setSDIS(true);
|
||||
}
|
||||
|
||||
void Device::leave(uint32_t leaveAddress) {
|
||||
if (leaveAddress == Ion::Device::InternalFlash::Config::StartAddress) {
|
||||
Ion::Device::Reset::coreWhilePlugged();
|
||||
} else {
|
||||
Ion::Device::Reset::jump(leaveAddress);
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
// Device only handles standard requests.
|
||||
if (request->requestType() != SetupPacket::RequestType::Standard) {
|
||||
return false;
|
||||
}
|
||||
switch (request->bRequest()) {
|
||||
case (int) Request::GetStatus:
|
||||
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetAddress:
|
||||
// Make sure the request is adress is valid.
|
||||
assert(request->wValue() < 128);
|
||||
/* According to the reference manual, the address should be set after the
|
||||
* Status stage of the current transaction, but this is not true.
|
||||
* It should be set here, after the Data stage. */
|
||||
setAddress(request->wValue());
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
case (int) Request::GetDescriptor:
|
||||
return getDescriptor(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetConfiguration:
|
||||
*transferBufferLength = 0;
|
||||
return setConfiguration(request);
|
||||
case (int) Request::GetConfiguration:
|
||||
return getConfiguration(transferBuffer, transferBufferLength);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Device::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = minUint16T(2, transferBufferMaxLength);
|
||||
for (int i = 0; i<*transferBufferLength; i++) {
|
||||
transferBuffer[i] = 0; // No remote wakeup, not self-powered.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Device::setAddress(uint8_t address) {
|
||||
OTG.DCFG()->setDAD(address);
|
||||
}
|
||||
|
||||
bool Device::getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
Descriptor * wantedDescriptor = descriptor(request->descriptorType(), request->descriptorIndex());
|
||||
if (wantedDescriptor == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*transferBufferLength = wantedDescriptor->copy(transferBuffer, transferBufferMaxLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength) {
|
||||
*transferBufferLength = 1;
|
||||
transferBuffer[0] = getActiveConfiguration();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::setConfiguration(SetupPacket * request) {
|
||||
// We support one configuration only
|
||||
setActiveConfiguration(request->wValue());
|
||||
/* There is one configuration only, we no need to set it again, just reset the
|
||||
* endpoint. */
|
||||
m_ep0.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user