Skip to content

Commit bb76db9

Browse files
committed
feat: add embassy-boot/ dfu support
Signed-off-by: Pascal Jäger <pascal.jaeger@leimstift.de>
1 parent 85e4dee commit bb76db9

1 file changed

Lines changed: 136 additions & 3 deletions

File tree

src/main.rs

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,40 @@ async fn init_project(
230230
chip_or_board.clone()
231231
};
232232

233+
// Ask about embassy-boot (DFU) for RP2040-based chips only.
234+
let mut embassy_boot = false;
235+
let mut flash_size: u32 = 2 * 1024 * 1024; // default 2 MB
236+
if chip_or_board == "rp2040" || chip_or_board == "pico_w" {
237+
embassy_boot = Select::new(
238+
"Use embassy-boot (DFU firmware update via USB)?",
239+
vec!["No", "Yes"],
240+
)
241+
.prompt()?
242+
== "Yes";
243+
if embassy_boot {
244+
flash_size = Select::new(
245+
"Total flash size:",
246+
vec!["2 MB", "4 MB", "8 MB", "16 MB"],
247+
)
248+
.with_help_message(
249+
"When in doubt, use 2 MB. Smaller works on bigger flashes.",
250+
)
251+
.prompt()
252+
.map(|s| match s {
253+
"2 MB" => 2 * 1024 * 1024,
254+
"4 MB" => 4 * 1024 * 1024,
255+
"8 MB" => 8 * 1024 * 1024,
256+
"16 MB" => 16 * 1024 * 1024,
257+
_ => unreachable!(),
258+
})?;
259+
}
260+
}
261+
262+
let target_dir_clone = target_dir.clone();
233263
let project_info = ProjectInfo {
234264
project_name,
235265
target_dir,
236-
remote_folder,
266+
remote_folder: remote_folder.clone(),
237267
chip: chip_or_board,
238268
uf2_key,
239269
disabled_default_feature: Vec::new(),
@@ -243,8 +273,14 @@ async fn init_project(
243273
// Download template
244274
match local_path {
245275
Some(p) => {
246-
// Copy local template to project_info.target_dir
247-
copy_dir_recursive(Path::new(&p), &project_info.target_dir)?;
276+
// Copy only the chip-specific subfolder to target directory
277+
let src = Path::new(&p).join(&remote_folder);
278+
if src.is_dir() {
279+
copy_dir_recursive(&src, &project_info.target_dir)?;
280+
} else {
281+
// Fallback: copy the whole directory
282+
copy_dir_recursive(Path::new(&p), &project_info.target_dir)?;
283+
}
248284
}
249285
None => {
250286
// Use remote template
@@ -261,6 +297,103 @@ async fn init_project(
261297
// Post-process
262298
post_process(project_info)?;
263299

300+
// Apply embassy-boot customisations
301+
if embassy_boot {
302+
apply_embassy_boot(&target_dir_clone, flash_size)?;
303+
}
304+
305+
Ok(())
306+
}
307+
308+
/// Apply embassy-boot (DFU) template customisations to a generated project.
309+
fn apply_embassy_boot(
310+
target_dir: &Path,
311+
flash_size: u32,
312+
) -> Result<(), Box<dyn Error>> {
313+
// ── memory.x ──────────────────────────────────────────────────────
314+
// No BOOT2 region — the embassy-boot bootloader (e.g. bootymcbootface)
315+
// provides it. The firmware starts at the ACTIVE slot (0x10007000).
316+
// Matches rmk-config auto-calc: use all remaining flash after
317+
// bootloader+state (28K), storage (default 128K=32×4K), and 1 page
318+
// for DFU delta (embassy-boot invariant: dfu = active + 1 page).
319+
let page_size = 4096u32;
320+
let storage_size = 128 * 1024; // 32 sectors × 4K (rp2040 default)
321+
let bootloader_state_end = 0x7000u32;
322+
let remaining = flash_size - bootloader_state_end - storage_size;
323+
let flash_len = (remaining - page_size) / 2;
324+
let flash_len_str = if flash_len >= 1024 * 1024 {
325+
format!("{}M", flash_len / (1024 * 1024))
326+
} else if flash_len % 1024 == 0 {
327+
format!("{}K", flash_len / 1024)
328+
} else {
329+
flash_len.to_string()
330+
};
331+
let memory_x = format!(
332+
"MEMORY {{\n\
333+
\x20 FLASH : ORIGIN = 0x10007000, LENGTH = {}\n\
334+
\x20 RAM : ORIGIN = 0x20000000, LENGTH = 256K\n\
335+
}}\n",
336+
flash_len_str
337+
);
338+
fs::write(target_dir.join("memory.x"), memory_x)?;
339+
340+
// ── Cargo.toml ────────────────────────────────────────────────────
341+
let cargo_path = target_dir.join("Cargo.toml");
342+
343+
// 1 cortex-m-rt: add set-vtor (needed by embassy-boot for vector table relocation)
344+
let cargo = fs::read_to_string(&cargo_path)?;
345+
let cargo = cargo.replace(
346+
"cortex-m-rt = \"0.7.5\"",
347+
"cortex-m-rt = { version = \"0.7.5\", features = [\"set-vtor\"] }",
348+
);
349+
fs::write(&cargo_path, &cargo)?;
350+
351+
// 2 rmk feature: swap "rp2040" → "dfu_rp"
352+
let mut manifest = cargo_toml::Manifest::from_path(&cargo_path)
353+
.map_err(|e| format!("Failed to parse Cargo.toml: {}", e))?;
354+
if let Some(cargo_toml::Dependency::Detailed(ref mut details)) = manifest.dependencies.get_mut("rmk") {
355+
if let Some(pos) = details.features.iter().position(|f| f == "rp2040") {
356+
details.features[pos] = "dfu_rp".to_string();
357+
}
358+
details.features.sort_unstable();
359+
details.features.dedup();
360+
}
361+
let updated_toml = toml::to_string(&manifest)
362+
.map_err(|e| format!("Failed to serialize Cargo.toml: {}", e))?;
363+
fs::write(&cargo_path, &updated_toml)?;
364+
365+
// ── keyboard.toml ─────────────────────────────────────────────────
366+
let kb_path = target_dir.join("keyboard.toml");
367+
let mut kb = fs::read_to_string(&kb_path)?;
368+
// ensure [storage] is present (needed by dfu_rp flash init)
369+
if !kb.contains("[storage]") {
370+
kb.push_str("\n[storage]\nenabled = true\n");
371+
}
372+
// Use led = "none" as default — PIN_25 conflicts with CYW43 on Pico W
373+
if !kb.contains("[dfu]") {
374+
kb.push_str(
375+
"\n\
376+
[dfu]\n\
377+
led = \"none\"\n",
378+
);
379+
}
380+
fs::write(&kb_path, kb)?;
381+
382+
// ── build.rs – strip flip-link & link-rp.x ────────────────────────
383+
let build_path = target_dir.join("build.rs");
384+
if build_path.exists() {
385+
let build = fs::read_to_string(&build_path)?;
386+
// comment out any flip-link references
387+
let build = build.replace("flip-link", "# flip-link");
388+
// strip -Tlink-rp.x — BOOT2 region is handled by the bootloader
389+
let build = build.replace(
390+
"println!(\"cargo:rustc-link-arg-bins=-Tlink-rp.x\");\n",
391+
"",
392+
);
393+
fs::write(&build_path, build)?;
394+
}
395+
396+
println!("✓ embassy-boot (DFU) template applied");
264397
Ok(())
265398
}
266399

0 commit comments

Comments
 (0)