#[cfg(target_os = "linux")]
use ados_protocol::hwcaps::{AbsenceReason, Evidence};
use ados_protocol::hwcaps::{Probed, SocCompatible};
#[cfg(target_os = "linux")]
const COMPATIBLE_NODE: &str = "/proc/device-tree/compatible";
pub fn probe_soc() -> Probed<SocCompatible> {
#[cfg(not(target_os = "linux"))]
{
Probed::NotProbed
}
#[cfg(target_os = "linux")]
{
let Ok(raw) = std::fs::read(COMPATIBLE_NODE) else {
return Probed::absent(AbsenceReason::NodeMissing);
};
let compatibles = parse_compatible(&raw);
match compatibles.first() {
Some(first) => {
let evidence = Evidence::DeviceTreeCompatible(first.clone());
Probed::present(SocCompatible(compatibles), evidence)
}
None => Probed::absent(AbsenceReason::NodeMissing),
}
}
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
fn parse_compatible(raw: &[u8]) -> Vec<String> {
raw.split(|&b| b == 0)
.filter(|chunk| !chunk.is_empty())
.map(|chunk| String::from_utf8_lossy(chunk).into_owned())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splits_nul_separated_list_most_specific_first() {
let raw = b"radxa,cubie-a7z\0allwinner,sun60i-a733\0";
let parsed = parse_compatible(raw);
assert_eq!(
parsed,
vec![
"radxa,cubie-a7z".to_string(),
"allwinner,sun60i-a733".to_string(),
]
);
assert_eq!(parsed.first().unwrap(), "radxa,cubie-a7z");
}
#[test]
fn handles_missing_trailing_nul() {
let raw = b"vendor,board\0vendor,soc";
assert_eq!(
parse_compatible(raw),
vec!["vendor,board".to_string(), "vendor,soc".to_string()]
);
}
#[test]
fn single_entry() {
let raw = b"brcm,bcm2711\0";
assert_eq!(parse_compatible(raw), vec!["brcm,bcm2711".to_string()]);
}
#[test]
fn drops_empty_runs_and_trailing_nul() {
let raw = b"a,one\0\0a,two\0";
assert_eq!(
parse_compatible(raw),
vec!["a,one".to_string(), "a,two".to_string()]
);
}
#[test]
fn empty_input_yields_no_compatibles() {
assert!(parse_compatible(b"").is_empty());
assert!(parse_compatible(b"\0\0").is_empty());
}
}