Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions crates/fbuild-deploy/src/rp2040.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ mod target;
mod topology;
use mount::try_mount_rom_device;
use target::{
describe_unhealthy, resolve_requested_runtime_target, select_cdc_candidate, serial_selector,
describe_unhealthy, preflash_eligible_port_names, resolve_requested_runtime_target,
select_cdc_candidate, serial_selector,
};

const UF2_MAGIC_START0: u32 = 0x0A32_4655;
Expand Down Expand Up @@ -1828,8 +1829,7 @@ impl Deployer for Rp2040Deployer {
"RP2040 serial snapshot task failed: {error}"
))
})??;
let ports_before: BTreeSet<String> =
current_ports.iter().map(|port| port.name.clone()).collect();
let ports_before = preflash_eligible_port_names(&current_ports);
let explicit_volume = selector.and_then(explicit_uf2_volume);
if selector.is_some_and(|value| value.to_ascii_lowercase().starts_with("uf2="))
&& explicit_volume.is_none()
Expand Down Expand Up @@ -2878,6 +2878,63 @@ mod tests {
Some(root.path().to_path_buf())
);
assert!(resolve_requested_runtime_target(&selector, &[]).is_err());

let before = preflash_eligible_port_names(&[cdc_candidate(
"COM18",
Some("2DCB876B587EA334"),
fbuild_serial::ports::PortHealth::Phantom {
problem_code: None,
status: None,
},
)]);
let recovered = select_cdc_candidate(
None,
None,
&before,
&[cdc_candidate(
"COM18",
Some("2DCB876B587EA334"),
fbuild_serial::ports::PortHealth::HealthyPresent,
)],
)
.unwrap()
.expect("same-name healthy CDC must be recovered after explicit UF2 deploy");
assert_eq!(recovered.name, "COM18");
}

#[test]
fn stale_com_selector_uses_unique_bootsel_and_reacquires_same_name() {
let stale = cdc_candidate(
"COM18",
Some("2DCB876B587EA334"),
fbuild_serial::ports::PortHealth::Phantom {
problem_code: None,
status: None,
},
);
let can_attribute = resolve_requested_runtime_target("COM18", std::slice::from_ref(&stale))
.ok()
.is_some();
let (volume, volumes_before) =
pretouch_volume_policy(vec![PathBuf::from("H:\\")], can_attribute).unwrap();

assert_eq!(volume, Some(PathBuf::from("H:\\")));
assert!(volumes_before.is_empty());

let before = preflash_eligible_port_names(&[stale]);
let recovered = select_cdc_candidate(
None,
None,
&before,
&[cdc_candidate(
"COM18",
Some("2DCB876B587EA334"),
fbuild_serial::ports::PortHealth::HealthyPresent,
)],
)
.unwrap()
.expect("stale COM selector must converge through unique BOOTSEL recovery");
assert_eq!(recovered.name, "COM18");
}

#[test]
Expand Down
45 changes: 45 additions & 0 deletions crates/fbuild-deploy/src/rp2040_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ pub(super) fn describe_unhealthy(port: &PicoCdcPort) -> String {
)
}

/// Snapshot only CDC names that were eligible before the flash. A stale
/// Windows devnode may later return healthy under the same COM name; treating
/// that historical phantom name as already present would hide the recovered
/// endpoint from post-flash discovery.
pub(super) fn preflash_eligible_port_names(candidates: &[PicoCdcPort]) -> BTreeSet<String> {
candidates
.iter()
.filter(|candidate| !candidate.health.is_known_unhealthy())
.map(|candidate| candidate.name.clone())
.collect()
}

pub(super) fn resolve_requested_runtime_target(
selector: &str,
candidates: &[PicoCdcPort],
Expand Down Expand Up @@ -177,6 +189,39 @@ mod tests {
);
}

#[test]
fn phantom_name_can_return_as_a_healthy_post_flash_endpoint() {
let before = preflash_eligible_port_names(&[
cdc_with_health(
"COM18",
Some("PICO-1"),
PortHealth::Phantom {
problem_code: None,
status: None,
},
),
cdc_with_health("COM27", Some("PICO-2"), PortHealth::HealthyPresent),
]);

assert_eq!(before, BTreeSet::from(["COM27".to_string()]));
assert_eq!(
selected_name(
select_cdc_candidate(
None,
None,
&before,
&[cdc_with_health(
"COM18",
Some("PICO-1"),
PortHealth::HealthyPresent,
)],
)
.unwrap()
),
Some("COM18".to_string())
);
}

#[test]
fn usb_serial_selector_survives_port_renumbering() {
let before = BTreeSet::from(["COM7".to_string()]);
Expand Down
Loading