firmware for my Touchscreen E-Paper Input Module for Framework Laptop 16
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

eepy-launcher: add serial functionality

+364 -216
+4
eepy-example-app/src/main.rs
··· 32 32 33 33 let mut draw_target = EpdDrawTarget::new(); 34 34 35 + let text = Text::new("Example App", Point::new(10, 20), DEFAULT_TEXT_STYLE); 35 36 let mut button = Button::with_default_style_auto_sized(Point::new(10, 40), "Click me", true); 36 37 let mut exit_button = Button::with_default_style_auto_sized(Point::new(10, 386), "Exit", false); 38 + 39 + text.draw(&mut draw_target).unwrap(); 37 40 button.draw_init(&mut draw_target); 38 41 exit_button.draw_init(&mut draw_target); 39 42 draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); ··· 51 54 let response = button.tick(&mut draw_target, ev); 52 55 if response.clicked { 53 56 draw_target.clear(BinaryColor::Off).unwrap(); 57 + text.draw(&mut draw_target).unwrap(); 54 58 button.draw_init(&mut draw_target); 55 59 exit_button.draw_init(&mut draw_target); 56 60
+3
eepy-launcher/Cargo.toml
··· 6 6 [dependencies] 7 7 eepy-sys = { path = "../eepy-sys", features = ["critical-section-impl"] } 8 8 eepy-gui = { path = "../eepy-gui" } 9 + eepy-serial = { path = "../eepy-serial" } 9 10 embedded-graphics.workspace = true 10 11 usb-device.workspace = true 11 12 usbd-serial.workspace = true 13 + portable-atomic.workspace = true 14 + heapless.workspace = true 12 15 panic-halt = "1.0"
+26 -32
eepy-launcher/src/main.rs
··· 1 1 #![no_std] 2 2 #![no_main] 3 3 4 + mod serial; 5 + 4 6 extern crate panic_halt; 5 7 6 8 use core::arch::asm; 9 + use core::sync::atomic::Ordering; 7 10 use embedded_graphics::geometry::AnchorPoint; 8 11 use embedded_graphics::pixelcolor::BinaryColor; 9 12 use embedded_graphics::prelude::*; ··· 17 20 use eepy_sys::image::RefreshBlockMode; 18 21 use eepy_sys::input::{has_event, next_event, set_touch_enabled, Event, TouchEventType}; 19 22 use eepy_sys::header::{ProgramSlotHeader, Programs}; 20 - use eepy_sys::misc::{get_serial, info, trace}; 23 + use eepy_sys::misc::{get_serial}; 21 24 use eepy_sys::usb; 22 25 use eepy_sys::usb::UsbBus; 23 26 use usb_device::prelude::*; 24 27 use usbd_serial::SerialPort; 28 + use crate::serial::NEEDS_REFRESH_PROGRAMS; 25 29 26 30 #[link_section = ".header"] 27 31 #[used] ··· 31 35 entry, 32 36 ); 33 37 38 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 34 39 enum Page { 35 40 MainPage, 36 41 ScratchpadPage, ··· 42 47 } 43 48 44 49 impl MainPage { 45 - fn new() -> Self { 46 - let mut buttons = [const { None }; 32]; 50 + fn refresh_buttons(&mut self) { 47 51 let mut programs = Programs::new(); 48 52 49 53 for y in 0..16 { ··· 58 62 false, 59 63 ); 60 64 let slot_num = unsafe { (&*prog).slot() }; 61 - buttons[bi] = Some((button, slot_num)) 65 + self.app_buttons[bi] = Some((button, slot_num)) 62 66 } 63 67 } 64 68 } 69 + } 65 70 66 - Self { 71 + fn new() -> Self { 72 + let mut res = Self { 67 73 scratchpad_button: Button::with_default_style_auto_sized(Point::new(10, 10), "Scratchpad", true), 68 - app_buttons: buttons, 69 - } 74 + app_buttons: [const { None }; 32], 75 + }; 76 + res.refresh_buttons(); 77 + res 70 78 } 71 79 } 72 80 ··· 97 105 let response = button.tick(draw_target, ev); 98 106 99 107 if response.clicked { 100 - cleanup_usb(); 108 + unsafe { cleanup_usb() }; 101 109 exec(*s); 102 110 } 103 111 ··· 290 298 static mut USB_DEVICE: Option<UsbDevice<UsbBus>> = None; 291 299 static mut USB_SERIAL: Option<SerialPort<UsbBus>> = None; 292 300 293 - #[allow(static_mut_refs)] 294 - pub extern "C" fn testing_usb_handler() { 295 - let dev: &mut UsbDevice<UsbBus> = unsafe { USB_DEVICE.as_mut().unwrap() }; 296 - let serial: &mut SerialPort<UsbBus> = unsafe { USB_SERIAL.as_mut().unwrap() }; 297 - 298 - trace("Launcher USB handler"); 299 - 300 - if dev.poll(&mut [serial]) { 301 - let mut buf = [0u8; 64]; 302 - match serial.read(&mut buf) { 303 - Err(_) => {}, 304 - Ok(0) => {}, 305 - Ok(_) => { 306 - let s = core::str::from_utf8(&buf); 307 - if let Ok(s) = s { 308 - info(s); 309 - } 310 - } 311 - } 312 - } 313 - } 314 - 315 - fn cleanup_usb() { 301 + unsafe fn cleanup_usb() { 316 302 #[allow(static_mut_refs)] 317 303 unsafe { 318 304 let _ = USB.take(); ··· 344 330 USB_DEVICE = Some(usb_dev); 345 331 } 346 332 347 - usb::set_handler(testing_usb_handler); 333 + usb::set_handler(serial::usb_handler); 348 334 349 335 let mut draw_target = EpdDrawTarget::new(); 350 336 set_touch_enabled(true); ··· 361 347 // has_event() is a syscall. The SVCall exception is a WFE wakeup event, so we need two 362 348 // WFEs so we don't immediately wake up. 363 349 unsafe { asm!("wfe", "wfe") }; 350 + } 351 + 352 + if NEEDS_REFRESH_PROGRAMS.swap(false, Ordering::Relaxed) { 353 + gui.main_page.refresh_buttons(); 354 + if gui.current_page == Page::MainPage { 355 + gui.draw_init(&mut draw_target); 356 + draw_target.refresh(false, RefreshBlockMode::BlockAcknowledge); 357 + } 364 358 } 365 359 } 366 360 }
+153
eepy-launcher/src/serial.rs
··· 1 + use core::sync::atomic::Ordering; 2 + use core::fmt::Write; 3 + use portable_atomic::AtomicBool; 4 + use usb_device::device::UsbDevice; 5 + use usbd_serial::SerialPort; 6 + use eepy_serial::{Response, SerialCommand}; 7 + use eepy_sys::flash::{erase_and_program, invalidate_cache}; 8 + use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; 9 + use eepy_sys::IMAGE_BYTES; 10 + use eepy_sys::misc::{debug, trace}; 11 + use eepy_sys::usb::UsbBus; 12 + use crate::{USB_DEVICE, USB_SERIAL}; 13 + 14 + #[derive(Copy, Clone, Debug)] 15 + enum SerialState { 16 + ReadyForCommand, 17 + 18 + ReceivingImage { 19 + fast_refresh: bool, 20 + index: usize, 21 + }, 22 + 23 + FlashingProgram { 24 + index: usize, 25 + page: usize, 26 + num_pages: Option<usize>, 27 + remainder: Option<usize>, 28 + }, 29 + } 30 + 31 + unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) { 32 + erase_and_program((slot as u32) * 512 * 1024 + (page as u32) * 4096, buf); 33 + } 34 + 35 + fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) { 36 + while !buf.is_empty() { 37 + let _ = serial.write(buf).map(|len| buf = &buf[len..]); 38 + } 39 + } 40 + 41 + pub(crate) static NEEDS_REFRESH_PROGRAMS: AtomicBool = AtomicBool::new(false); 42 + 43 + pub(crate) extern "C" fn usb_handler() { 44 + trace("USB handler"); 45 + 46 + static mut STATE: SerialState = SerialState::ReadyForCommand; 47 + #[allow(static_mut_refs)] 48 + let state = unsafe { &mut STATE }; 49 + 50 + #[allow(static_mut_refs)] 51 + let dev: &mut UsbDevice<UsbBus> = unsafe { USB_DEVICE.as_mut().unwrap() }; 52 + #[allow(static_mut_refs)] 53 + let serial: &mut SerialPort<UsbBus> = unsafe { USB_SERIAL.as_mut().unwrap() }; 54 + 55 + // Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for 56 + // receiving flash applications. 57 + static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES]; 58 + #[allow(static_mut_refs)] 59 + let buf = unsafe { &mut BUF }; 60 + 61 + if dev.poll(&mut [serial]) { 62 + match state { 63 + SerialState::ReadyForCommand => { 64 + let mut cmd_buf = [0u8]; 65 + if let Ok(count) = serial.read(&mut cmd_buf) { 66 + if count == 0 { 67 + return; 68 + } 69 + 70 + match SerialCommand::try_from(cmd_buf[0]) { 71 + Ok(SerialCommand::RefreshNormal) => *state = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 72 + Ok(SerialCommand::RefreshFast) => *state = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 73 + Ok(SerialCommand::UploadProgram) => *state = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None }, 74 + Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]), 75 + Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 76 + } 77 + } 78 + } 79 + 80 + SerialState::ReceivingImage { fast_refresh, index } => { 81 + if let Ok(count) = serial.read(&mut buf[*index..]) { 82 + *index += count; 83 + if *index == IMAGE_BYTES { 84 + write_image(buf); 85 + refresh(*fast_refresh, RefreshBlockMode::NonBlocking); 86 + write_all(serial, &[Response::Ack as u8]); 87 + *state = SerialState::ReadyForCommand; 88 + } 89 + } 90 + } 91 + 92 + SerialState::FlashingProgram { index, page, num_pages, remainder } => { 93 + // Write page 0 last - this is the header, so we only want to write it once everything 94 + // else is written successfully 95 + // Keep page 0 in the first 4096 bytes of BUF for the end 96 + if *page == 0 { 97 + debug("receiving page 0"); 98 + if let Ok(count) = serial.read(&mut buf[*index..4096]) { 99 + *index += count; 100 + 101 + if num_pages.is_none() && *index >= 12 { 102 + let mut b = [0u8; 4]; 103 + b.copy_from_slice(&buf[8..12]); 104 + let num_bytes = usize::from_le_bytes(b); 105 + *num_pages = Some(num_bytes.div_ceil(4096)); 106 + *remainder = Some(num_bytes % 4096); 107 + } 108 + 109 + if *index == 4096 { 110 + *index = 0; 111 + *page += 1; 112 + } 113 + } 114 + } else { 115 + if let Ok(count) = serial.read(&mut buf[(4096 + *index)..8192]) { 116 + let mut message = heapless::String::<32>::new(); 117 + write!(message, "receiving page {page}").unwrap(); 118 + debug(&message); 119 + 120 + *index += count; 121 + 122 + let num_pages = num_pages.unwrap(); 123 + let remainder = remainder.unwrap(); 124 + if *index == 4096 || (*page == num_pages - 1 && *index == remainder) { 125 + *index = 0; 126 + 127 + // Actually write the flash page 128 + // TODO: get next slot instead of always using slot 1 129 + // TODO: wear levelling 130 + unsafe { write_flash(&buf[4096..8192], 1, *page) }; 131 + 132 + *page += 1; 133 + // If this is the last page, also flash the first page which we didn't 134 + // do at the start 135 + if *page == num_pages { 136 + unsafe { write_flash(&buf[0..4096], 1, 0) }; 137 + 138 + // Invalidate the XIP cache, in case something from the flash area 139 + // we just wrote is in there 140 + unsafe { invalidate_cache() } 141 + 142 + NEEDS_REFRESH_PROGRAMS.store(true, Ordering::Relaxed); 143 + debug("Finished writing program"); 144 + 145 + *state = SerialState::ReadyForCommand; 146 + } 147 + } 148 + } 149 + } 150 + } 151 + } 152 + } 153 + }
+2 -2
eepy-sys/src/critical_section_impl.rs
··· 7 7 8 8 unsafe impl critical_section::Impl for EepyCs { 9 9 unsafe fn acquire() -> bool { 10 - let mut state: bool; 10 + let mut state: usize; 11 11 syscall!( 12 12 SyscallNumber::CriticalSection, 13 13 out state in CsSyscall::Acquire, 14 14 ); 15 - state 15 + state != 0 16 16 } 17 17 18 18 unsafe fn release(state: bool) {
+62
eepy-sys/src/flash.rs
··· 1 + use crate::syscall; 2 + use crate::syscall::SyscallNumber; 3 + 4 + #[repr(usize)] 5 + #[derive(Copy, Clone, Debug, Eq, PartialEq)] 6 + #[cfg_attr(feature = "defmt", derive(defmt::Format))] 7 + pub enum FlashSyscall { 8 + Erase = 0, 9 + Program = 1, 10 + EraseAndProgram = 2, 11 + InvalidateCache = 3, 12 + } 13 + 14 + impl TryFrom<usize> for FlashSyscall { 15 + type Error = (); 16 + 17 + fn try_from(value: usize) -> Result<Self, Self::Error> { 18 + match value { 19 + x if x == FlashSyscall::Erase as usize => Ok(FlashSyscall::Erase), 20 + x if x == FlashSyscall::Program as usize => Ok(FlashSyscall::Program), 21 + x if x == FlashSyscall::EraseAndProgram as usize => Ok(FlashSyscall::EraseAndProgram), 22 + x if x == FlashSyscall::InvalidateCache as usize => Ok(FlashSyscall::InvalidateCache), 23 + _ => Err(()), 24 + } 25 + } 26 + } 27 + 28 + pub unsafe fn erase(start_addr: u32, len: u32) { 29 + syscall!( 30 + SyscallNumber::Flash, 31 + in FlashSyscall::Erase, 32 + in start_addr, 33 + in len, 34 + ); 35 + } 36 + 37 + pub unsafe fn program(start_addr: u32, data: &[u8]) { 38 + syscall!( 39 + SyscallNumber::Flash, 40 + in FlashSyscall::Program, 41 + in start_addr, 42 + in data.len(), 43 + in data.as_ptr(), 44 + ); 45 + } 46 + 47 + pub unsafe fn erase_and_program(start_addr: u32, data: &[u8]) { 48 + syscall!( 49 + SyscallNumber::Flash, 50 + in FlashSyscall::EraseAndProgram, 51 + in start_addr, 52 + in data.len(), 53 + in data.as_ptr(), 54 + ); 55 + } 56 + 57 + pub unsafe fn invalidate_cache() { 58 + syscall!( 59 + SyscallNumber::Flash, 60 + in FlashSyscall::InvalidateCache, 61 + ); 62 + }
+1
eepy-sys/src/lib.rs
··· 8 8 pub mod usb; 9 9 pub mod exec; 10 10 pub mod critical_section; 11 + pub mod flash; 11 12 12 13 #[cfg(feature = "critical-section-impl")] 13 14 mod critical_section_impl;
+2
eepy-sys/src/syscall.rs
··· 8 8 Usb = 3, 9 9 Exec = 4, 10 10 CriticalSection = 5, 11 + Flash = 6, 11 12 } 12 13 13 14 impl TryFrom<u8> for SyscallNumber { ··· 21 22 x if x == SyscallNumber::Usb as u8 => Ok(SyscallNumber::Usb), 22 23 x if x == SyscallNumber::Exec as u8 => Ok(SyscallNumber::Exec), 23 24 x if x == SyscallNumber::CriticalSection as u8 => Ok(SyscallNumber::CriticalSection), 25 + x if x == SyscallNumber::Flash as u8 => Ok(SyscallNumber::Flash), 24 26 _ => Err(()), 25 27 } 26 28 }
-182
eepy/src/serial.rs
··· 1 - use core::sync::atomic::Ordering; 2 - use crate::{interrupt, FLASHING, FLASHING_ACK}; 3 - use defmt::{debug, trace}; 4 - use usbd_serial::SerialPort; 5 - use fw16_epd_bsp::hal::usb::UsbBus; 6 - use fw16_epd_bsp::pac; 7 - use eepy_sys::header::ProgramSlotHeader; 8 - use eepy_serial::{Response, SerialCommand}; 9 - use eepy_sys::image::{refresh, write_image, RefreshBlockMode}; 10 - use tp370pgh01::IMAGE_BYTES; 11 - #[derive(Copy, Clone, Debug, defmt::Format)] 12 - enum SerialState { 13 - ReadyForCommand, 14 - 15 - ReceivingImage { 16 - fast_refresh: bool, 17 - index: usize, 18 - }, 19 - 20 - FlashingProgram { 21 - index: usize, 22 - page: usize, 23 - num_pages: Option<usize>, 24 - remainder: Option<usize>, 25 - }, 26 - } 27 - 28 - fn write_all(serial: &mut SerialPort<UsbBus>, mut buf: &[u8]) { 29 - while !buf.is_empty() { 30 - let _ = serial.write(buf).map(|len| buf = &buf[len..]); 31 - } 32 - } 33 - 34 - /// Safety: 35 - /// 36 - /// This function takes care of the main safety requirements of flashing, but the 37 - /// caller must ensure that the `slot` and `page` parameters are valid and do 38 - /// not produce an address outside the flash's range. Additionally, do not write 39 - /// to slot 0 as this contains the firmware. 40 - unsafe fn write_flash(buf: &[u8], slot: u8, page: usize) { 41 - debug!("Begin write slot {} page {}", slot, page); 42 - 43 - // Make sure core1 is running code from RAM with interrupts disabled 44 - FLASHING.store(true, Ordering::Relaxed); 45 - cortex_m::asm::sev(); 46 - // Wait until core1 has acknowledged that it is now in RAM code 47 - while !FLASHING_ACK.load(Ordering::Relaxed) {} 48 - // Disable interrupts on this core 49 - cortex_m::interrupt::disable(); 50 - 51 - unsafe { 52 - rp2040_flash::flash::flash_range_erase_and_program( 53 - (slot as u32) * 512 * 1024 + (page as u32) * 4096, 54 - buf, 55 - true 56 - ); 57 - } 58 - 59 - // Enable interrupts 60 - unsafe { cortex_m::interrupt::enable() } 61 - // Wake up core1 62 - FLASHING.store(false, Ordering::Relaxed); 63 - cortex_m::asm::sev(); 64 - 65 - debug!("End write slot {} page {}", slot, page); 66 - } 67 - 68 - /* 69 - #[interrupt] 70 - fn USBCTRL_IRQ() { 71 - static mut STATE: SerialState = SerialState::ReadyForCommand; 72 - 73 - // Receive buffer. Size equal to IMAGE_BYTES so it can store an entire frame; also used for 74 - // receiving flash applications. 75 - static mut BUF: [u8; IMAGE_BYTES] = [0; IMAGE_BYTES]; 76 - 77 - trace!("USBCTRL_IRQ"); 78 - 79 - // Safety: These are only accessed within this interrupt handler, or in main() before the 80 - // interrupt is enabled. 81 - #[allow(static_mut_refs)] 82 - let usb_dev = unsafe { GLOBAL_USB_DEVICE.as_mut().unwrap() }; 83 - #[allow(static_mut_refs)] 84 - let serial = unsafe { GLOBAL_USB_SERIAL.as_mut().unwrap() }; 85 - 86 - if usb_dev.poll(&mut [serial]) { 87 - match STATE { 88 - SerialState::ReadyForCommand => { 89 - let mut cmd_buf = [0u8]; 90 - if let Ok(count) = serial.read(&mut cmd_buf) { 91 - if count == 0 { 92 - return; 93 - } 94 - 95 - match SerialCommand::try_from(cmd_buf[0]) { 96 - Ok(SerialCommand::RefreshNormal) => *STATE = SerialState::ReceivingImage { fast_refresh: false, index: 0 }, 97 - Ok(SerialCommand::RefreshFast) => *STATE = SerialState::ReceivingImage { fast_refresh: true, index: 0 }, 98 - Ok(SerialCommand::UploadProgram) => *STATE = SerialState::FlashingProgram { index: 0, page: 0, num_pages: None, remainder: None }, 99 - Ok(_) => write_all(serial, &[Response::UnknownCommand as u8]), 100 - Err(_) => write_all(serial, &[Response::UnknownCommand as u8]), 101 - } 102 - } 103 - } 104 - 105 - SerialState::ReceivingImage { fast_refresh, index } => { 106 - if let Ok(count) = serial.read(&mut BUF[*index..]) { 107 - *index += count; 108 - if *index == IMAGE_BYTES { 109 - write_image(BUF); 110 - refresh(*fast_refresh, RefreshBlockMode::NonBlocking); 111 - write_all(serial, &[Response::Ack as u8]); 112 - *STATE = SerialState::ReadyForCommand; 113 - } 114 - } 115 - } 116 - 117 - SerialState::FlashingProgram { index, page, num_pages, remainder } => { 118 - debug!("Flashing program - page {}", *page); 119 - // Write page 0 last - this is the header, so we only want to write it once everything 120 - // else is written successfully 121 - // Keep page 0 in the first 4096 bytes of BUF for the end 122 - debug!("{} {} {} {}", index, page, num_pages, remainder); 123 - if *page == 0 { 124 - if let Ok(count) = serial.read(&mut BUF[*index..4096]) { 125 - *index += count; 126 - 127 - if num_pages.is_none() && *index >= 12 { 128 - let mut b = [0u8; 4]; 129 - b.copy_from_slice(&BUF[8..12]); 130 - let num_bytes = usize::from_le_bytes(b); 131 - debug!("Program is {} bytes ({} pages) long", num_bytes, num_bytes.div_ceil(4096)); 132 - *num_pages = Some(num_bytes.div_ceil(4096)); 133 - *remainder = Some(num_bytes % 4096); 134 - } 135 - 136 - if *index == 4096 { 137 - *index = 0; 138 - *page += 1; 139 - } 140 - } 141 - } else { 142 - if let Ok(count) = serial.read(&mut BUF[(4096 + *index)..8192]) { 143 - *index += count; 144 - 145 - let num_pages = num_pages.unwrap(); 146 - let remainder = remainder.unwrap(); 147 - if *index == 4096 || (*page == num_pages - 1 && *index == remainder) { 148 - *index = 0; 149 - 150 - // Actually write the flash page 151 - // TODO: get next slot instead of always using slot 1 152 - // TODO: wear levelling 153 - unsafe { write_flash(&BUF[4096..8192], 1, *page) }; 154 - 155 - *page += 1; 156 - // If this is the last page, also flash the first page which we didn't 157 - // do at the start 158 - if *page == num_pages { 159 - unsafe { write_flash(&BUF[0..4096], 1, 0) }; 160 - 161 - // Invalidate the XIP cache, in case something from the flash area 162 - // we just wrote is in there 163 - unsafe { 164 - // FIXME: steal 165 - let xip = pac::Peripherals::steal().XIP_CTRL; 166 - xip.flush().write(|w| w.flush().set_bit()); 167 - xip.flush().read(); 168 - }; 169 - 170 - let program = unsafe { &*(0x10080000 as *const ProgramSlotHeader) }; 171 - debug!("{} {}", program.name().unwrap(), program.version().unwrap()); 172 - 173 - *STATE = SerialState::ReadyForCommand; 174 - } 175 - } 176 - } 177 - } 178 - } 179 - } 180 - } 181 - } 182 - */
+111
eepy/src/syscall.rs
··· 33 33 Ok(SyscallNumber::Usb) => crate::usb::handle_usb(stack_values), 34 34 Ok(SyscallNumber::Exec) => handle_exec(stack_values, using_psp), 35 35 Ok(SyscallNumber::CriticalSection) => cs::handle_cs(stack_values), 36 + Ok(SyscallNumber::Flash) => flash::handle_flash(stack_values), 36 37 Err(_) => panic!("illegal syscall"), 37 38 } 38 39 } ··· 234 235 } 235 236 } 236 237 core::sync::atomic::compiler_fence(Ordering::SeqCst); 238 + } 239 + } 240 + 241 + mod flash { 242 + use core::sync::atomic::Ordering; 243 + use eepy_sys::flash::FlashSyscall; 244 + use eepy_sys::header::{SLOT_SIZE, XIP_BASE}; 245 + use fw16_epd_bsp::pac; 246 + use crate::exception::StackFrame; 247 + use crate::{FLASHING, FLASHING_ACK}; 248 + 249 + pub(super) fn handle_flash(stack_values: &mut StackFrame) { 250 + match FlashSyscall::try_from(stack_values.r0) { 251 + Ok(FlashSyscall::Erase) => handle_erase(stack_values), 252 + Ok(FlashSyscall::Program) => handle_program(stack_values), 253 + Ok(FlashSyscall::EraseAndProgram) => handle_erase_and_program(stack_values), 254 + Ok(FlashSyscall::InvalidateCache) => handle_invalidate_cache(), 255 + Err(_) => panic!("illegal syscall"), 256 + } 257 + } 258 + 259 + fn assert_permissions(stack_values: &mut StackFrame, start: u32, len: u32) { 260 + let slot_n = (stack_values.pc as usize - XIP_BASE as usize) / SLOT_SIZE; 261 + let start_addr_xip = start as usize + XIP_BASE as usize; 262 + let end_addr_xip = start_addr_xip + len as usize; 263 + 264 + if slot_n == 0 { 265 + // Slot 0 (launcher) can write any flash except kernel 266 + if start_addr_xip < (XIP_BASE as usize + 128 * 1024) { 267 + panic!("illegal flash write"); 268 + } 269 + return; 270 + } 271 + 272 + let slot_start = XIP_BASE as usize + (slot_n * SLOT_SIZE); 273 + let slot_end = slot_start + SLOT_SIZE; 274 + if start_addr_xip < slot_start || end_addr_xip > slot_end { 275 + panic!("illegal flash write"); 276 + } 277 + } 278 + 279 + fn begin() { 280 + // Make sure core1 is running code from RAM with interrupts disabled 281 + FLASHING.store(true, Ordering::Relaxed); 282 + cortex_m::asm::sev(); 283 + // Wait until core1 has acknowledged that it is now in RAM code 284 + while !FLASHING_ACK.load(Ordering::Relaxed) {} 285 + // Disable interrupts on this core 286 + cortex_m::interrupt::disable(); 287 + } 288 + 289 + fn end() { 290 + // Enable interrupts 291 + unsafe { cortex_m::interrupt::enable() } 292 + // Wake up core1 293 + FLASHING.store(false, Ordering::Relaxed); 294 + cortex_m::asm::sev(); 295 + } 296 + 297 + fn handle_erase(stack_values: &mut StackFrame) { 298 + let start_addr = stack_values.r1 as u32; 299 + let len = stack_values.r2 as u32; 300 + assert_permissions(stack_values, start_addr, len); 301 + if start_addr % 4096 != 0 || len % 4096 != 0 { 302 + panic!("unaligned flash erase"); 303 + } 304 + 305 + begin(); 306 + unsafe { 307 + rp2040_flash::flash::flash_range_erase(start_addr, len, true); 308 + } 309 + end(); 310 + } 311 + 312 + fn handle_program(stack_values: &mut StackFrame) { 313 + let start_addr = stack_values.r1 as u32; 314 + let data = unsafe { core::slice::from_raw_parts(stack_values.r3 as *const u8, stack_values.r2) }; 315 + assert_permissions(stack_values, start_addr, data.len() as u32); 316 + if start_addr % 256 != 0 || data.len() % 256 != 0 { 317 + panic!("unaligned flash program"); 318 + } 319 + 320 + begin(); 321 + unsafe { 322 + rp2040_flash::flash::flash_range_program(start_addr, data, true); 323 + } 324 + end(); 325 + } 326 + 327 + fn handle_erase_and_program(stack_values: &mut StackFrame) { 328 + let start_addr = stack_values.r1 as u32; 329 + let data = unsafe { core::slice::from_raw_parts(stack_values.r3 as *const u8, stack_values.r2) }; 330 + assert_permissions(stack_values, start_addr, data.len() as u32); 331 + if start_addr % 4096 != 0 || data.len() % 4096 != 0 { 332 + panic!("unaligned flash erase"); 333 + } 334 + 335 + begin(); 336 + unsafe { 337 + rp2040_flash::flash::flash_range_erase_and_program(start_addr, data, true); 338 + } 339 + end(); 340 + } 341 + 342 + fn handle_invalidate_cache() { 343 + unsafe { 344 + let xip = pac::Peripherals::steal().XIP_CTRL; 345 + xip.flush().write(|w| w.flush().set_bit()); 346 + xip.flush().read(); 347 + }; 237 348 } 238 349 }