8f1549e284
This allows a single PCI daemon to run on the whole system, prevents multiple drivers from claiming the same PCI device and makes it possible for userspace to enumerate all available PCI devices. In the future this will enable an lspci tool, possibly PCIe hot plugging and more. Co-Authored-By: 4lDO2 <4lDO2@protonmail.com>
103 lines
3.0 KiB
Rust
103 lines
3.0 KiB
Rust
use std::fs;
|
|
use std::process::Command;
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
|
|
use pcid_interface::config::Config;
|
|
use pcid_interface::PciFunctionHandle;
|
|
|
|
fn main() -> Result<()> {
|
|
let mut args = pico_args::Arguments::from_env();
|
|
let config_path = args
|
|
.free_from_str::<String>()
|
|
.expect("failed to parse --config argument");
|
|
|
|
common::setup_logging(
|
|
"bus",
|
|
"pci",
|
|
"pci-spawner.log",
|
|
log::LevelFilter::Info,
|
|
log::LevelFilter::Trace,
|
|
);
|
|
|
|
let config_data = if fs::metadata(&config_path)?.is_file() {
|
|
fs::read_to_string(&config_path)?
|
|
} else {
|
|
let mut config_data = String::new();
|
|
for path in fs::read_dir(&config_path)? {
|
|
if let Ok(tmp) = fs::read_to_string(path.unwrap().path()) {
|
|
config_data.push_str(&tmp);
|
|
}
|
|
}
|
|
config_data
|
|
};
|
|
let config: Config = toml::from_str(&config_data)?;
|
|
|
|
for entry in fs::read_dir("/scheme/pci")? {
|
|
let entry = entry.context("failed to get entry")?;
|
|
let device_path = entry.path();
|
|
log::trace!("ENTRY: {}", device_path.to_string_lossy());
|
|
|
|
let mut handle = match PciFunctionHandle::connect_by_path(&device_path) {
|
|
Ok(handle) => handle,
|
|
Err(err) => {
|
|
// Either the device is gone or it is already in-use by a driver.
|
|
log::debug!(
|
|
"pcid-spawner: {} already in use: {err}",
|
|
device_path.display(),
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let full_device_id = handle.config().func.full_device_id;
|
|
|
|
log::debug!(
|
|
"pcid-spawner enumerated: PCI {} {}",
|
|
handle.config().func.addr,
|
|
full_device_id.display()
|
|
);
|
|
|
|
let Some(driver) = config
|
|
.drivers
|
|
.iter()
|
|
.find(|driver| driver.match_function(&full_device_id))
|
|
else {
|
|
log::debug!("no driver for {}, continuing", handle.config().func.addr);
|
|
continue;
|
|
};
|
|
|
|
let mut args = driver.command.iter();
|
|
|
|
let program = args
|
|
.next()
|
|
.ok_or_else(|| anyhow!("driver configuration entry did not have any command!"))?;
|
|
let program = if program.starts_with('/') {
|
|
program.to_owned()
|
|
} else {
|
|
"/usr/lib/drivers/".to_owned() + program
|
|
};
|
|
|
|
let mut command = Command::new(program);
|
|
command.args(args);
|
|
|
|
log::info!("pcid-spawner: spawn {:?}", command);
|
|
|
|
handle.enable_device()?;
|
|
|
|
let channel_fd = handle.into_inner_fd();
|
|
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
|
|
|
match command.status() {
|
|
Ok(status) if !status.success() => {
|
|
log::error!("pcid-spawner: driver {command:?} failed with {status}");
|
|
}
|
|
Ok(_) => {}
|
|
Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"),
|
|
}
|
|
syscall::close(channel_fd as usize).unwrap();
|
|
}
|
|
|
|
Ok(())
|
|
}
|