use crate::error::FpgadError;
use crate::softeners::error::FpgadSoftenerError;
use log::trace;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
pub fn get_dfx_mgr_client_path() -> Result<String, FpgadSoftenerError> {
let dfx_mgr_client_path = if let Ok(snap_components) = env::var("SNAP_COMPONENTS") {
let path = format!("{}/dfx-mgr/usr/bin/dfx-mgr-client", snap_components);
if !Path::new(&path).exists() {
return Err(FpgadSoftenerError::DfxMgr(format!(
"dfx-mgr-client not found at '{path}'.\n\n\
To enable Xilinx DFX Manager support, install the dfx-mgr component:\n\n\
`sudo snap install fpgad+dfx-mgr.comp --dangerous`\n\n\
Or run the CLI using the `xlnx-sys` platform (no dfx-mgr required):\n\n\
`fpgad --platform=xlnx-sys <command>`\n\n\
If you are calling the daemon over DBus manually \n\n \
set the `platform_string` to `xlnx-sys` instead."
)));
}
path
} else {
let path = String::from("/usr/bin/dfx-mgr-client");
if !Path::new(&path).exists() {
return Err(FpgadSoftenerError::DfxMgr(format!(
"dfx-mgr-client not found on system at '{path}'.\n\n\
To enable Xilinx DFX Manager support, install the dfx-mgr binaries e.g.\n\n\
`sudo apt install dfx-mgr`\n\n\
Or run the CLI using the `xlnx-sys` platform (no dfx-mgr required):\n\n\
`fpgad --platform=xlnx-sys <command>`\n\n\
If you are calling the daemon over DBus manually \n\n \
set the `platform_string` to `xlnx-sys` instead."
)));
}
path
};
Ok(dfx_mgr_client_path)
}
pub fn run_dfx_mgr(args: &[&str]) -> Result<String, FpgadSoftenerError> {
let dfx_mgr_client_path = get_dfx_mgr_client_path()?;
trace!("Calling dfx-mgr-client with args {:#?}", args);
let output = Command::new(&dfx_mgr_client_path)
.args(args)
.output()
.map_err(|e| {
FpgadSoftenerError::DfxMgr(format!("dfx-mgr-client failed to produce output:\n{e}"))
})?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(FpgadSoftenerError::DfxMgr(format!(
"dfx-mgr-client failed.\n{}\nStdout:\n{:?}\nStderr:\n{:?}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)))
}
}
pub fn extract_firmware_name(dtbo_path: &Path) -> Result<String, FpgadError> {
trace!("Extracting firmware-name from '{}'", dtbo_path.display());
let dtb_data = fs::read(dtbo_path).map_err(|e| {
FpgadSoftenerError::DfxMgr(format!(
"Failed to read dtbo file '{}': {}",
dtbo_path.display(),
e
))
})?;
let fdt = fdt::Fdt::new(&dtb_data).map_err(|e| {
FpgadSoftenerError::DfxMgr(format!(
"Failed to parse dtbo file '{}': {:?}",
dtbo_path.display(),
e
))
})?;
for node in fdt.all_nodes() {
if let Some(firmware_name_prop) = node.property("firmware-name") {
let value = firmware_name_prop.value;
let end = value.iter().position(|&b| b == 0).unwrap_or(value.len());
let firmware_name = std::str::from_utf8(&value[..end]).map_err(|e| {
FpgadSoftenerError::DfxMgr(format!(
"Failed to parse firmware-name as UTF-8 string: {}",
e
))
})?;
trace!("Found firmware-name='{}' in dtbo", firmware_name);
return Ok(firmware_name.to_string());
}
}
Err(FpgadSoftenerError::DfxMgr(format!(
"`firmware-name` property not found in dtbo file '{}'",
dtbo_path.display()
))
.into())
}
#[cfg(test)]
mod tests {
use crate::softeners::xilinx_dfx_mgr::xilinx_dfx_mgr_helpers::extract_firmware_name;
use googletest::prelude::*;
use std::path::PathBuf;
#[gtest]
fn test_extract_firmware_name_k26() {
let test_dtbo = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/test_data/k26-starter-kits/k26_starter_kits.dtbo");
if !test_dtbo.exists() {
println!("SKIP: test data not found at {}", test_dtbo.display());
return;
}
let result = extract_firmware_name(&test_dtbo);
assert_that!(result, ok(eq("k26_starter_kits.bit.bin")));
}
#[gtest]
fn test_extract_firmware_name_k24() {
let test_dtbo = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/test_data/k24-starter-kits/k24_starter_kits.dtbo");
if !test_dtbo.exists() {
println!("SKIP: test data not found at {}", test_dtbo.display());
return;
}
let result = extract_firmware_name(&test_dtbo);
assert_that!(result, ok(eq("k24_starter_kits.bit.bin")));
}
}