Skip to content

Commit

Permalink
Try find port info from usb_interface 's MODALIAS
Browse files Browse the repository at this point in the history
  • Loading branch information
soiamsoNG committed Mar 30, 2024
1 parent 78ab552 commit 90b1733
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ nix = { version = "0.26", default-features = false, features = ["fs", "ioctl", "
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
libudev = { version = "0.3.0", optional = true }
unescaper = "0.1.3"
regex = "1.5.5"

[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation-sys = "0.8.4"
Expand Down
89 changes: 76 additions & 13 deletions src/posix/enumerate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use cfg_if::cfg_if;

#[cfg(all(target_os = "linux", not(target_env = "musl"), feature = "libudev"))]
use std::ffi::OsStr;
cfg_if! {
if #[cfg(all(target_os = "linux", not(target_env = "musl"), feature = "libudev"))]{
use std::ffi::OsStr;
use regex::Regex;
}
}

cfg_if! {
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
Expand Down Expand Up @@ -175,22 +179,81 @@ fn port_type(d: &libudev::Device) -> Result<SerialPortType> {
}
}
None => {
let p = d.parent().unwrap();
let parent_driver = p.driver().unwrap().to_str().unwrap();
let parent_subsystem = p.subsystem().unwrap().to_str().unwrap();
fn find_usb_interface_from_parents(
parent: Option<libudev::Device>,
) -> Option<libudev::Device> {
let mut p = parent?;

// limit the query depth
for _ in 1..4 {
match p.devtype() {
None => match p.parent() {
None => break,
Some(x) => p = x,
},
Some(s) => {
if s.to_str()? == "usb_interface" {
break;
} else {
match p.parent() {
None => break,
Some(x) => p = x,
}
}
}
}
}

if parent_driver == "cdc_acm" && parent_subsystem == "usb" {
let product_code = p.property_value("PRODUCT").and_then(OsStr::to_str).unwrap();
Ok(SerialPortType::UsbPort(UsbPortInfo {
vid: u16::from_str_radix(&product_code[0..4], 16).unwrap(),
pid: u16::from_str_radix(&product_code[5..9], 16).unwrap(),
Some(p)
}

fn get_modalias_from_device(d: libudev::Device) -> Option<String> {
Some(
d.property_value("MODALIAS")
.and_then(OsStr::to_str)?
.to_owned(),
)
}

// MODALIAS = usb:v303Ap1001d0101dcEFdsc02dp01ic02isc02ip00in00
// v 303A (device vendor)
// p 1001 (device product)
// d 0101 (bcddevice)
// dc EF (device class)
// dsc 02 (device subclass)
// dp 01 (device protocol)
// ic 02 (interface class)
// isc 02 (interface subclass)
// ip 00 (interface protocol)
// in 00 (interface number)
fn parse_modalias(moda: String) -> Option<UsbPortInfo> {
let re = Regex::new(concat!(
r"usb:v(?P<vid>[[:xdigit:]]{4})",
r"p(?P<pid>[[:xdigit:]]{4})",
r".*",
r"in(?P<in>[[:xdigit:]]{2})"
))
.unwrap();

let caps = re.captures(moda.as_str())?;

Some(UsbPortInfo {
vid: u16::from_str_radix(&caps["vid"], 16).ok()?,
pid: u16::from_str_radix(&caps["pid"], 16).ok()?,
serial_number: None,
manufacturer: None,
product: None,
}))
} else {
Ok(SerialPortType::Unknown)
#[cfg(feature = "usbportinfo-interface")]
interface: u8::from_str_radix(&caps["in"], 16).ok(),
})
}

find_usb_interface_from_parents(d.parent())
.and_then(get_modalias_from_device)
.and_then(parse_modalias)
.map_or(Ok(SerialPortType::Unknown), |port_info| {
Ok(SerialPortType::UsbPort(port_info))
})
}
_ => Ok(SerialPortType::Unknown),
}
Expand Down

0 comments on commit 90b1733

Please sign in to comment.