Skip to content

Commit e095439

Browse files
soiamsoNGsirhcel
authored andcommitted
Try find port info from usb_interface 's MODALIAS
1 parent 14746e6 commit e095439

File tree

2 files changed

+77
-13
lines changed

2 files changed

+77
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ nix = { version = "0.26", default-features = false, features = ["fs", "ioctl", "
2222
[target.'cfg(all(target_os = "linux", not(target_env = "musl")))'.dependencies]
2323
libudev = { version = "0.3.0", optional = true }
2424
unescaper = "0.1.3"
25+
regex = "1.5.5"
2526

2627
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
2728
core-foundation-sys = "0.8.4"

src/posix/enumerate.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use cfg_if::cfg_if;
22

3-
#[cfg(all(target_os = "linux", not(target_env = "musl"), feature = "libudev"))]
4-
use std::ffi::OsStr;
3+
cfg_if! {
4+
if #[cfg(all(target_os = "linux", not(target_env = "musl"), feature = "libudev"))]{
5+
use std::ffi::OsStr;
6+
use regex::Regex;
7+
}
8+
}
59

610
cfg_if! {
711
if #[cfg(any(target_os = "ios", target_os = "macos"))] {
@@ -175,22 +179,81 @@ fn port_type(d: &libudev::Device) -> Result<SerialPortType> {
175179
}
176180
}
177181
None => {
178-
let p = d.parent().unwrap();
179-
let parent_driver = p.driver().unwrap().to_str().unwrap();
180-
let parent_subsystem = p.subsystem().unwrap().to_str().unwrap();
182+
fn find_usb_interface_from_parents(
183+
parent: Option<libudev::Device>,
184+
) -> Option<libudev::Device> {
185+
let mut p = parent?;
186+
187+
// limit the query depth
188+
for _ in 1..4 {
189+
match p.devtype() {
190+
None => match p.parent() {
191+
None => break,
192+
Some(x) => p = x,
193+
},
194+
Some(s) => {
195+
if s.to_str()? == "usb_interface" {
196+
break;
197+
} else {
198+
match p.parent() {
199+
None => break,
200+
Some(x) => p = x,
201+
}
202+
}
203+
}
204+
}
205+
}
181206

182-
if parent_driver == "cdc_acm" && parent_subsystem == "usb" {
183-
let product_code = p.property_value("PRODUCT").and_then(OsStr::to_str).unwrap();
184-
Ok(SerialPortType::UsbPort(UsbPortInfo {
185-
vid: u16::from_str_radix(&product_code[0..4], 16).unwrap(),
186-
pid: u16::from_str_radix(&product_code[5..9], 16).unwrap(),
207+
Some(p)
208+
}
209+
210+
fn get_modalias_from_device(d: libudev::Device) -> Option<String> {
211+
Some(
212+
d.property_value("MODALIAS")
213+
.and_then(OsStr::to_str)?
214+
.to_owned(),
215+
)
216+
}
217+
218+
// MODALIAS = usb:v303Ap1001d0101dcEFdsc02dp01ic02isc02ip00in00
219+
// v 303A (device vendor)
220+
// p 1001 (device product)
221+
// d 0101 (bcddevice)
222+
// dc EF (device class)
223+
// dsc 02 (device subclass)
224+
// dp 01 (device protocol)
225+
// ic 02 (interface class)
226+
// isc 02 (interface subclass)
227+
// ip 00 (interface protocol)
228+
// in 00 (interface number)
229+
fn parse_modalias(moda: String) -> Option<UsbPortInfo> {
230+
let re = Regex::new(concat!(
231+
r"usb:v(?P<vid>[[:xdigit:]]{4})",
232+
r"p(?P<pid>[[:xdigit:]]{4})",
233+
r".*",
234+
r"in(?P<in>[[:xdigit:]]{2})"
235+
))
236+
.unwrap();
237+
238+
let caps = re.captures(moda.as_str())?;
239+
240+
Some(UsbPortInfo {
241+
vid: u16::from_str_radix(&caps["vid"], 16).ok()?,
242+
pid: u16::from_str_radix(&caps["pid"], 16).ok()?,
187243
serial_number: None,
188244
manufacturer: None,
189245
product: None,
190-
}))
191-
} else {
192-
Ok(SerialPortType::Unknown)
246+
#[cfg(feature = "usbportinfo-interface")]
247+
interface: u8::from_str_radix(&caps["in"], 16).ok(),
248+
})
193249
}
250+
251+
find_usb_interface_from_parents(d.parent())
252+
.and_then(get_modalias_from_device)
253+
.and_then(parse_modalias)
254+
.map_or(Ok(SerialPortType::Unknown), |port_info| {
255+
Ok(SerialPortType::UsbPort(port_info))
256+
})
194257
}
195258
_ => Ok(SerialPortType::Unknown),
196259
}

0 commit comments

Comments
 (0)