@@ -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]\n enabled = 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