mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[bootloader/storage] new bootloader and fix python issue
This commit is contained in:
@@ -140,8 +140,6 @@ protected:
|
||||
};
|
||||
RecordIterator end() const { return RecordIterator(nullptr); }
|
||||
|
||||
mutable Record m_lastRecordRetrieved;
|
||||
mutable char * m_lastRecordRetrievedPointer;
|
||||
private:
|
||||
constexpr static uint32_t Magic = 0xEE0BDDBA;
|
||||
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
|
||||
@@ -174,6 +172,9 @@ private:
|
||||
char m_buffer[k_storageSize];
|
||||
uint32_t m_magicFooter;
|
||||
StorageDelegate * m_delegate;
|
||||
public:
|
||||
mutable Record m_lastRecordRetrieved;
|
||||
mutable char * m_lastRecordRetrievedPointer;
|
||||
};
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
12
ion/src/device/bootloader/drivers/usb_desc.cpp
Normal file
12
ion/src/device/bootloader/drivers/usb_desc.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
const char* stringDescriptor() {
|
||||
return "@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
123
ion/src/device/bootloader/internal_flash.ld
Normal file
123
ion/src/device/bootloader/internal_flash.ld
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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
|
||||
|
||||
.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 sould 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.*)
|
||||
}
|
||||
}
|
||||
|
||||
314
ion/src/device/bootloader/usb/dfu_interface.cpp
Normal file
314
ion/src/device/bootloader/usb/dfu_interface.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
#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::DFUData * config = getDfuConfig();
|
||||
|
||||
if (config != nullptr) {
|
||||
// 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);
|
||||
}
|
||||
} else {
|
||||
if (m_erasePage == Flash::TotalNumberOfSectors()) {
|
||||
Flash::MassErase();
|
||||
} else {
|
||||
Flash::EraseSector(m_erasePage);
|
||||
}
|
||||
}
|
||||
|
||||
/* Put an out of range value in m_erasePage to indicate that no erase is
|
||||
* waiting. */
|
||||
m_erasePage = -1;
|
||||
m_state = State::dfuDNLOADIDLE;
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
void DFUInterface::writeOnMemory() {
|
||||
if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
|
||||
// Write on SRAM
|
||||
// FIXME We should check that we are not overriding the current instructions.
|
||||
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
|
||||
} else if (Flash::SectorAtAddress(m_writeAddress) >= 0) {
|
||||
|
||||
Bootloader::DFUData * config = getDfuConfig();
|
||||
|
||||
if (config != nullptr) {
|
||||
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 {
|
||||
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::DFUData(!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(nullptr),
|
||||
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::DFUData * data) { m_dfuConfig = data; copyDfuData(); }
|
||||
Bootloader::DFUData * 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::DFUData * m_dfuConfig;
|
||||
uint32_t m_eraseAddress;
|
||||
Bootloader::DFUData 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_CAPABLITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABLITY_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_CAPABLITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABLITY_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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
67
ion/src/device/bootloader/usb/stack/device.h
Normal file
67
ion/src/device/bootloader/usb/stack/device.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_DEVICE_H
|
||||
#define ION_DEVICE_SHARED_USB_DEVICE_H
|
||||
|
||||
#include "descriptor/descriptor.h"
|
||||
#include "endpoint0.h"
|
||||
#include "interface.h"
|
||||
#include "request_recipient.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
// We only handle control transfers, on EP0.
|
||||
class Device : public RequestRecipient {
|
||||
public:
|
||||
Device(Interface * interface) :
|
||||
RequestRecipient(&m_ep0),
|
||||
m_ep0(this, interface),
|
||||
m_resetOnDisconnect(false)
|
||||
{
|
||||
}
|
||||
void poll();
|
||||
bool isSoftDisconnected() const;
|
||||
void detach();
|
||||
void leave(uint32_t leaveAddress);
|
||||
bool resetOnDisconnect() { return m_resetOnDisconnect; }
|
||||
void setResetOnDisconnect(bool reset) { m_resetOnDisconnect = reset; }
|
||||
protected:
|
||||
virtual Descriptor * descriptor(uint8_t type, uint8_t index) = 0;
|
||||
virtual void setActiveConfiguration(uint8_t configurationIndex) = 0;
|
||||
virtual uint8_t getActiveConfiguration() = 0;
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
Endpoint0 m_ep0;
|
||||
private:
|
||||
// USB Standard Device Request Codes
|
||||
enum class Request {
|
||||
GetStatus = 0,
|
||||
ClearFeature = 1,
|
||||
SetFeature = 3,
|
||||
SetAddress = 5,
|
||||
GetDescriptor = 6,
|
||||
SetDescriptor = 7,
|
||||
GetConfiguration = 8,
|
||||
SetConfiguration = 9,
|
||||
};
|
||||
|
||||
enum class TransactionType {
|
||||
Setup,
|
||||
In,
|
||||
Out
|
||||
};
|
||||
|
||||
void setAddress(uint8_t address);
|
||||
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength);
|
||||
bool setConfiguration(SetupPacket * request);
|
||||
|
||||
bool m_resetOnDisconnect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
348
ion/src/device/bootloader/usb/stack/endpoint0.cpp
Normal file
348
ion/src/device/bootloader/usb/stack/endpoint0.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
#include "endpoint0.h"
|
||||
#include <string.h>
|
||||
#include <regs/regs.h>
|
||||
#include "device.h"
|
||||
#include "interface.h"
|
||||
#include "request_recipient.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
using namespace Regs;
|
||||
|
||||
constexpr int Endpoint0::k_maxPacketSize;
|
||||
constexpr uint16_t Endpoint0::MaxTransferSize;
|
||||
|
||||
void Endpoint0::setup() {
|
||||
// Setup the IN direction
|
||||
|
||||
// Reset the device IN endpoint 0 transfer size register
|
||||
class OTG::DIEPTSIZ0 dieptsiz0(0);
|
||||
/* Transfer size. The core interrupts the application only after it has
|
||||
* exhausted the transfer size amount of data. The transfer size is set to the
|
||||
* maximum packet size, to be interrupted at the end of each packet. */
|
||||
dieptsiz0.setXFRSIZ(k_maxPacketSize);
|
||||
OTG.DIEPTSIZ0()->set(dieptsiz0);
|
||||
|
||||
// Reset the device IN endpoint 0 control register
|
||||
class OTG::DIEPCTL0 diepctl0(0); // Reset value
|
||||
// Set the maximum packet size
|
||||
diepctl0.setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64);
|
||||
// Set the NAK bit: all IN transactions on endpoint 0 receive a NAK answer
|
||||
diepctl0.setSNAK(true);
|
||||
// Enable the endpoint
|
||||
diepctl0.setEPENA(true);
|
||||
OTG.DIEPCTL0()->set(diepctl0);
|
||||
|
||||
// Setup the OUT direction
|
||||
|
||||
setupOut();
|
||||
// Set the NAK bit
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
// Enable the endpoint
|
||||
enableOut();
|
||||
|
||||
// Setup the Tx FIFO
|
||||
|
||||
/* Tx FIFO depth
|
||||
* We process each packet as soon as it arrives, so we only need
|
||||
* k_maxPacketSize bytes. TX0FD being in terms of 32-bit words, we divide
|
||||
* k_maxPacketSize by 4. */
|
||||
OTG.DIEPTXF0()->setTX0FD(k_maxPacketSize/4);
|
||||
/* Tx FIFO RAM start address. It starts just after the Rx FIFOso the value is
|
||||
* Rx FIFO start address (0) + Rx FIFO depth. the Rx FIFO depth is set in
|
||||
* usb.cpp, but because the code is linked separately, we cannot get it. */
|
||||
OTG.DIEPTXF0()->setTX0FSA(128);
|
||||
}
|
||||
|
||||
void Endpoint0::setupOut() {
|
||||
class OTG::DOEPTSIZ0 doeptsiz0(0);
|
||||
// Number of back-to-back SETUP data packets the endpoint can receive
|
||||
doeptsiz0.setSTUPCNT(1);
|
||||
// Packet count, false if a packet is written into the Rx FIFO
|
||||
doeptsiz0.setPKTCNT(true);
|
||||
/* Transfer size. The core interrupts the application only after it has
|
||||
* exhausted the transfer size amount of data. The transfer size is set to the
|
||||
* maximum packet size, to be interrupted at the end of each packet. */
|
||||
doeptsiz0.setXFRSIZ(64);
|
||||
OTG.DOEPTSIZ0()->set(doeptsiz0);
|
||||
}
|
||||
|
||||
void Endpoint0::setOutNAK(bool nak) {
|
||||
m_forceNAK = nak;
|
||||
/* We need to keep track of the NAK state of the endpoint to use the value
|
||||
* after a setupOut in poll() of device.cpp. */
|
||||
if (nak) {
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
} else {
|
||||
OTG.DOEPCTL0()->setCNAK(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::enableOut() {
|
||||
OTG.DOEPCTL0()->setEPENA(true);
|
||||
}
|
||||
|
||||
void Endpoint0::reset() {
|
||||
flushTxFifo();
|
||||
flushRxFifo();
|
||||
}
|
||||
|
||||
void Endpoint0::readAndDispatchSetupPacket() {
|
||||
setOutNAK(true);
|
||||
|
||||
// Read the 8-bytes Setup packet
|
||||
if (readPacket(m_largeBuffer, sizeof(SetupPacket)) != sizeof(SetupPacket)) {
|
||||
stallTransaction();
|
||||
return;
|
||||
};
|
||||
|
||||
m_request = SetupPacket(m_largeBuffer);
|
||||
uint16_t maxBufferLength = std::min(m_request.wLength(), MaxTransferSize);
|
||||
|
||||
// Forward the request to the request recipient
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::processINpacket() {
|
||||
switch (m_state) {
|
||||
case State::DataIn:
|
||||
sendSomeData();
|
||||
break;
|
||||
case State::LastDataIn:
|
||||
m_state = State::StatusOut;
|
||||
// Prepare to receive the OUT Data[] transaction.
|
||||
setOutNAK(false);
|
||||
break;
|
||||
case State::StatusIn:
|
||||
{
|
||||
m_state = State::Idle;
|
||||
// All the data has been received. Callback the request recipient.
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stallTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::processOUTpacket() {
|
||||
switch (m_state) {
|
||||
case State::DataOut:
|
||||
if (receiveSomeData() < 0) {
|
||||
break;
|
||||
}
|
||||
if ((m_request.wLength() - m_transferBufferLength) <= k_maxPacketSize) {
|
||||
m_state = State::LastDataOut;
|
||||
}
|
||||
break;
|
||||
case State::LastDataOut:
|
||||
if (receiveSomeData() < 0) {
|
||||
break;
|
||||
}
|
||||
// Send the DATA1[] to the host.
|
||||
writePacket(NULL, 0);
|
||||
m_state = State::StatusIn;
|
||||
break;
|
||||
case State::StatusOut:
|
||||
{
|
||||
// Read the DATA1[] sent by the host.
|
||||
readPacket(NULL, 0);
|
||||
m_state = State::Idle;
|
||||
// All the data has been sent. Callback the request recipient.
|
||||
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
|
||||
if (type == 0) {
|
||||
// Device recipient
|
||||
m_requestRecipients[0]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
} else {
|
||||
// Interface recipient
|
||||
m_requestRecipients[1]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
stallTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::flushTxFifo() {
|
||||
// Set IN endpoint NAK
|
||||
OTG.DIEPCTL0()->setSNAK(true);
|
||||
|
||||
// Wait for core to respond
|
||||
while (!OTG.DIEPINT(0)->getINEPNE()) {
|
||||
}
|
||||
|
||||
// Get the Tx FIFO number
|
||||
uint32_t fifo = OTG.DIEPCTL0()->getTXFNUM();
|
||||
|
||||
// Wait for AHB idle
|
||||
while (!OTG.GRSTCTL()->getAHBIDL()) {
|
||||
}
|
||||
|
||||
// Flush Tx FIFO
|
||||
OTG.GRSTCTL()->setTXFNUM(fifo);
|
||||
OTG.GRSTCTL()->setTXFFLSH(true);
|
||||
|
||||
// Reset packet counter
|
||||
OTG.DIEPTSIZ0()->set(0);
|
||||
|
||||
// Wait for the flush
|
||||
while (OTG.GRSTCTL()->getTXFFLSH()) {
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::flushRxFifo() {
|
||||
// Set OUT endpoint NAK
|
||||
OTG.DOEPCTL0()->setSNAK(true);
|
||||
|
||||
// Wait for AHB idle
|
||||
while (!OTG.GRSTCTL()->getAHBIDL()) {
|
||||
}
|
||||
|
||||
// Flush Rx FIFO
|
||||
OTG.GRSTCTL()->setRXFFLSH(true);
|
||||
|
||||
// Reset packet counter
|
||||
OTG.DOEPTSIZ0()->set(0);
|
||||
|
||||
// Wait for the flush
|
||||
while (OTG.GRSTCTL()->getRXFFLSH()) {
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint0::discardUnreadData() {
|
||||
for (int i = 0; i < m_receivedPacketSize; i += 4) {
|
||||
OTG.DFIFO0()->get();
|
||||
}
|
||||
m_receivedPacketSize = 0;
|
||||
}
|
||||
|
||||
void Endpoint0::sendSomeData() {
|
||||
if (k_maxPacketSize < m_transferBufferLength) {
|
||||
// More than one packet needs to be sent
|
||||
writePacket(m_largeBuffer + m_bufferOffset, k_maxPacketSize);
|
||||
m_state = State::DataIn;
|
||||
m_bufferOffset += k_maxPacketSize;
|
||||
m_transferBufferLength -= k_maxPacketSize;
|
||||
return;
|
||||
}
|
||||
// Last data packet sent
|
||||
writePacket(m_largeBuffer + m_bufferOffset, m_transferBufferLength);
|
||||
if (m_zeroLengthPacketNeeded) {
|
||||
m_state = State::DataIn;
|
||||
} else {
|
||||
m_state = State::LastDataIn;
|
||||
}
|
||||
m_bufferOffset = 0;
|
||||
m_zeroLengthPacketNeeded = false;
|
||||
m_transferBufferLength = 0;
|
||||
}
|
||||
|
||||
void Endpoint0::clearForOutTransactions(uint16_t wLength) {
|
||||
m_transferBufferLength = 0;
|
||||
m_state = (wLength > k_maxPacketSize) ? State::DataOut : State::LastDataOut;
|
||||
setOutNAK(false);
|
||||
}
|
||||
|
||||
int Endpoint0::receiveSomeData() {
|
||||
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
|
||||
uint16_t packetSize = std::min(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
|
||||
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);
|
||||
if (sizeOfPacketRead != packetSize) {
|
||||
stallTransaction();
|
||||
return -1;
|
||||
}
|
||||
m_transferBufferLength += packetSize;
|
||||
return packetSize;
|
||||
}
|
||||
|
||||
uint16_t Endpoint0::readPacket(void * buffer, uint16_t length) {
|
||||
uint32_t * buffer32 = (uint32_t *) buffer;
|
||||
uint16_t buffer32Length = std::min(length, m_receivedPacketSize);
|
||||
|
||||
int i;
|
||||
// The RX FIFO is read 4 bytes by 4 bytes
|
||||
for (i = buffer32Length; i >= 4; i -= 4) {
|
||||
*buffer32++ = OTG.DFIFO0()->get();
|
||||
m_receivedPacketSize -= 4;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
/* If there are remaining bytes that should be read, read the next 4 bytes
|
||||
* and copy only the wanted bytes. */
|
||||
uint32_t extraData = OTG.DFIFO0()->get();
|
||||
memcpy(buffer32, &extraData, i);
|
||||
if (m_receivedPacketSize < 4) {
|
||||
m_receivedPacketSize = 0;
|
||||
} else {
|
||||
m_receivedPacketSize -= 4;
|
||||
}
|
||||
}
|
||||
return buffer32Length;
|
||||
}
|
||||
|
||||
uint16_t Endpoint0::writePacket(const void * buffer, uint16_t length) {
|
||||
const uint32_t * buffer32 = (uint32_t *) buffer;
|
||||
|
||||
// Return if there is already a packet waiting to be read in the TX FIFO
|
||||
if (OTG.DIEPTSIZ0()->getPKTCNT()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enable transmission
|
||||
|
||||
class OTG::DIEPTSIZ0 dieptsiz0(0);
|
||||
// Indicate that the Transfer Size is one packet
|
||||
dieptsiz0.setPKTCNT(1);
|
||||
// Indicate the length of the Transfer Size
|
||||
dieptsiz0.setXFRSIZ(length);
|
||||
OTG.DIEPTSIZ0()->set(dieptsiz0);
|
||||
// Enable the endpoint
|
||||
OTG.DIEPCTL0()->setEPENA(true);
|
||||
// Clear the NAK bit
|
||||
OTG.DIEPCTL0()->setCNAK(true);
|
||||
|
||||
// Copy the buffer to the TX FIFO by writing data 32bits by 32 bits.
|
||||
for (int i = length; i > 0; i -= 4) {
|
||||
OTG.DFIFO0()->set(*buffer32++);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void Endpoint0::stallTransaction() {
|
||||
OTG.DIEPCTL0()->setSTALL(true);
|
||||
m_state = State::Idle;
|
||||
}
|
||||
|
||||
void Endpoint0::computeZeroLengthPacketNeeded() {
|
||||
if (m_transferBufferLength
|
||||
&& m_transferBufferLength < m_request.wLength()
|
||||
&& m_transferBufferLength % k_maxPacketSize == 0)
|
||||
{
|
||||
m_zeroLengthPacketNeeded = true;
|
||||
return;
|
||||
}
|
||||
m_zeroLengthPacketNeeded = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
79
ion/src/device/bootloader/usb/stack/endpoint0.h
Normal file
79
ion/src/device/bootloader/usb/stack/endpoint0.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_ENDPOINT0_H
|
||||
#define ION_DEVICE_SHARED_USB_ENDPOINT0_H
|
||||
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class RequestRecipient;
|
||||
|
||||
class Endpoint0 {
|
||||
public:
|
||||
enum class State {
|
||||
Idle,
|
||||
Stalled,
|
||||
DataIn,
|
||||
LastDataIn,
|
||||
StatusIn,
|
||||
DataOut,
|
||||
LastDataOut,
|
||||
StatusOut,
|
||||
};
|
||||
|
||||
constexpr static int k_maxPacketSize = 64;
|
||||
constexpr static uint16_t MaxTransferSize = 2048;
|
||||
|
||||
constexpr Endpoint0(RequestRecipient * device, RequestRecipient * interface) :
|
||||
m_forceNAK(false),
|
||||
m_bufferOffset(0),
|
||||
m_transferBufferLength(0),
|
||||
m_receivedPacketSize(0),
|
||||
m_zeroLengthPacketNeeded(false),
|
||||
m_request(),
|
||||
m_requestRecipients{device, interface},
|
||||
m_state(State::Idle),
|
||||
m_largeBuffer{0}
|
||||
{
|
||||
}
|
||||
void setup();
|
||||
void setupOut();
|
||||
void setOutNAK(bool nak);
|
||||
void enableOut();
|
||||
void reset();
|
||||
bool NAKForced() const { return m_forceNAK; }
|
||||
void readAndDispatchSetupPacket();
|
||||
void processINpacket();
|
||||
void processOUTpacket();
|
||||
void flushTxFifo();
|
||||
void flushRxFifo();
|
||||
void setReceivedPacketSize(uint16_t size) { m_receivedPacketSize = size; }
|
||||
void discardUnreadData();
|
||||
void stallTransaction();
|
||||
void computeZeroLengthPacketNeeded();
|
||||
void setState(State state) { m_state = state; }
|
||||
void sendSomeData(); // Writes the next data packet and updates the state.
|
||||
void clearForOutTransactions(uint16_t wLength);
|
||||
|
||||
private:
|
||||
int receiveSomeData();
|
||||
uint16_t readPacket(void * buffer, uint16_t length);
|
||||
uint16_t writePacket(const void * buffer, uint16_t length);
|
||||
|
||||
bool m_forceNAK;
|
||||
int m_bufferOffset; // When sending large data stored in the buffer, the offset keeps tracks of which data packet should be sent next.
|
||||
uint16_t m_transferBufferLength;
|
||||
uint16_t m_receivedPacketSize;
|
||||
bool m_zeroLengthPacketNeeded;
|
||||
SetupPacket m_request;
|
||||
RequestRecipient * m_requestRecipients[2];
|
||||
State m_state;
|
||||
uint8_t m_largeBuffer[MaxTransferSize];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
66
ion/src/device/bootloader/usb/stack/interface.cpp
Normal file
66
ion/src/device/bootloader/usb/stack/interface.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "interface.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
static inline uint16_t minUint16T(uint16_t x, uint16_t y) { return x < y ? x : y; }
|
||||
|
||||
bool Interface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (request->requestType() != SetupPacket::RequestType::Standard) {
|
||||
return false;
|
||||
}
|
||||
switch (request->bRequest()) {
|
||||
case (int) Request::GetStatus:
|
||||
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetInterface:
|
||||
return setInterface(request, transferBufferLength);
|
||||
case (int) Request::GetInterface:
|
||||
return getInterface(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::ClearFeature:
|
||||
return clearFeature(transferBufferLength);
|
||||
case (int) Request::SetFeature:
|
||||
return setFeature(transferBufferLength);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Interface::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = minUint16T(2, transferBufferMaxLength);
|
||||
for (int i = 0; i<*transferBufferLength; i++) {
|
||||
transferBuffer[i] = 0; // Reserved, must be set to 0
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
*transferBufferLength = minUint16T(1, transferBufferMaxLength);;
|
||||
if (*transferBufferLength > 0) {
|
||||
transferBuffer[0] = getActiveInterfaceAlternative();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::setInterface(SetupPacket * request, uint16_t * transferBufferLength) {
|
||||
// We support one interface only
|
||||
setActiveInterfaceAlternative(request->wValue());
|
||||
// There is one interface alternative only, we no need to set it again.
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::clearFeature(uint16_t * transferBufferLength) {
|
||||
// Not needed for now
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interface::setFeature(uint16_t * transferBufferLength) {
|
||||
// Not needed for now
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
42
ion/src/device/bootloader/usb/stack/interface.h
Normal file
42
ion/src/device/bootloader/usb/stack/interface.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_INTERFACE_H
|
||||
#define ION_DEVICE_SHARED_USB_INTERFACE_H
|
||||
|
||||
#include "endpoint0.h"
|
||||
#include "request_recipient.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class Interface : public RequestRecipient {
|
||||
public:
|
||||
Interface(Endpoint0 * ep0) :
|
||||
RequestRecipient(ep0)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
virtual void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) = 0;
|
||||
virtual uint8_t getActiveInterfaceAlternative() = 0;
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
private:
|
||||
// USB Standard Interface Request Codes
|
||||
enum class Request {
|
||||
GetStatus = 0,
|
||||
ClearFeature = 1,
|
||||
SetFeature = 3,
|
||||
GetInterface = 10,
|
||||
SetInterface = 11,
|
||||
};
|
||||
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool setInterface(SetupPacket * request, uint16_t * transferBufferLength);
|
||||
bool clearFeature(uint16_t * transferBufferLength);
|
||||
bool setFeature(uint16_t * transferBufferLength);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
32
ion/src/device/bootloader/usb/stack/request_recipient.cpp
Normal file
32
ion/src/device/bootloader/usb/stack/request_recipient.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "request_recipient.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
bool RequestRecipient::processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
if (request->followingTransaction() == SetupPacket::TransactionType::InTransaction) {
|
||||
// There is no data stage in this transaction, or the data stage will be in IN direction.
|
||||
if (!processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
}
|
||||
if (*transferBufferLength > 0) {
|
||||
m_ep0->computeZeroLengthPacketNeeded();
|
||||
m_ep0->sendSomeData();
|
||||
} else {
|
||||
m_ep0->sendSomeData();
|
||||
// On seeing a zero length packet, sendSomeData changed endpoint0 state to
|
||||
// LastDataIn, but it should be StatusIn as there was no data stage.
|
||||
m_ep0->setState(Endpoint0::State::StatusIn);
|
||||
}
|
||||
} else {
|
||||
// The following transaction will be an OUT transaction.
|
||||
m_ep0->clearForOutTransactions(request->wLength());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ion/src/device/bootloader/usb/stack/request_recipient.h
Normal file
29
ion/src/device/bootloader/usb/stack/request_recipient.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_REQUEST_RECIPIENT_H
|
||||
#define ION_DEVICE_SHARED_USB_REQUEST_RECIPIENT_H
|
||||
|
||||
#include "endpoint0.h"
|
||||
#include "setup_packet.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class RequestRecipient {
|
||||
public:
|
||||
RequestRecipient(Endpoint0 * ep0):
|
||||
m_ep0(ep0)
|
||||
{
|
||||
}
|
||||
bool processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
virtual void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
|
||||
virtual void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
|
||||
protected:
|
||||
virtual bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) = 0;
|
||||
Endpoint0 * m_ep0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
38
ion/src/device/bootloader/usb/stack/setup_packet.cpp
Normal file
38
ion/src/device/bootloader/usb/stack/setup_packet.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "setup_packet.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
SetupPacket::SetupPacket(void * buffer) {
|
||||
memcpy(this, buffer, sizeof(SetupPacket));
|
||||
}
|
||||
|
||||
SetupPacket::TransactionType SetupPacket::followingTransaction() const {
|
||||
if (m_wLength == 0 || (m_bmRequestType & 0b10000000) != 0) {
|
||||
return TransactionType::InTransaction;
|
||||
} else {
|
||||
return TransactionType::OutTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
SetupPacket::RequestType SetupPacket::requestType() const {
|
||||
return (RequestType) ((m_bmRequestType & 0b01100000) >> 5);
|
||||
}
|
||||
|
||||
SetupPacket::RecipientType SetupPacket::recipientType() const {
|
||||
return (RecipientType) (m_bmRequestType & 0b00001111);
|
||||
}
|
||||
|
||||
int SetupPacket::descriptorIndex() {
|
||||
return m_wValue & 0xFF;
|
||||
}
|
||||
|
||||
int SetupPacket::descriptorType() {
|
||||
return m_wValue >> 8;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
65
ion/src/device/bootloader/usb/stack/setup_packet.h
Normal file
65
ion/src/device/bootloader/usb/stack/setup_packet.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_SETUP_PACKET_H
|
||||
#define ION_DEVICE_SHARED_USB_SETUP_PACKET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class SetupPacket {
|
||||
public:
|
||||
enum class TransactionType {
|
||||
SetupTransaction,
|
||||
InTransaction,
|
||||
OutTransaction
|
||||
};
|
||||
|
||||
enum class RequestType {
|
||||
Standard = 0,
|
||||
Class = 1,
|
||||
Vendor = 2
|
||||
};
|
||||
|
||||
enum class RecipientType {
|
||||
Device = 0,
|
||||
Interface = 1,
|
||||
Endpoint = 2,
|
||||
Other = 3
|
||||
};
|
||||
|
||||
constexpr SetupPacket() :
|
||||
m_bmRequestType(0),
|
||||
m_bRequest(0),
|
||||
m_wValue(0),
|
||||
m_wIndex(0),
|
||||
m_wLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
SetupPacket(void * buffer);
|
||||
TransactionType followingTransaction() const;
|
||||
RequestType requestType() const;
|
||||
RecipientType recipientType() const;
|
||||
int descriptorIndex();
|
||||
int descriptorType();
|
||||
uint8_t bmRequestType() { return m_bmRequestType; }
|
||||
uint8_t bRequest() { return m_bRequest; }
|
||||
uint16_t wValue() { return m_wValue; }
|
||||
uint16_t wIndex() { return m_wIndex; }
|
||||
uint16_t wLength() { return m_wLength; }
|
||||
private:
|
||||
uint8_t m_bmRequestType;
|
||||
uint8_t m_bRequest;
|
||||
uint16_t m_wValue;
|
||||
uint16_t m_wIndex;
|
||||
uint16_t m_wLength;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SetupPacket) == 8, "SetupData must be packed");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
15
ion/src/device/bootloader/usb/stack/streamable.cpp
Normal file
15
ion/src/device/bootloader/usb/stack/streamable.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "streamable.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
uint16_t Streamable::copy(void * target, size_t maxSize) const {
|
||||
Channel c(target, maxSize);
|
||||
push(&c);
|
||||
return maxSize - c.sizeLeft();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
44
ion/src/device/bootloader/usb/stack/streamable.h
Normal file
44
ion/src/device/bootloader/usb/stack/streamable.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STREAMABLE_H
|
||||
#define ION_DEVICE_SHARED_USB_STREAMABLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class Streamable {
|
||||
public:
|
||||
uint16_t copy(void * target, size_t maxSize) const;
|
||||
protected:
|
||||
class Channel {
|
||||
public:
|
||||
Channel(void * pointer, size_t maxSize) :
|
||||
m_pointer(pointer),
|
||||
m_sizeLeft(maxSize)
|
||||
{
|
||||
}
|
||||
template<typename T>
|
||||
void push(T data) {
|
||||
if (m_sizeLeft >= sizeof(T)) {
|
||||
T * typedPointer = static_cast<T *>(m_pointer);
|
||||
*typedPointer++ = data; // Actually push the data
|
||||
m_pointer = static_cast<void *>(typedPointer);
|
||||
m_sizeLeft -= sizeof(T);
|
||||
}
|
||||
}
|
||||
size_t sizeLeft() { return m_sizeLeft; }
|
||||
private:
|
||||
void * m_pointer;
|
||||
size_t m_sizeLeft;
|
||||
};
|
||||
virtual void push(Channel * c) const = 0;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,16 +1,10 @@
|
||||
#include <ion.h>
|
||||
#include <kandinsky.h>
|
||||
|
||||
namespace Flasher {
|
||||
namespace Display {
|
||||
|
||||
void init() {
|
||||
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(0x5e81ac));
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->setOrigin(KDPointZero);
|
||||
ctx->setClippingRect(screen);
|
||||
ctx->drawString("RECOVERY MODE", KDPoint(10, 10), KDFont::LargeFont, KDColorWhite, KDColor::RGB24(0x5e81ac));
|
||||
Ion::Display::pushRectUniform(KDRect(0,0,Ion::Display::Width,Ion::Display::Height), KDColor::RGB24(0xFFFF00));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,55 +1,47 @@
|
||||
#include <ion.h>
|
||||
#include <kandinsky.h>
|
||||
#include "image.h"
|
||||
|
||||
namespace Flasher {
|
||||
namespace Display {
|
||||
namespace Display {
|
||||
|
||||
constexpr static int sNumberOfMessages = 5;
|
||||
constexpr static int sNumberOfMessages = 5;
|
||||
constexpr static int sNumberOfLanguages = 2;
|
||||
|
||||
constexpr static const char * sMessages[sNumberOfMessages] = {
|
||||
"RECOVERY MODE",
|
||||
"Your calculator is waiting",
|
||||
"for Upsilon to be installed.",
|
||||
"Follow the instructions",
|
||||
"on your computer to continue.",
|
||||
};
|
||||
|
||||
void init() {
|
||||
KDRect screen = KDRect(0,0,Ion::Display::Width,Ion::Display::Height);
|
||||
Ion::Display::pushRectUniform(screen, KDColor::RGB24(0x2B2B2B));
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->setOrigin(KDPointZero);
|
||||
ctx->setClippingRect(screen);
|
||||
KDCoordinate margin = 30;
|
||||
KDCoordinate currentHeight = margin;
|
||||
|
||||
/* Title */
|
||||
const char * title = sMessages[0];
|
||||
KDSize titleSize = KDFont::LargeFont->stringSize(title);
|
||||
ctx->drawString(title, KDPoint((Ion::Display::Width-titleSize.width())/2, currentHeight),
|
||||
KDFont::LargeFont, KDColorWhite, KDColor::RGB24(0x2B2B2B));
|
||||
currentHeight = (uint16_t)((Ion::Display::Height*2)/3);
|
||||
|
||||
/* Logo */
|
||||
for (int i = 0; i < IMAGE_WIDTH; ++i) {
|
||||
for (int j = 0; j < IMAGE_HEIGHT; ++j) {
|
||||
ctx->setPixel(KDPoint(i+(uint16_t)((Ion::Display::Width-IMAGE_WIDTH)/2),
|
||||
j+(titleSize.height()+margin+15)),
|
||||
KDColor::RGB16(image[i+(j*IMAGE_WIDTH)]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
const char * message;
|
||||
for (int i = 1; i < sNumberOfMessages; ++i) {
|
||||
message = sMessages[i];
|
||||
KDSize messageSize = KDFont::SmallFont->stringSize(message);
|
||||
ctx->drawString(message, KDPoint((Ion::Display::Width-messageSize.width())/2, currentHeight),
|
||||
KDFont::SmallFont, KDColorWhite, KDColor::RGB24(0x2B2B2B));
|
||||
currentHeight += messageSize.height();
|
||||
}
|
||||
}
|
||||
constexpr static const char * sMessages[sNumberOfLanguages][sNumberOfMessages] = {
|
||||
{"RECOVERY MODE",
|
||||
"Your calculator is waiting",
|
||||
"for a new software.",
|
||||
"Follow the instructions",
|
||||
"on your computer to continue."},
|
||||
{"MODE RECUPERATION",
|
||||
"Votre calculatrice attend",
|
||||
"l'installation d'un nouveau logiciel.",
|
||||
"Suivez les instructions sur",
|
||||
"votre ordinateur pour continuer."}
|
||||
};
|
||||
|
||||
void init() {
|
||||
KDRect screen = KDRect(0,0,Ion::Display::Width,Ion::Display::Height);
|
||||
Ion::Display::pushRectUniform(screen, KDColorWhite);
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
ctx->setOrigin(KDPointZero);
|
||||
ctx->setClippingRect(screen);
|
||||
KDCoordinate margin = 20;
|
||||
KDCoordinate currentHeight = 0;
|
||||
for (int i = 0; i < sNumberOfLanguages; i++) {
|
||||
currentHeight += margin;
|
||||
const char * title = sMessages[i][0];
|
||||
KDSize titleSize = KDFont::LargeFont->stringSize(title);
|
||||
ctx->drawString(title, KDPoint((Ion::Display::Width-titleSize.width())/2, currentHeight), KDFont::LargeFont);
|
||||
currentHeight += 2*titleSize.height();
|
||||
for (int j = 1; j < sNumberOfMessages; j++) {
|
||||
const char * message = sMessages[i][j];
|
||||
KDSize messageSize = KDFont::SmallFont->stringSize(message);
|
||||
ctx->drawString(message, KDPoint((Ion::Display::Width-messageSize.width())/2, currentHeight), KDFont::SmallFont);
|
||||
currentHeight += messageSize.height();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ void ion_main(int argc, const char * const argv[]) {
|
||||
Ion::USB::enable();
|
||||
while (!Ion::USB::isEnumerated()) {
|
||||
}
|
||||
Ion::USB::DFU(false, false, 0);
|
||||
Ion::USB::DFU(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ion.h>
|
||||
#include <boot/isr.h>
|
||||
#include <drivers/board.h>
|
||||
#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 <ion.h>
|
||||
#include <kandinsky.h>
|
||||
#include <regs/config/pwr.h>
|
||||
#include <regs/config/rcc.h>
|
||||
#include <regs/regs.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;
|
||||
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
|
||||
}
|
||||
|
||||
/* In order to ensure that this method is execute from the external flash, we
|
||||
@@ -38,6 +38,7 @@ static void __attribute__((noinline)) external_flash_start() {
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -63,154 +64,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). */
|
||||
@@ -244,7 +97,7 @@ void __attribute__((noinline)) start() {
|
||||
* call the pointed function. */
|
||||
#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0
|
||||
#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS
|
||||
for (cxx_constructor* c = &_init_array_start; c < &_init_array_end; c++) {
|
||||
for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) {
|
||||
(*c)();
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -71,7 +71,7 @@ SECTIONS {
|
||||
/* 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
|
||||
* persistent memory in the first place), but it is a R/W area of memory
|
||||
* 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).
|
||||
*
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
|
||||
board.cpp \
|
||||
cache.cpp \
|
||||
external_flash.cpp \
|
||||
led.cpp \
|
||||
@@ -8,9 +7,21 @@ ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
|
||||
usb.cpp \
|
||||
)
|
||||
|
||||
ifeq ($(filter bootloader, ${MAKECMDGOALS}), bootloader)
|
||||
ion_device_src += $(addprefix bootloader/drivers/, \
|
||||
board.cpp \
|
||||
)
|
||||
ion_device_src += $(addprefix bootloader/boot/, \
|
||||
rt0.cpp \
|
||||
)
|
||||
else
|
||||
ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
|
||||
board.cpp \
|
||||
)
|
||||
ion_device_src += $(addprefix ion/src/device/n0110/boot/, \
|
||||
rt0.cpp \
|
||||
)
|
||||
endif
|
||||
|
||||
ion_device_src += $(addprefix ion/src/device/n0110/, \
|
||||
platform_info.cpp \
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ion.h>
|
||||
#include <boot/isr.h>
|
||||
#include <drivers/board.h>
|
||||
#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 <ion.h>
|
||||
#include <kandinsky.h>
|
||||
#include <regs/config/pwr.h>
|
||||
#include <regs/config/rcc.h>
|
||||
#include <regs/regs.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;
|
||||
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
|
||||
}
|
||||
|
||||
/* In order to ensure that this method is execute from the external flash, we
|
||||
@@ -38,6 +38,7 @@ static void __attribute__((noinline)) external_flash_start() {
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -63,154 +64,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). */
|
||||
@@ -244,7 +97,7 @@ void __attribute__((noinline)) start() {
|
||||
* call the pointed function. */
|
||||
#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0
|
||||
#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS
|
||||
for (cxx_constructor* c = &_init_array_start; c < &_init_array_end; c++) {
|
||||
for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) {
|
||||
(*c)();
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -51,10 +51,8 @@ void initMPU() {
|
||||
// 1.1 Memory barrier
|
||||
Cache::dmb();
|
||||
|
||||
// 1.2 Enable fault exceptions
|
||||
CORTEX.SHCRS()->setMEMFAULTENA(true);
|
||||
CORTEX.SHCRS()->setBUSFAULTENA(true);
|
||||
CORTEX.SHCRS()->setUSGFAULTENA(true);
|
||||
// 1.2 Disable fault exceptions
|
||||
CORTEX.SHCRS()->setMEMFAULTENA(false);
|
||||
|
||||
// 1.3 Disable the MPU and clear the control register
|
||||
MPU.CTRL()->setENABLE(false);
|
||||
@@ -64,7 +62,7 @@ void initMPU() {
|
||||
/* 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-cacheable, non-buffereable and non shareable. */
|
||||
* writeable non-cachable, non-buffereable and non shareable. */
|
||||
int sector = 0;
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000);
|
||||
@@ -106,7 +104,7 @@ void initMPU() {
|
||||
* 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 External Chip as executable and
|
||||
* Quad-SPI region corresponding to the Expternal Chip as executable and
|
||||
* fully accessible (AN4861). */
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
|
||||
@@ -59,7 +59,7 @@ constexpr static double modulationDepth = 0.25; // Must be (0.25% <= md <= 2%)
|
||||
constexpr static uint32_t SSCG_INCSTEP = (32767*modulationDepth*PLL_N)/(1.0*100*5*SSCG_MODPER);
|
||||
static_assert(SSCG_MODPER == 250, "SSCG_MODPER changed");
|
||||
static_assert(SSCG_INCSTEP == 25, "SSCG_INCSTEP changed");
|
||||
static_assert(SSCG_INCSTEP * SSCG_MODPER < 32767, "Wrong values for the Spread spectrum clock generator");
|
||||
static_assert(SSCG_INCSTEP * SSCG_MODPER < 32767, "Wrong values for the Spread spectrun clock generator");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -63,14 +63,10 @@ SECTIONS {
|
||||
. = ALIGN(4);
|
||||
*(.text.start)
|
||||
*(.text.abort)
|
||||
*(.text.uf_abort)
|
||||
*(.text.bf_abort)
|
||||
*(.text.nmi_abort)
|
||||
*(.text.abort_init)
|
||||
*(.text.abort_core)
|
||||
*(.text.abort_sleeping)
|
||||
*(.text.abort_economy)
|
||||
*(.text.abort_screen)
|
||||
*(.text.hard_fault_handler)
|
||||
*(.text.mem_fault_handler)
|
||||
*(.text.usage_fault_handler)
|
||||
*(.text.bus_fault_handler)
|
||||
*(.text.isr_systick)
|
||||
*(.text.__assert)
|
||||
*(.text.memcpy)
|
||||
@@ -97,123 +93,6 @@ SECTIONS {
|
||||
|
||||
/* 'abort' dependencies */
|
||||
*(.text._ZN3Ion6Device5Reset4coreEv)
|
||||
*(.text._ZN3Ion3LED8setColorE7KDColor)
|
||||
*(.text._ZN3Ion3LED11setBlinkingEtf)
|
||||
*(.text._ZN3Ion6Device3LED*)
|
||||
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv)
|
||||
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4BaseEv)
|
||||
*(.text._ZNK3Ion6Device4Regs3*)
|
||||
*(.text.___ZN3Ion6Device4Regs8*)
|
||||
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv)
|
||||
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv*)
|
||||
*(.text._ZN3Ion6Device5Board15initPeripheralsEb)
|
||||
*(.text._ZN3Ion6Device9Backlight4initEv)
|
||||
*(.text._ZN3Ion6Device6Timing4initEv)
|
||||
*(.text._ZN3Ion6Timing6msleepEj)
|
||||
*(.text._ZN3Ion6Device8Keyboard4initEv)
|
||||
*(.text._ZN3Ion6Device7Battery8initGPIOEv)
|
||||
*(.text._ZN3Ion6Device3USB8initGPIOEv)
|
||||
*(.text._ZN3Ion6Device9Backlight8setLevelEh)
|
||||
*(.text._ZN3Ion6Device6Timing19setSysTickFrequencyEi)
|
||||
*(.text._ZN3Ion6Device5Board17setClockFrequencyENS1_9FrequencyE)
|
||||
*(.text._ZN3Ion6Device9Backlight10sendPulsesEi)
|
||||
*(.text._ZN3Ion6Device5Board19shutdownPeripheralsEb)
|
||||
*(.text._ZN3Ion6Device6Timing8shutdownEv)
|
||||
*(.text._ZN3Ion6Device8Keyboard8shutdownEv)
|
||||
*(.text._ZN9KDContext10drawStringEPKc7KDPointPK6KDFont7KDColorS6_i)
|
||||
*(.text._ZNK8TextArea11ContentView12drawStringAtEP9KDContextiiPKci7KDColorS5_S4_S4_S5_*)
|
||||
*(.text._ZL11KDPointZero*)
|
||||
*(.text._ZGVZN12KDIonContext13sharedContextEvE7context)
|
||||
*(.text._ZZN12KDIonContext13sharedContextEvE7context)
|
||||
*(.text._ZN12KDIonContext13sharedContextEv)
|
||||
*(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZNK6KDRect15intersectedWithERKS_)
|
||||
*(.text._ZN7KDColor6RGB888Ehhh)
|
||||
*(.text._ZN3Ion6Device7Display14setDrawingAreaE6KDRectNS1_11OrientationE)
|
||||
*(.text.powf)
|
||||
*(.text._ZN9KDContext16pushOrPullStringEPKc7KDPointPK6KDFont7KDColorS6_ibPi)
|
||||
*(.text._ZNK7KDPoint12translatedByES_)
|
||||
*(.text._ZNK6KDRect12translatedByE7KDPoint)
|
||||
*(.text._Z7toGammai)
|
||||
*(.text._ZN3Ion7Display8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN24KDPostProcessZoomContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN16KDRealIonContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN25KDPostProcessGammaContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN12KDIonContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN26KDPostProcessInvertContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text._ZN20KDPostProcessContext8pullRectE6KDRectP7KDColor)
|
||||
*(.text.sqrtf)
|
||||
*(.text._ZN11UTF8Decoder13nextCodePointEv)
|
||||
*(.text._ZN7KDColor5blendES_S_h)
|
||||
*(.text._ZN9KDContext17blendRectWithMaskE6KDRect7KDColorPKhPS1_)
|
||||
*(.text.scalbnf)
|
||||
*(.text._ZNK6KDRect10intersectsERKS_)
|
||||
*(.text._ZN9KDContext18fillRectWithPixelsE6KDRectPK7KDColorPS1_)
|
||||
*(.text._ZN9KDContext15setClippingRectE6KDRect)
|
||||
*(.text._ZN9KDContext9setOriginE7KDPoint)
|
||||
*(.text._ZN20KDPostProcessContext9setOriginE7KDPoint)
|
||||
*(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN20KDPostProcessContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN12KDIonContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN26KDPostProcessInvertContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN24KDPostProcessZoomContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN25KDPostProcessGammaContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZN16KDRealIonContext8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN3Ion7Display8pushRectE6KDRectPK7KDColor)
|
||||
*(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor)
|
||||
*(.text._ZNK6KDRect7isEmptyEv)
|
||||
*(.text._ZN20KDPostProcessContext15setClippingRectE6KDRect)
|
||||
*(.text._ZNK6KDFont17indexForCodePointE9CodePoint)
|
||||
*(.text._ZNK6KDFont26fetchGrayscaleGlyphAtIndexEhPh)
|
||||
*(.text.LZ4_decompress_safe*)
|
||||
*(.text.LZ4_wildCopy*)
|
||||
*(.text.*DFU*)
|
||||
*(.text.*isEnumerated*)
|
||||
*(.text._ZN3Ion3USB6enableEv)
|
||||
*(.text._ZN3Ion7Battery5levelEv)
|
||||
*(.text._ZN3Ion7Battery7voltageEv)
|
||||
*(.text._ZN3Ion3USB9isPluggedEv)
|
||||
*(.text.*sleepConfiguration*)
|
||||
*(.text.*onOnOffKeyDown*)
|
||||
*(.text.*WakeUp*)
|
||||
*(.text._ZN3Ion6Device9Backlight*)
|
||||
*(.text._ZN3Ion9Backlight*)
|
||||
|
||||
*(.text._ZNK10Statistics5Store6medianEi)
|
||||
*(.text._ZNK10Regression5Store12meanOfColumnEiib)
|
||||
*(.text._ZNK6Shared15DoublePairStore11sumOfColumnEiib)
|
||||
*(.text._ZNK10Statistics5Store13firstQuartileEi)
|
||||
*(.text._ZNK10Regression5Store23squaredValueSumOfColumnEiib)
|
||||
*(.text._ZNK10Regression5Store16varianceOfColumnEiib)
|
||||
*(.text._ZNK10Statistics5Store8maxValueEi)
|
||||
*(.text._ZNK10Regression5Store25standardDeviationOfColumnEiib)
|
||||
*(.text._ZNK10Statistics5Store13thirdQuartileEi)
|
||||
*(.text._ZNK10Statistics5Store8minValueEi)
|
||||
*(.text._ZNK6Shared8Interval18IntervalParameters*)
|
||||
*(.text._ZN6Shared8Interval18IntervalParameters*)
|
||||
*(.text.sqrt)
|
||||
*(.text.log)
|
||||
*(.text._ZN17GlobalPreferences23sharedGlobalPreferencesEv)
|
||||
*(.text._ZNK10Statistics5Store16sumOfOccurrencesEi)
|
||||
*(.text.floor)
|
||||
*(.text.ceil)
|
||||
*(.text._ZNK10Statistics5Store21frequenciesAreIntegerEi)
|
||||
*(.text._ZNK10Statistics5Store34sortedElementAtCumulatedPopulationEidb)
|
||||
*(.text._ZNK10Statistics5Store33sortedElementAtCumulatedFrequencyEidb)
|
||||
*(.text.round)
|
||||
*(.text._ZNK10Statistics5Store8minIndexEPdi*)
|
||||
*(.text.LZ4_decompress_safe*)
|
||||
|
||||
/* 'standby' dependencies '*/
|
||||
*(.text._ZN3Ion6Device5Power20internalFlashStandbyEv)
|
||||
@@ -236,60 +115,9 @@ SECTIONS {
|
||||
. = ALIGN(4);
|
||||
*(.rodata._ZN3Ion6Device13ExternalFlash*)
|
||||
/* 'start' dependencies */
|
||||
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL10ColumnGPIOE*)
|
||||
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL7RowGPIOE*)
|
||||
*(.rodata._ZN3Ion6Device4RegsL5GPIOAE)
|
||||
*(.rodata._ZN3Ion6Device4RegsL5GPIOBE)
|
||||
*(.rodata._ZN3Ion6Device3LED6ConfigL7RGBPinsE)
|
||||
*(.rodata._ZN3Ion6Device5Board4initEv.str1.4)
|
||||
*(.rodata._ZL12KDColorWhite*)
|
||||
*(.rodata._ZL10KDColorRed*)
|
||||
*(.rodata._ZL12KDColorBlack*)
|
||||
*(.rodata._ZN4CodeL15BackgroundColorE*)
|
||||
*(.rodata._ZN3Ion6Device3SWD6ConfigL4PinsE)
|
||||
*(.rodata._ZN3Ion6Device7Display6ConfigL8FSMCPinsE)
|
||||
*(.rodata._ZZN3Ion6Device7Display9initPanelEvE11calibration)
|
||||
*(.rodata._ZN3Ion6Device3USB6ConfigL5DmPinE)
|
||||
*(.rodata._ZN3Ion6Device3USB6ConfigL5DpPinE)
|
||||
*(.rodata._ZN3Ion6Device3USB6ConfigL7VbusPinE)
|
||||
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL10ColumnPinsE)
|
||||
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL7RowPinsE)
|
||||
*(.rodata._ZZN3Ion6Device3USB12shutdownGPIOEvE4Pins)
|
||||
*(.rodata._ZN6KDFont16privateLargeFontE)
|
||||
*(.rodata.abort.str1.1)
|
||||
*(.rodata.uf_abort.str1.1)
|
||||
*(.rodata.bf_abort.str1.1)
|
||||
*(.rodata.nmi_abort.str1.1)
|
||||
*(.rodata.abort_screen.str1.1)
|
||||
*(.rodata._ZL5table*)
|
||||
*(.rodata._ZL15glyphDataOffset*)
|
||||
*(.rodata._ZL11KDPointZero*)
|
||||
*(.rodata._ZL9glyphData*)
|
||||
*(.rodata._ZN4CodeL14HighlightColorE*)
|
||||
*(.rodata._ZTV12KDIonContext)
|
||||
*(.rodata._ZTV16KDRealIonContext)
|
||||
*(.rodata._ZTV24KDPostProcessZoomContext)
|
||||
*(.rodata._ZTV25KDPostProcessGammaContext)
|
||||
*(.rodata._ZTV26KDPostProcessInvertContext)
|
||||
*(.rodata.bp)
|
||||
*(.rodata.dp_h)
|
||||
*(.rodata.dp_l)
|
||||
*(.rodata._ZN11MicroPython5Color5ParseEPvNS0_4ModeE*)
|
||||
*(.rodata._ZN9Clipboard10storedTextEv*)
|
||||
*(.rodata._ZN8Sequence14ListController27toolboxForInputEventHandlerEP17InputEventHandler*)
|
||||
*(.rodata._ZN8Sequence23TypeParameterController25willDisplayCellAtLocationEP13HighlightCellii*)
|
||||
*(.rodata._ZN6KDFont16privateSmallFontE)
|
||||
*(.rodata._ZN4I18nL23CountryPreferencesArrayE)
|
||||
*(.rodata._ZN3Ion6Device3LED6ConfigL7RGBPinsE*)
|
||||
*(.rodata._ZN4I18nL23CountryPreferencesArrayE*)
|
||||
*(.rodata._ZN3Ion6Device3USB6ConfigL7VbusPinE*)
|
||||
*(.rodata.bp*)
|
||||
*(.rodata.dp_l*)
|
||||
*(.rodata.dp_h*)
|
||||
*(.rodata.abort_sleeping.str1.1)
|
||||
*(.rodata.abort_core.str1.1)
|
||||
*(.rodata.dfu_bootloader)
|
||||
*(.rodata)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.exam_mode_buffer ORIGIN(EXTERNAL_FLASH) : {
|
||||
@@ -323,7 +151,7 @@ SECTIONS {
|
||||
/* 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
|
||||
* persistent memory in the first place), but it is a R/W area of memory
|
||||
* 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).
|
||||
*
|
||||
|
||||
@@ -64,7 +64,7 @@ SECTIONS {
|
||||
/* 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
|
||||
* persistent memory in the first place), but it is a R/W area of memory
|
||||
* 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).
|
||||
*
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
include ion/src/device/shared/boot/Makefile
|
||||
ifneq ($(filter bootloader, ${MAKECMDGOALS}), bootloader)
|
||||
include ion/src/device/shared/usb/Makefile
|
||||
else
|
||||
include ion/src/device/bootloader/usb/Makefile
|
||||
endif
|
||||
include ion/src/device/shared/drivers/Makefile
|
||||
|
||||
ion_device_src += $(addprefix ion/src/device/shared/, \
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
ifeq ($(filter bootloader, ${MAKECMDGOALS}), bootloader)
|
||||
ion_device_src += $(addprefix bootloader/boot/, \
|
||||
isr.c \
|
||||
)
|
||||
else
|
||||
ion_device_src += $(addprefix ion/src/device/shared/boot/, \
|
||||
isr.c \
|
||||
)
|
||||
endif
|
||||
|
||||
@@ -18,11 +18,11 @@ ISR InitialisationVector[INITIALISATION_VECTOR_SIZE]
|
||||
= {
|
||||
(ISR)&_stack_start, // Stack start
|
||||
start, // Reset service routine,
|
||||
nmi_abort, // NMI service routine,
|
||||
0, // NMI service routine,
|
||||
abort, // HardFault service routine,
|
||||
0, // MemManage service routine,
|
||||
bf_abort, // BusFault service routine,
|
||||
uf_abort, // UsageFault service routine,
|
||||
0, // BusFault service routine,
|
||||
0, // UsageFault service routine,
|
||||
0, 0, 0, 0, // Reserved
|
||||
0, // SVCall service routine,
|
||||
0, // DebugMonitor service routine,
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void bf_abort();
|
||||
void uf_abort();
|
||||
void nmi_abort();
|
||||
void abort_init();
|
||||
void abort_core(const char *);
|
||||
void abort_screen(const char *);
|
||||
void abort_sleeping();
|
||||
void abort_economy();
|
||||
void start();
|
||||
void abort();
|
||||
void isr_systick();
|
||||
|
||||
@@ -25,6 +25,5 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \
|
||||
swd.cpp \
|
||||
timing.cpp \
|
||||
usb.cpp \
|
||||
usb_desc.cpp \
|
||||
wakeup.cpp \
|
||||
)
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
/* To measure the battery voltage, we're using the internal ADC. The ADC works
|
||||
* by comparing the input voltage to a reference voltage. The only fixed voltage
|
||||
* we have around is 2.8V, so that's the one we're using as a reference. However,
|
||||
* we have around is 2.8V, so that's the one we're using as a refrence. However,
|
||||
* and ADC can only measure voltage that is lower than the reference voltage. So
|
||||
* we need to use a voltage divider before sampling Vbat.
|
||||
* To avoid draining the battery, we're using a high-impedance voltage divider,
|
||||
* To avoid draining the battery, we're using an high-impedence voltage divider,
|
||||
* so we need to be careful when sampling the ADC. See AN2834 for more info. */
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ bool waitForVBlank() {
|
||||
uint64_t startTime = Timing::millis();
|
||||
uint64_t timeout = startTime + timeoutDelta;
|
||||
|
||||
/* If current time is big enough, currentTime + timeout wraps around the
|
||||
/* If current time is big enough, currentTime + timeout wraps aroud the
|
||||
* uint64_t. We need to take this into account when computing the terminating
|
||||
* event.
|
||||
*
|
||||
|
||||
@@ -51,28 +51,28 @@ size_t numberOfBitsAfterLeadingZeroes(int i) {
|
||||
}
|
||||
|
||||
uint8_t * SignificantExamModeAddress() {
|
||||
uint32_t * persistence_start_32 = (uint32_t *)&_exam_mode_buffer_start;
|
||||
uint32_t * persistence_end_32 = (uint32_t *)&_exam_mode_buffer_end;
|
||||
assert((persistence_end_32 - persistence_start_32) % 4 == 0);
|
||||
while (persistence_start_32 < persistence_end_32 && *persistence_start_32 == 0x0) {
|
||||
uint32_t * persitence_start_32 = (uint32_t *)&_exam_mode_buffer_start;
|
||||
uint32_t * persitence_end_32 = (uint32_t *)&_exam_mode_buffer_end;
|
||||
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
|
||||
persistence_start_32++;
|
||||
persitence_start_32++;
|
||||
}
|
||||
uint8_t * persistence_start_8 = (uint8_t *)persistence_start_32;
|
||||
uint8_t * persistence_end_8 = (uint8_t *)persistence_end_32;
|
||||
while (persistence_start_8 < persistence_end_8 && *persistence_start_8 == 0x0) {
|
||||
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
|
||||
persistence_start_8++;
|
||||
persitence_start_8++;
|
||||
}
|
||||
if (persistence_start_8 == persistence_end_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
|
||||
|| (persistence_start_8 + 1 == persistence_end_8 && *persistence_start_8 == 1)) {
|
||||
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
|
||||
assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start) >= 0);
|
||||
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start));
|
||||
return (uint8_t *)&_exam_mode_buffer_start;
|
||||
}
|
||||
|
||||
return persistence_start_8;
|
||||
return persitence_start_8;
|
||||
}
|
||||
|
||||
uint8_t FetchExamMode() {
|
||||
|
||||
@@ -35,6 +35,9 @@ void EraseSector(int i);
|
||||
void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length);
|
||||
void JDECid(uint8_t * manufacturerID, uint8_t * memoryType, uint8_t * capacityType);
|
||||
|
||||
void LockSlotA();
|
||||
void LockSlotB();
|
||||
|
||||
static constexpr uint8_t NumberOfAddressBitsInChip = 23; // 2^23 Bytes = 8 MBytes
|
||||
static constexpr uint32_t FlashAddressSpaceSize = 1 << NumberOfAddressBitsInChip;
|
||||
|
||||
|
||||
@@ -50,6 +50,26 @@ void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
void DisableInternalProtection() {
|
||||
InternalFlash::DisableProtection();
|
||||
}
|
||||
|
||||
void EnableInternalProtection() {
|
||||
InternalFlash::EnableProtection();
|
||||
}
|
||||
|
||||
void SetInternalSectorProtection(int i, bool protect) {
|
||||
InternalFlash::SetSectorProtection(i, protect);
|
||||
}
|
||||
|
||||
void LockSlotA() {
|
||||
ExternalFlash::LockSlotA();
|
||||
}
|
||||
|
||||
void LockSlotB() {
|
||||
ExternalFlash::LockSlotB();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,12 @@ void MassErase();
|
||||
void EraseSector(int i);
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length);
|
||||
|
||||
void DisableInternalProtection();
|
||||
void EnableInternalProtection();
|
||||
void SetInternalSectorProtection(int i, bool protect);
|
||||
void LockSlotA();
|
||||
void LockSlotB();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,87 @@ static void open() {
|
||||
FLASH.CR()->setPSIZE(MemoryAccessWidth);
|
||||
}
|
||||
|
||||
static void open_protection() {
|
||||
if (FLASH.OPTCR()->getLOCK()) {
|
||||
FLASH.OPTKEYR()->set(0x08192A3B);
|
||||
FLASH.OPTKEYR()->set(0x4C5D6E7F);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_protection() {
|
||||
if(!FLASH.OPTCR()->getLOCK()) {
|
||||
FLASH.OPTCR()->setLOCK(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void enable_protection_at(int i) {
|
||||
if (!FLASH.OPTCR()->getLOCK()) {
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
FLASH.OPTCR()->setnWRP0(true);
|
||||
break;
|
||||
case 1:
|
||||
FLASH.OPTCR()->setnWRP1(true);
|
||||
break;
|
||||
case 2:
|
||||
FLASH.OPTCR()->setnWRP2(true);
|
||||
break;
|
||||
case 3:
|
||||
FLASH.OPTCR()->setnWRP3(true);
|
||||
break;
|
||||
case 4:
|
||||
FLASH.OPTCR()->setnWRP4(true);
|
||||
break;
|
||||
case 5:
|
||||
FLASH.OPTCR()->setnWRP5(true);
|
||||
break;
|
||||
case 6:
|
||||
FLASH.OPTCR()->setnWRP6(true);
|
||||
break;
|
||||
case 7:
|
||||
FLASH.OPTCR()->setnWRP7(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_protection_at(int i) {
|
||||
if (!FLASH.OPTCR()->getLOCK()) {
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
FLASH.OPTCR()->setnWRP0(false);
|
||||
break;
|
||||
case 1:
|
||||
FLASH.OPTCR()->setnWRP1(false);
|
||||
break;
|
||||
case 2:
|
||||
FLASH.OPTCR()->setnWRP2(false);
|
||||
break;
|
||||
case 3:
|
||||
FLASH.OPTCR()->setnWRP3(false);
|
||||
break;
|
||||
case 4:
|
||||
FLASH.OPTCR()->setnWRP4(false);
|
||||
break;
|
||||
case 5:
|
||||
FLASH.OPTCR()->setnWRP5(false);
|
||||
break;
|
||||
case 6:
|
||||
FLASH.OPTCR()->setnWRP6(false);
|
||||
break;
|
||||
case 7:
|
||||
FLASH.OPTCR()->setnWRP7(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void close() {
|
||||
// Clear error flags
|
||||
class FLASH::SR sr(0);
|
||||
@@ -247,6 +328,22 @@ void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
close();
|
||||
}
|
||||
|
||||
void EnableProtection() {
|
||||
close_protection();
|
||||
}
|
||||
|
||||
void DisableProtection() {
|
||||
open_protection();
|
||||
}
|
||||
|
||||
void SetSectorProtection(int i, bool protect) {
|
||||
if (protect) {
|
||||
enable_protection_at(i);
|
||||
} else {
|
||||
disable_protection_at(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ void EraseSector(int i);
|
||||
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length);
|
||||
|
||||
void EnableProtection();
|
||||
void DisableProtection();
|
||||
void SetSectorProtection(int i, bool protect);
|
||||
|
||||
/* The Device is powered by a 2.8V LDO. This allows us to perform writes to the
|
||||
* Flash 32 bits at once. */
|
||||
constexpr Regs::FLASH::CR::PSIZE MemoryAccessWidth = Regs::FLASH::CR::PSIZE::X32;
|
||||
|
||||
@@ -49,7 +49,7 @@ void jump(uint32_t jumpIsrVectorAddress) {
|
||||
// Disable cache before reset
|
||||
Ion::Device::Cache::disable();
|
||||
|
||||
/* Shutdown all clocks and peripherals to mimic a hardware reset. */
|
||||
/* Shutdown all clocks and periherals to mimic a hardware reset. */
|
||||
Board::shutdownPeripherals();
|
||||
internalFlashJump(jumpIsrVectorAddress);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Device {
|
||||
namespace WakeUp {
|
||||
|
||||
/* All wakeup functions can be called together without overwriting the same
|
||||
* register. All together, they will set SYSCFG and EXTi registers as follow:
|
||||
* register. All togethed, they will set SYSCFG and EXTi registers as follow:
|
||||
*
|
||||
* GPIO Pin Number|EXTI_EMR|EXTI_FTSR|EXTI_RTSR|EXTICR1|EXTICR2|EXTICR3| Wake up
|
||||
* ---------------+--------+---------+---------+-------+-------+-------+-------------------------
|
||||
|
||||
@@ -22,11 +22,7 @@ MEMORY {
|
||||
* object). Using a stack too small would result in some memory being
|
||||
* overwritten (for instance, vtables that live in the .rodata section). */
|
||||
|
||||
/* The image is quite large too!
|
||||
* So we put the stack to 18K so there's still space
|
||||
* for our image, if not LD will throw an error. */
|
||||
|
||||
STACK_SIZE = 18K;
|
||||
STACK_SIZE = 32K;
|
||||
|
||||
SECTIONS {
|
||||
.isr_vector_table ORIGIN(RAM_BUFFER) : {
|
||||
|
||||
@@ -28,6 +28,9 @@ public:
|
||||
class KEYR : public Register32 {
|
||||
};
|
||||
|
||||
class OPTKEYR : public Register32 {
|
||||
};
|
||||
|
||||
class CR : public Register32 {
|
||||
public:
|
||||
enum class PSIZE : uint8_t {
|
||||
@@ -56,11 +59,26 @@ public:
|
||||
REGS_BOOL_FIELD(EOP, 0);
|
||||
};
|
||||
|
||||
class OPTCR : public Register32 {
|
||||
public:
|
||||
REGS_BOOL_FIELD(nWRP0, 16);
|
||||
REGS_BOOL_FIELD(nWRP1, 17);
|
||||
REGS_BOOL_FIELD(nWRP2, 18);
|
||||
REGS_BOOL_FIELD(nWRP3, 19);
|
||||
REGS_BOOL_FIELD(nWRP4, 20);
|
||||
REGS_BOOL_FIELD(nWRP5, 21);
|
||||
REGS_BOOL_FIELD(nWRP6, 22);
|
||||
REGS_BOOL_FIELD(nWRP7, 23);
|
||||
REGS_BOOL_FIELD(LOCK, 0);
|
||||
};
|
||||
|
||||
constexpr FLASH() {};
|
||||
REGS_REGISTER_AT(ACR, 0x00);
|
||||
REGS_REGISTER_AT(KEYR, 0x04);
|
||||
REGS_REGISTER_AT(OPTKEYR, 0x08);
|
||||
REGS_REGISTER_AT(SR, 0x0C);
|
||||
REGS_REGISTER_AT(CR, 0x10);
|
||||
REGS_REGISTER_AT(OPTCR, 0x14);
|
||||
private:
|
||||
constexpr uint32_t Base() const {
|
||||
return 0x40023C00;
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
};
|
||||
|
||||
class CCMR : Register64 {
|
||||
/* We're declaring CCMR as a 64 bits register. CCMR doesn't exist per se,
|
||||
/* We're declaring CCMR as a 64 bits register. CCMR doesn't exsist per se,
|
||||
* it is in fact the consolidation of CCMR1 and CCMR2. Both are 16 bits
|
||||
* registers, so one could expect the consolidation to be 32 bits. However,
|
||||
* both CCMR1 and CCMR2 live on 32-bits boundaries, so the consolidation has
|
||||
|
||||
@@ -93,3 +93,7 @@ $(BUILD_DIR)/ion/src/device/shared/usb/dfu.o: $(BUILD_DIR)/ion/src/device/shared
|
||||
|
||||
ion_device_src += ion/src/device/shared/usb/dfu.cpp:-usbxip
|
||||
ion_device_src += ion/src/device/shared/usb/dfu_relocated.cpp:-usbxip
|
||||
|
||||
ion_device_src += $(addprefix ion/src/device/shared/drivers/, \
|
||||
usb_desc.cpp \
|
||||
)
|
||||
|
||||
@@ -7,11 +7,11 @@ namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
void Calculator::PollAndReset(bool exitWithKeyboard, bool unlock, int level) {
|
||||
void Calculator::PollAndReset(bool exitWithKeyboard) {
|
||||
char serialNumber[Ion::Device::SerialNumber::Length+1];
|
||||
Ion::Device::SerialNumber::copy(serialNumber);
|
||||
Calculator c(serialNumber);
|
||||
|
||||
|
||||
/* Leave DFU mode if the Back key is pressed, the calculator unplugged or the
|
||||
* USB core soft-disconnected. */
|
||||
Ion::Keyboard::Key exitKey = Ion::Keyboard::Key::Back;
|
||||
@@ -19,10 +19,6 @@ void Calculator::PollAndReset(bool exitWithKeyboard, bool unlock, int level) {
|
||||
uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey);
|
||||
|
||||
Ion::Device::Keyboard::activateRow(exitKeyRow);
|
||||
c.m_dfuInterface.setLevel(level);
|
||||
if (unlock) {
|
||||
c.m_dfuInterface.unlockDfu();
|
||||
}
|
||||
|
||||
while (!(exitWithKeyboard && !c.isErasingAndWriting() && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) &&
|
||||
Ion::USB::isPlugged() &&
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace USB {
|
||||
|
||||
class Calculator : public Device {
|
||||
public:
|
||||
static void PollAndReset(bool exitWithKeyboard, bool unlocked, int level)
|
||||
static void PollAndReset(bool exitWithKeyboard)
|
||||
__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
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
&m_webUSBPlatformDescriptor),
|
||||
m_languageStringDescriptor(),
|
||||
m_manufacturerStringDescriptor("NumWorks"),
|
||||
m_productStringDescriptor("NumWorks Calculator"),
|
||||
m_productStringDescriptor("Upsilon Calculator"),
|
||||
m_serialNumberStringDescriptor(serialNumber),
|
||||
m_interfaceStringDescriptor(stringDescriptor()),
|
||||
//m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"),
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
ExtendedCompatIDDescriptor m_extendedCompatIdDescriptor;
|
||||
|
||||
Descriptor * m_descriptors[8];
|
||||
/* m_descriptors contains only descriptors that should be returned via the
|
||||
/* m_descriptors contains only descriptors that sould 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. */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* DFU transfers can serve two purposes:
|
||||
* - Transferring RAM data between the machine and the host, e.g. Python scripts
|
||||
* - 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
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
|
||||
#include "dfu_interface.h"
|
||||
|
||||
#include <drivers/config/external_flash.h>
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/external_flash.h>
|
||||
#include <drivers/flash.h>
|
||||
#include <drivers/internal_flash.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/storage.h>
|
||||
#include <ion/timing.h>
|
||||
#include <string.h>
|
||||
#include <drivers/flash.h>
|
||||
#include <ion/timing.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
@@ -17,7 +9,7 @@ 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 {
|
||||
void DFUInterface::StatusData::push(Channel * c) const {
|
||||
c->push(m_bStatus);
|
||||
c->push(m_bwPollTimeout[2]);
|
||||
c->push(m_bwPollTimeout[1]);
|
||||
@@ -26,20 +18,20 @@ void DFUInterface::StatusData::push(Channel *c) const {
|
||||
c->push(m_iString);
|
||||
}
|
||||
|
||||
void DFUInterface::StateData::push(Channel *c) const {
|
||||
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) {
|
||||
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:
|
||||
case (uint8_t) DFUDownloadCommand::SetAddressPointer:
|
||||
setAddressPointerCommand(request, transferBuffer, *transferBufferLength);
|
||||
return;
|
||||
case (uint8_t)DFUDownloadCommand::Erase:
|
||||
case (uint8_t) DFUDownloadCommand::Erase:
|
||||
eraseCommand(transferBuffer, *transferBufferLength);
|
||||
return;
|
||||
default:
|
||||
@@ -63,17 +55,17 @@ void DFUInterface::wholeDataReceivedCallback(SetupPacket *request, uint8_t *tran
|
||||
}
|
||||
}
|
||||
|
||||
void DFUInterface::wholeDataSentCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) {
|
||||
if (request->bRequest() == (uint8_t)DFURequest::GetStatus) {
|
||||
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. */
|
||||
* "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();
|
||||
@@ -89,34 +81,31 @@ void DFUInterface::wholeDataSentCallback(SetupPacket *request, uint8_t *transfer
|
||||
}
|
||||
}
|
||||
|
||||
bool DFUInterface::processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
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:
|
||||
case (uint8_t) DFURequest::Detach:
|
||||
m_device->detach();
|
||||
return true;
|
||||
case (uint8_t)DFURequest::Download:
|
||||
case (uint8_t) DFURequest::Download:
|
||||
return processDownloadRequest(request->wLength(), transferBufferLength);
|
||||
case (uint8_t)DFURequest::Upload:
|
||||
case (uint8_t) DFURequest::Upload:
|
||||
return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t)DFURequest::GetStatus:
|
||||
case (uint8_t) DFURequest::GetStatus:
|
||||
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t)DFURequest::ClearStatus:
|
||||
case (uint8_t) DFURequest::ClearStatus:
|
||||
return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t)DFURequest::GetState:
|
||||
case (uint8_t) DFURequest::GetState:
|
||||
return getState(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (uint8_t)DFURequest::Abort:
|
||||
case (uint8_t) DFURequest::Abort:
|
||||
return dfuAbort(transferBufferLength);
|
||||
case (uint8_t)DFURequest::Unlock:
|
||||
m_dfuUnlocked = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t *transferBufferLength) {
|
||||
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;
|
||||
@@ -134,8 +123,8 @@ bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t *transferBu
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -145,7 +134,7 @@ bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferB
|
||||
* the command codes for :
|
||||
* Get command / Set Address Pointer / Erase / Read Unprotect
|
||||
* We no not need it for now. */
|
||||
return false;
|
||||
return false;
|
||||
} else if (request->wValue() == 1) {
|
||||
m_ep0->stallTransaction();
|
||||
return false;
|
||||
@@ -164,7 +153,7 @@ bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferB
|
||||
return true;
|
||||
}
|
||||
|
||||
void DFUInterface::setAddressPointerCommand(SetupPacket *request, uint8_t *transferBuffer, uint16_t transferBufferLength) {
|
||||
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]
|
||||
@@ -186,7 +175,7 @@ void DFUInterface::changeAddressPointerIfNeeded() {
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength) {
|
||||
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;
|
||||
@@ -200,12 +189,12 @@ void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBuffer
|
||||
// Sector erase
|
||||
assert(transferBufferLength == 5);
|
||||
|
||||
m_eraseAddress = transferBuffer[1]
|
||||
uint32_t eraseAddress = transferBuffer[1]
|
||||
+ (transferBuffer[2] << 8)
|
||||
+ (transferBuffer[3] << 16)
|
||||
+ (transferBuffer[4] << 24);
|
||||
|
||||
m_erasePage = Flash::SectorAtAddress(m_eraseAddress);
|
||||
m_erasePage = Flash::SectorAtAddress(eraseAddress);
|
||||
if (m_erasePage < 0) {
|
||||
// Unrecognized sector
|
||||
m_state = State::dfuERROR;
|
||||
@@ -213,26 +202,25 @@ void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBuffer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DFUInterface::eraseMemoryIfNeeded() {
|
||||
if (m_erasePage < 0) {
|
||||
// There was no erase waiting.
|
||||
return;
|
||||
}
|
||||
|
||||
willErase();
|
||||
|
||||
#if 0 // We don't erase now the flash memory to avoid crash if writing is refused
|
||||
if (m_erasePage == Flash::TotalNumberOfSectors()) {
|
||||
Flash::MassErase();
|
||||
} else {
|
||||
Flash::EraseSector(m_erasePage);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((m_eraseAddress >= k_ExternalBorderAddress && m_eraseAddress < ExternalFlash::Config::EndAddress) || m_dfuUnlocked) {
|
||||
int32_t order = Flash::SectorAtAddress(m_eraseAddress);
|
||||
Flash::EraseSector(order);
|
||||
}
|
||||
/* 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;
|
||||
m_erasePage = -1;
|
||||
}
|
||||
|
||||
void DFUInterface::writeOnMemory() {
|
||||
@@ -240,90 +228,7 @@ void DFUInterface::writeOnMemory() {
|
||||
// Write on SRAM
|
||||
// FIXME We should check that we are not overriding the current instructions.
|
||||
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
|
||||
resetFlashParameters(); // We are writing in SRAM, so we can reset flash parameters
|
||||
} else if (Flash::SectorAtAddress(m_writeAddress) >= 0) {
|
||||
if (m_dfuLevel == 2) { // We don't accept update
|
||||
m_largeBufferLength = 0;
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errWRITE;
|
||||
return;
|
||||
}
|
||||
|
||||
int currentMemoryType; // Detection of the current memory type (Internal or External)
|
||||
|
||||
if (m_writeAddress >= InternalFlash::Config::StartAddress && m_writeAddress <= InternalFlash::Config::EndAddress) {
|
||||
// We are writing in Internal where live the internal recovery (it's the most sensitive memory type)
|
||||
if (m_isInternalLocked && !m_dfuUnlocked) {
|
||||
// We have to check if external was written in order to
|
||||
// prevent recovery mode loop or the necessity to activate STM bootloader (which is like a superuser mode)
|
||||
// Nevertheless, unlike NumWorks, we don't forbid its access.
|
||||
m_largeBufferLength = 0;
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errTARGET;
|
||||
leaveDFUAndReset(false);
|
||||
return;
|
||||
}
|
||||
|
||||
currentMemoryType = 0;
|
||||
|
||||
// If the protection is activated,
|
||||
// we check the internal magic code in order to prevent the NumWorks' Bootloader flash
|
||||
|
||||
if (m_isFirstInternalPacket && !m_dfuUnlocked) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (k_omegaMagic[i] != m_largeBuffer[k_internalMagicAddress + i]) {
|
||||
m_largeBufferLength = 0;
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errVERIFY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We only check the first packet because there is some predictable data in there
|
||||
m_isFirstInternalPacket = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
currentMemoryType = 1;
|
||||
// We are writing in the external part where live the users apps. It's not a sensitive memory,
|
||||
// but we check it in Upsilon Mode to ensure compatibility between the internal and the external.
|
||||
if (m_writeAddress < k_ExternalBorderAddress && m_isFirstExternalPacket && m_dfuLevel == 0 &&
|
||||
!m_dfuUnlocked) {
|
||||
// We skip any data verification if the user is writing in the Optionals Applications part in the
|
||||
// external (Externals Apps)
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (k_externalUpsilonMagic[i] != m_largeBuffer[k_externalMagicAddress + i]) {
|
||||
m_largeBufferLength = 0;
|
||||
leaveDFUAndReset(false);
|
||||
return;
|
||||
}
|
||||
m_largeBuffer[k_externalMagicAddress + i] = 0;
|
||||
}
|
||||
}
|
||||
// We only check the first packet because there is some predictable data in there,
|
||||
// and we unlock the internal memory
|
||||
m_isFirstExternalPacket = false;
|
||||
m_isInternalLocked = false;
|
||||
}
|
||||
|
||||
// We check if we changed the memory type where we are writing from last time.
|
||||
if (m_lastMemoryType >= 0 && currentMemoryType != m_lastMemoryType) {
|
||||
m_lastMemoryType = -1;
|
||||
}
|
||||
|
||||
m_erasePage = Flash::SectorAtAddress(m_writeAddress);
|
||||
|
||||
// We check if the Sector where we are writing was not already erased and if not, we erase it.
|
||||
if ((m_lastMemoryType < 0 || m_erasePage != m_lastPageErased) &&
|
||||
m_writeAddress < k_ExternalBorderAddress && !m_dfuUnlocked) {
|
||||
Flash::EraseSector(m_erasePage);
|
||||
m_lastMemoryType = currentMemoryType;
|
||||
}
|
||||
|
||||
m_lastPageErased = m_erasePage;
|
||||
m_erasePage = -1;
|
||||
|
||||
// We wait a little before writing in order to prevent some memory error.
|
||||
Ion::Timing::msleep(1);
|
||||
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
|
||||
} else {
|
||||
// Invalid write address
|
||||
@@ -332,6 +237,7 @@ void DFUInterface::writeOnMemory() {
|
||||
m_status = Status::errTARGET;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the buffer length
|
||||
m_largeBufferLength = 0;
|
||||
// Change the interface state and status
|
||||
@@ -339,7 +245,8 @@ void DFUInterface::writeOnMemory() {
|
||||
m_status = Status::OK;
|
||||
}
|
||||
|
||||
bool DFUInterface::getStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
|
||||
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;
|
||||
@@ -351,30 +258,26 @@ bool DFUInterface::getStatus(SetupPacket *request, uint8_t *transferBuffer, uint
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DFUInterface::clearStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) {
|
||||
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) {
|
||||
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) {
|
||||
bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) {
|
||||
m_status = Status::OK;
|
||||
m_state = State::dfuIDLE;
|
||||
*transferBufferLength = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DFUInterface::leaveDFUAndReset(bool do_reset) {
|
||||
resetFlashParameters();
|
||||
m_isInternalLocked = true;
|
||||
m_isFirstInternalPacket = true;
|
||||
m_isFirstExternalPacket = true;
|
||||
m_device->setResetOnDisconnect(do_reset);
|
||||
void DFUInterface::leaveDFUAndReset() {
|
||||
m_device->setResetOnDisconnect(true);
|
||||
m_device->detach();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,20 +8,15 @@
|
||||
#include "stack/endpoint0.h"
|
||||
#include "stack/setup_packet.h"
|
||||
#include "stack/streamable.h"
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/config/external_flash.h>
|
||||
|
||||
namespace Ion
|
||||
{
|
||||
namespace Device
|
||||
{
|
||||
namespace USB
|
||||
{
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
class DFUInterface : public Interface {
|
||||
|
||||
public:
|
||||
DFUInterface(Device *device, Endpoint0 *ep0, uint8_t bInterfaceAlternateSetting) :
|
||||
DFUInterface(Device * device, Endpoint0 * ep0, uint8_t bInterfaceAlternateSetting) :
|
||||
Interface(ep0),
|
||||
m_device(device),
|
||||
m_status(Status::OK),
|
||||
@@ -32,25 +27,14 @@ public:
|
||||
m_largeBuffer{0},
|
||||
m_largeBufferLength(0),
|
||||
m_writeAddress(0),
|
||||
m_eraseAddress(0),
|
||||
m_bInterfaceAlternateSetting(bInterfaceAlternateSetting),
|
||||
m_isErasingAndWriting(false),
|
||||
m_isFirstInternalPacket(true),
|
||||
m_isInternalLocked(true),
|
||||
m_isFirstExternalPacket(true),
|
||||
m_lastMemoryType(-1),
|
||||
m_lastPageErased(-1),
|
||||
m_dfuUnlocked(false),
|
||||
m_dfuLevel(0) {
|
||||
m_isErasingAndWriting(false)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
|
||||
void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
|
||||
bool isErasingAndWriting() const { return m_isErasingAndWriting; }
|
||||
void unlockDfu() { m_dfuUnlocked = true; };
|
||||
void setLevel(uint8_t lvl) { m_dfuLevel = lvl; }
|
||||
|
||||
protected:
|
||||
void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override {
|
||||
@@ -59,7 +43,7 @@ protected:
|
||||
uint8_t getActiveInterfaceAlternative() override {
|
||||
return m_bInterfaceAlternateSetting;
|
||||
}
|
||||
bool processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
|
||||
|
||||
private:
|
||||
// DFU Request Codes
|
||||
@@ -70,11 +54,10 @@ private:
|
||||
GetStatus = 3,
|
||||
ClearStatus = 4,
|
||||
GetState = 5,
|
||||
Abort = 6,
|
||||
Unlock = 11
|
||||
Abort = 6
|
||||
};
|
||||
|
||||
// DFU Download Command Codes
|
||||
// DFU Download Commmand Codes
|
||||
enum class DFUDownloadCommand {
|
||||
GetCommand = 0x00,
|
||||
SetAddressPointer = 0x21,
|
||||
@@ -102,97 +85,82 @@ private:
|
||||
};
|
||||
|
||||
enum class State : uint8_t {
|
||||
appIDLE = 0,
|
||||
appDETACH = 1,
|
||||
dfuIDLE = 2,
|
||||
dfuDNLOADSYNC = 3,
|
||||
dfuDNBUSY = 4,
|
||||
dfuDNLOADIDLE = 5,
|
||||
dfuMANIFESTSYNC = 6,
|
||||
dfuMANIFEST = 7,
|
||||
appIDLE = 0,
|
||||
appDETACH = 1,
|
||||
dfuIDLE = 2,
|
||||
dfuDNLOADSYNC = 3,
|
||||
dfuDNBUSY = 4,
|
||||
dfuDNLOADIDLE = 5,
|
||||
dfuMANIFESTSYNC = 6,
|
||||
dfuMANIFEST = 7,
|
||||
dfuMANIFESTWAITRESET = 8,
|
||||
dfuUPLOADIDLE = 9,
|
||||
dfuERROR = 10
|
||||
dfuUPLOADIDLE = 9,
|
||||
dfuERROR = 10
|
||||
};
|
||||
|
||||
class StatusData : public Streamable {
|
||||
public:
|
||||
StatusData(Status status, State state, uint32_t pollTimeout = 10) :
|
||||
/* We put a default pollTimeout value of 1ms: if the device is busy, the
|
||||
* host has to wait 1ms before sending a getStatus Request. */
|
||||
m_bStatus((uint8_t) status),
|
||||
m_bwPollTimeout{uint8_t((pollTimeout >> 16) & 0xFF), uint8_t((pollTimeout >> 8) & 0xFF),uint8_t(pollTimeout & 0xFF)},
|
||||
m_bState((uint8_t) state),
|
||||
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;
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
uint8_t m_bStatus; // Status resulting from the execution of the most recent request
|
||||
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_bState; // State of the device immediately following transmission of this response
|
||||
uint8_t m_iString;
|
||||
};
|
||||
|
||||
class StateData : public Streamable
|
||||
{
|
||||
class StateData : public Streamable {
|
||||
public:
|
||||
StateData(State state) : m_bState((uint8_t) state) {}
|
||||
StateData(State state) : m_bState((uint8_t)state) {}
|
||||
protected:
|
||||
void push(Channel *c) const override;
|
||||
void push(Channel * c) const override;
|
||||
private:
|
||||
uint8_t m_bState; // Current state of the device
|
||||
};
|
||||
|
||||
/* The Flash and SRAM addresses are in flash.ld. However, dfu_interface is
|
||||
* linked with dfu.ld, so we cannot access the values. */
|
||||
constexpr static uint32_t k_sramStartAddress = 0x20000000;
|
||||
constexpr static uint32_t k_sramEndAddress = 0x20040000;
|
||||
constexpr static uint32_t k_ExternalBorderAddress = 0x90200000;
|
||||
|
||||
const static int k_internalMagicAddress = 0x1C4;
|
||||
constexpr static int k_externalMagicAddress = 0x44f;
|
||||
constexpr static uint8_t k_omegaMagic[4] = {0xF0, 0x0D, 0xC0, 0xDE};
|
||||
// TODO maybe do: add seperated upsilon magic (k_upsilonMagic)
|
||||
constexpr static uint8_t k_externalUpsilonMagic[4] = {0x32, 0x30, 0x30, 0x36};
|
||||
* 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);
|
||||
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 setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength);
|
||||
void changeAddressPointerIfNeeded();
|
||||
// Access memory
|
||||
void eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength);
|
||||
void eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength);
|
||||
void eraseMemoryIfNeeded();
|
||||
void eraseMemoryIfNeededWithoutErasingAtAll();
|
||||
void writeOnMemory();
|
||||
void unlockFlashMemory();
|
||||
void lockFlashMemoryAndPurgeCaches();
|
||||
// Status
|
||||
bool getStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
bool clearStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength);
|
||||
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);
|
||||
bool getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize);
|
||||
// Abort
|
||||
bool dfuAbort(uint16_t *transferBufferLength);
|
||||
bool dfuAbort(uint16_t * transferBufferLength);
|
||||
// Leave DFU
|
||||
void leaveDFUAndReset(bool do_reset = true);
|
||||
|
||||
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 resetFlashParameters() {
|
||||
m_lastMemoryType = -1;
|
||||
m_lastPageErased = -1;
|
||||
}
|
||||
|
||||
Device *m_device;
|
||||
Device * m_device;
|
||||
Status m_status;
|
||||
State m_state;
|
||||
uint32_t m_addressPointer;
|
||||
@@ -201,16 +169,8 @@ private:
|
||||
uint8_t m_largeBuffer[Endpoint0::MaxTransferSize];
|
||||
uint16_t m_largeBufferLength;
|
||||
uint32_t m_writeAddress;
|
||||
uint32_t m_eraseAddress;
|
||||
uint8_t m_bInterfaceAlternateSetting;
|
||||
bool m_isErasingAndWriting;
|
||||
bool m_isFirstInternalPacket;
|
||||
bool m_isInternalLocked;
|
||||
bool m_isFirstExternalPacket;
|
||||
uint8_t m_lastMemoryType; // -1: None; 0: internal; 1: external
|
||||
uint8_t m_lastPageErased; // -1 default value
|
||||
bool m_dfuUnlocked;
|
||||
uint8_t m_dfuLevel; // 0: Upsilon only, 1: Omega-forked only, 2: No update
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ extern char _dfu_bootloader_flash_end;
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
typedef void (*PollFunctionPointer)(bool exitWithKeyboard, bool unlocked, int level);
|
||||
typedef void (*PollFunctionPointer)(bool exitWithKeyboard, void * data);
|
||||
|
||||
void DFU(bool exitWithKeyboard, bool unlocked, int level) {
|
||||
void DFU(bool exitWithKeyboard, void * data) {
|
||||
Ion::updateSlotInfo();
|
||||
|
||||
/* DFU transfers can serve two purposes:
|
||||
@@ -59,7 +59,7 @@ void DFU(bool exitWithKeyboard, bool unlocked, int level) {
|
||||
|
||||
/* 4- Disable all interrupts
|
||||
* The interrupt service routines live in the Flash and could be overwritten
|
||||
* by garbage during a firmware upgrade operation, so we disable them. */
|
||||
* 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
|
||||
@@ -76,7 +76,7 @@ void DFU(bool exitWithKeyboard, bool unlocked, int level) {
|
||||
* add-symbol-file ion/src/device/usb/dfu.elf 0x20038000
|
||||
*/
|
||||
|
||||
dfu_bootloader_entry(exitWithKeyboard, unlocked, level);
|
||||
dfu_bootloader_entry(exitWithKeyboard, data);
|
||||
|
||||
/* 5- Restore interrupts */
|
||||
Device::Timing::init();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
namespace Ion {
|
||||
namespace USB {
|
||||
|
||||
void DFU(bool exitWithKeyboard, bool unlocked, int level) {
|
||||
void DFU(bool exitWithKeyboard, void * data) {
|
||||
Ion::updateSlotInfo();
|
||||
Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, unlocked, level);
|
||||
Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
|
||||
#include "descriptor.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_DESCRIPTOR_H
|
||||
#ifndef ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABLITY_DESCRIPTOR_H
|
||||
|
||||
#include "device_capability_descriptor.h"
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ bool Device::processSetupInRequest(SetupPacket * request, uint8_t * transferBuff
|
||||
case (int) Request::GetStatus:
|
||||
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
|
||||
case (int) Request::SetAddress:
|
||||
// Make sure the request is address is valid.
|
||||
// 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.
|
||||
|
||||
Reference in New Issue
Block a user