use super::DAPLinkDevice;
use crate::{
probe::{DebugProbeInfo, DebugProbeType, ProbeCreationError},
DebugProbeSelector,
};
use rusb::{Device, UsbContext};
use std::time::Duration;
pub fn list_daplink_devices() -> Vec<DebugProbeInfo> {
match rusb::Context::new().and_then(|ctx| ctx.devices()) {
Ok(devices) => devices
.iter()
.filter_map(|device| get_daplink_info(&device))
.collect(),
Err(_) => match hidapi::HidApi::new() {
Ok(api) => api.device_list().filter_map(get_daplink_hid_info).collect(),
Err(_) => vec![],
},
}
}
fn get_daplink_info(device: &Device<rusb::Context>) -> Option<DebugProbeInfo> {
let timeout = Duration::from_millis(100);
let d_desc = device.device_descriptor().ok()?;
let handle = device.open().ok()?;
let language = handle.read_languages(timeout).ok()?[0];
let prod_str = handle
.read_product_string(language, &d_desc, timeout)
.ok()?;
let sn_str = handle
.read_serial_number_string(language, &d_desc, timeout)
.ok();
if prod_str.contains("CMSIS-DAP") {
Some(DebugProbeInfo {
identifier: prod_str,
vendor_id: d_desc.vendor_id(),
product_id: d_desc.product_id(),
serial_number: sn_str,
probe_type: DebugProbeType::DAPLink,
})
} else {
None
}
}
fn get_daplink_hid_info(device: &hidapi::DeviceInfo) -> Option<DebugProbeInfo> {
if let Some(prod_str) = device.product_string() {
if prod_str.contains("CMSIS-DAP") {
return Some(DebugProbeInfo {
identifier: prod_str.to_owned(),
vendor_id: device.vendor_id(),
product_id: device.product_id(),
serial_number: device.serial_number().map(|s| s.to_owned()),
probe_type: DebugProbeType::DAPLink,
});
}
}
None
}
pub fn open_v2_device(device: Device<rusb::Context>) -> Option<DAPLinkDevice> {
let timeout = Duration::from_millis(100);
let d_desc = device.device_descriptor().ok()?;
let vid = d_desc.vendor_id();
let pid = d_desc.product_id();
let mut handle = device.open().ok()?;
let language = handle.read_languages(timeout).ok()?[0];
let c_desc = device.config_descriptor(0).ok()?;
for interface in c_desc.interfaces() {
for i_desc in interface.descriptors() {
match handle.read_interface_string(language, &i_desc, timeout) {
Ok(i_str) if !i_str.contains("CMSIS-DAP") => continue,
Err(_) => continue,
Ok(_) => (),
}
let n_ep = i_desc.num_endpoints();
if n_ep < 2 || n_ep > 3 {
continue;
}
let eps: Vec<_> = i_desc.endpoint_descriptors().collect();
if eps[0].transfer_type() != rusb::TransferType::Bulk
|| eps[0].direction() != rusb::Direction::Out
{
continue;
}
if eps[1].transfer_type() != rusb::TransferType::Bulk
|| eps[1].direction() != rusb::Direction::In
{
continue;
}
let out_ep = eps[0].address();
let in_ep = eps[1].address();
match handle.claim_interface(interface.number()) {
Ok(()) => {
log::debug!("Opening {:04x}:{:04x} in CMSIS-DAPv2 mode", vid, pid);
return Some(DAPLinkDevice::V2 {
handle,
out_ep,
in_ep,
});
}
Err(_) => continue,
}
}
}
log::debug!(
"Could not open {:04x}:{:04x} in CMSIS-DAP v2 mode",
vid,
pid
);
None
}
pub fn open_device_from_selector(
selector: impl Into<DebugProbeSelector>,
) -> Result<DAPLinkDevice, ProbeCreationError> {
let selector = selector.into();
if let Ok(devices) = rusb::Context::new().and_then(|ctx| ctx.devices()) {
for device in devices.iter() {
let d_desc = match device.device_descriptor() {
Ok(d_desc) => d_desc,
Err(_) => continue,
};
let handle = match device.open() {
Ok(handle) => handle,
Err(_) => continue,
};
let sn_str = handle.read_serial_number_string_ascii(&d_desc).ok();
drop(handle);
if d_desc.vendor_id() == selector.vendor_id
&& d_desc.product_id() == selector.product_id
&& sn_str == selector.serial_number
&& get_daplink_info(&device).is_some()
{
if let Some(device) = open_v2_device(device) {
return Ok(device);
}
}
}
}
let vid = selector.vendor_id;
let pid = selector.product_id;
let sn = &selector.serial_number;
log::debug!(
"Attempting to open {:04x}:{:04x} in CMSIS-DAP v1 mode",
vid,
pid
);
let hid_device = match sn {
Some(sn) => hidapi::HidApi::new().and_then(|api| api.open_serial(vid, pid, &sn)),
None => hidapi::HidApi::new().and_then(|api| api.open(vid, pid)),
};
match hid_device {
Ok(device) => {
match device.get_product_string() {
Ok(Some(s)) if s.contains("CMSIS-DAP") => Ok(DAPLinkDevice::V1(device)),
_ => {
Err(ProbeCreationError::NotFound)
}
}
}
Err(_) => Err(ProbeCreationError::NotFound),
}
}