#[derive(Debug, Clone)]
pub struct PortInfo {
pub name: String,
pub description: String,
}
pub fn available_ports() -> Vec<PortInfo> {
match serialport::available_ports() {
Ok(ports) => ports
.into_iter()
.filter(|p| {
match &p.port_type {
serialport::SerialPortType::UsbPort(_) => true,
serialport::SerialPortType::PciPort => {
is_embedded_port(&p.port_name)
}
serialport::SerialPortType::Unknown => {
!is_legacy_serial(&p.port_name)
}
serialport::SerialPortType::BluetoothPort => false,
}
})
.map(|p| {
let description = match &p.port_type {
serialport::SerialPortType::UsbPort(info) => {
let mut desc = String::new();
if let Some(manufacturer) = &info.manufacturer {
desc.push_str(manufacturer);
}
if let Some(product) = &info.product {
if !desc.is_empty() {
desc.push_str(" - ");
}
desc.push_str(product);
}
if desc.is_empty() {
format!("USB ({:04x}:{:04x})", info.vid, info.pid)
} else {
desc
}
}
serialport::SerialPortType::PciPort => "PCI".to_string(),
serialport::SerialPortType::BluetoothPort => "Bluetooth".to_string(),
serialport::SerialPortType::Unknown => "Unknown".to_string(),
};
PortInfo {
name: p.port_name,
description,
}
})
.collect(),
Err(_) => Vec::new(),
}
}
fn is_legacy_serial(port_name: &str) -> bool {
if let Some(suffix) = port_name.strip_prefix("/dev/ttyS") {
return suffix.chars().all(|c| c.is_ascii_digit());
}
false
}
fn is_embedded_port(port_name: &str) -> bool {
if port_name.starts_with("/dev/ttyAMA") {
return true;
}
if port_name.starts_with("/dev/serial") {
return true;
}
false
}