From cc110cd3a2bbb61a1d42b92bcaddded8ea15e7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20J=C3=A4ger?= Date: Tue, 9 Jun 2026 08:23:48 +0200 Subject: [PATCH] feat: add embassy-boot/ dfu support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Jäger --- src/main.rs | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/main.rs b/src/main.rs index 5bcaf57..f1158cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -230,6 +230,36 @@ async fn init_project( chip_or_board.clone() }; + // Ask about embassy-boot (DFU) for RP2040-based chips only. + let mut embassy_boot = false; + let mut flash_size: u32 = 2 * 1024 * 1024; // default 2 MB + if chip_or_board == "rp2040" || chip_or_board == "pico_w" { + embassy_boot = Select::new( + "Use embassy-boot (DFU firmware update via USB)?", + vec!["No", "Yes"], + ) + .prompt()? + == "Yes"; + if embassy_boot { + flash_size = Select::new( + "Total flash size:", + vec!["2 MB", "4 MB", "8 MB", "16 MB"], + ) + .with_help_message( + "When in doubt, use 2 MB. Smaller works on bigger flashes.", + ) + .prompt() + .map(|s| match s { + "2 MB" => 2 * 1024 * 1024, + "4 MB" => 4 * 1024 * 1024, + "8 MB" => 8 * 1024 * 1024, + "16 MB" => 16 * 1024 * 1024, + _ => unreachable!(), + })?; + } + } + + let target_dir_clone = target_dir.clone(); let project_info = ProjectInfo { project_name, target_dir, @@ -261,6 +291,123 @@ async fn init_project( // Post-process post_process(project_info)?; + // Apply embassy-boot customisations + if embassy_boot { + apply_embassy_boot(&target_dir_clone, flash_size)?; + } + + Ok(()) +} + +/// Apply embassy-boot (DFU) template customisations to a generated project. +fn apply_embassy_boot( + target_dir: &Path, + flash_size: u32, +) -> Result<(), Box> { + // ── memory.x ────────────────────────────────────────────────────── + // No BOOT2 region — the embassy-boot bootloader (e.g. bootymcbootface) + // provides it. The firmware starts at the ACTIVE slot (0x10007000). + let flash_len = flash_size / 4; + let flash_len_str = if flash_len >= 1024 * 1024 { + format!("{}M", flash_len / (1024 * 1024)) + } else if flash_len % 1024 == 0 { + format!("{}K", flash_len / 1024) + } else { + flash_len.to_string() + }; + let memory_x = format!( + "MEMORY {{\n\ + \x20 FLASH : ORIGIN = 0x10007000, LENGTH = {}\n\ + \x20 RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\ + }}\n", + flash_len_str + ); + fs::write(target_dir.join("memory.x"), memory_x)?; + + // ── Cargo.toml ──────────────────────────────────────────────────── + let cargo_path = target_dir.join("Cargo.toml"); + let cargo = fs::read_to_string(&cargo_path)?; + + // 1 rmk feature: "rp2040" → "dfu_rp" + let cargo = cargo.replace( + r#"features = ["rp2040"]"#, + r#"features = ["dfu_rp"]"#, + ); + + // 2 cortex-m-rt: add set-vtor + let cargo = cargo.replace( + "cortex-m-rt = \"0.7.5\"", + "cortex-m-rt = { version = \"0.7.5\", features = [\"set-vtor\"] }", + ); + + // 3 embassy-rp: add critical-section-impl feature + let cargo = cargo.replace( + r#"features = [ + "rp2040", + "defmt", + "time-driver", +]"#, + r#"features = [ + "rp2040", + "defmt", + "time-driver", + "critical-section-impl", +]"#, + ); + + // 4 Add portable-atomic (needed by embassy-boot-rp) + let cargo = if cargo.contains("portable-atomic") { + cargo + } else { + // Insert after the embassy-rp / embassy-executor block + cargo.replace( + "embassy-executor", + "portable-atomic = { version = \"1.11\", features = [\"critical-section\"] }\nembassy-executor", + ) + }; + + // 6 embassy-rp: bump version to 0.10 (needed by embassy-boot-rp) + let cargo = cargo.replace( + "embassy-rp = { version = \"0.8\", features = [", + "embassy-rp = { version = \"0.10\", features = [", + ); + + // 7 Add embassy-usb-driver (needed by embassy-usb-dfu on RP2040) + let cargo = if cargo.contains("embassy-usb-driver") { + cargo + } else { + cargo.replace( + "panic-probe", + "embassy-usb-driver = \"=0.2.0\"\npanic-probe", + ) + }; + + fs::write(&cargo_path, cargo)?; + + // ── keyboard.toml ───────────────────────────────────────────────── + let kb_path = target_dir.join("keyboard.toml"); + let mut kb = fs::read_to_string(&kb_path)?; + // ensure [storage] is present (needed by dfu_rp flash init) + if !kb.contains("[storage]") { + kb.push_str("\n[storage]\nenabled = true\n"); + } + kb.push_str( + "\n\ + [dfu]\n\ + led = \"PIN_25\"\n", + ); + fs::write(&kb_path, kb)?; + + // ── build.rs – strip flip-link ──────────────────────────────────── + let build_path = target_dir.join("build.rs"); + if build_path.exists() { + let build = fs::read_to_string(&build_path)?; + // comment out any flip-link references + let build = build.replace("flip-link", "# flip-link"); + fs::write(&build_path, build)?; + } + + println!("✓ embassy-boot (DFU) template applied"); Ok(()) }