use std::path::Path;
use udevlib;
use crate::error::{Error, ErrorKind};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct UdevInfo {
pub driver: Option<String>,
pub syspath: Option<String>,
}
fn get_device(port_path: &str) -> Result<udevlib::Device, Error> {
udevlib::Device::from_subsystem_sysname(String::from("usb"), port_path.to_string()).map_err(
|e| {
log::error!(
"Failed to get udev info for device at {}: Error({})",
port_path,
e
);
Error::new(
ErrorKind::Udev,
&format!(
"Failed to get udev info for device at {}: Error({})",
port_path, e
),
)
},
)
}
pub fn get_udev_info(port_path: &str) -> Result<UdevInfo, Error> {
let device = get_device(port_path)?;
Ok({
UdevInfo {
driver: device
.driver()
.map(|s| s.to_str().unwrap_or("").to_string()),
syspath: device.syspath().to_str().map(|s| s.to_string()),
}
})
}
pub fn get_udev_driver_name(port_path: &str) -> Result<Option<String>, Error> {
let device = get_device(port_path)?;
Ok(device
.driver()
.map(|s| s.to_str().unwrap_or("").to_string()))
}
pub fn get_udev_syspath(port_path: &str) -> Result<Option<String>, Error> {
let device = get_device(port_path)?;
Ok(device.syspath().to_str().map(|s| s.to_string()))
}
pub fn get_udev_attribute<T: AsRef<std::ffi::OsStr> + std::fmt::Display>(
port_path: &str,
attribute: T,
) -> Result<Option<String>, Error> {
let path: String = format!("/sys/bus/usb/devices/{}", port_path);
let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| {
Error::new(
ErrorKind::Udev,
&format!(
"Failed to get udev attribute {} for device at {}: Error({})",
attribute, path, e
),
)
})?;
Ok(device
.attribute_value(attribute)
.map(|s| s.to_str().unwrap_or("").to_string()))
}
pub fn get_devlinks(sys_dev: &str) -> Result<Option<Vec<String>>, Error> {
let device = udevlib::Device::from_syspath(Path::new(sys_dev)).map_err(|e| {
Error::new(
ErrorKind::Udev,
&format!(
"Failed to get DEVLINKS for device at {}: Error({})",
sys_dev, e
),
)
})?;
Ok(device.property_value("DEVLINKS").map(|s| {
s.to_string_lossy()
.split_whitespace()
.map(|s| s.to_string())
.collect()
}))
}
#[cfg(feature = "udev_hwdb")]
pub mod hwdb {
use super::*;
pub fn get(modalias: &str, key: &'static str) -> Result<Option<String>, Error> {
let hwdb = udevlib::Hwdb::new().map_err(|e| {
Error::new(
ErrorKind::Udev,
&format!("Failed to get hwdb: Error({})", e),
)
})?;
Ok(hwdb
.query_one(&modalias.to_string(), &key.to_string())
.map(|s| s.to_str().unwrap_or("").to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg_attr(not(feature = "usb_test"), ignore)]
#[test]
fn test_udev_info() {
let udevi = get_udev_info("1-0:1.0").unwrap();
assert_eq!(udevi.driver, Some("hub".into()));
assert!(udevi.syspath.unwrap().contains("usb1/1-0:1.0"));
}
#[cfg_attr(not(feature = "usb_test"), ignore)]
#[test]
fn test_udev_attribute() {
let interface_class = get_udev_attribute("1-0:1.0", "bInterfaceClass").unwrap();
assert_eq!(interface_class, Some("09".into()));
}
}