Skip to content

Commit

Permalink
use sysfs directly to power-cycle usb devices instead of uhubctl.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirbaio committed Jan 26, 2024
1 parent ee86dc2 commit 9df8c9e
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 26 deletions.
2 changes: 2 additions & 0 deletions teleprobe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ futures = "0.3.30"
walkdir = "2.4.0"
orion = "0.17.6"
hex = "0.4.3"
nusb = "0.1.4"
libc = "0.2.152"

[target.'cfg(not(windows))'.dependencies]
openssl = { version = "0.10.63", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion teleprobe/src/logutil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub(crate) fn init() {
if let Ok(s) = ::std::env::var("RUST_LOG") {
ui_filter.parse(&s);
} else {
ui_filter.filter_level(LevelFilter::Error);
ui_filter.filter_level(LevelFilter::Warn);
ui_filter.filter_module("teleprobe", LevelFilter::Info);
ui_filter.filter_module("device", LevelFilter::Trace);
}
Expand Down
95 changes: 70 additions & 25 deletions teleprobe/src/probe/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::process::Command;
use std::time::Instant;

use anyhow::{bail, Result};
use anyhow::{anyhow, bail, Result};
use clap::Parser;
use probe_rs::{DebugProbeSelector, Lister, MemoryInterface, Permissions, Probe, Session};

const SETTLE_REPROBE_INTERVAL: std::time::Duration = std::time::Duration::from_millis(100);
const SETTLE_REPROBE_INTERVAL: std::time::Duration = std::time::Duration::from_millis(250);

#[derive(Clone, Parser)]
pub struct Opts {
Expand Down Expand Up @@ -163,29 +162,75 @@ fn open_probe(opts: &Opts) -> Result<Probe> {
}
}

#[cfg(not(target_os = "linux"))]
fn power_reset(probe_serial: &str, cycle_delay_seconds: f64) -> Result<()> {
let output = Command::new("uhubctl")
.arg("-a")
.arg("cycle")
.arg("-d")
.arg(format!("{:.2}", cycle_delay_seconds))
.arg("-s")
.arg(probe_serial)
.output();

match output {
Ok(output) => {
if output.status.success() {
Ok(())
} else {
bail!(
"uhubctl failed for serial \'{}\' with delay {}: {}",
probe_serial,
cycle_delay_seconds,
String::from_utf8_lossy(&output.stderr)
)
}
anyhow::bail!("USB power reset is only supported on linux")
}

#[cfg(target_os = "linux")]
fn power_reset(probe_serial: &str, cycle_delay_seconds: f64) -> Result<()> {
use std::ffi::CString;
use std::fs::File;
use std::io::Write;
use std::os::fd::FromRawFd;
use std::os::unix::ffi::OsStrExt;
use std::thread::sleep;
use std::time::Duration;

let dev = nusb::list_devices()?
.find(|d| {
let serial = d.serial_number().unwrap_or_default();

serial == probe_serial || to_hex(serial) == probe_serial
})
.ok_or_else(|| anyhow!("device with serial {} not found", probe_serial))?;

let port_path = dev.sysfs_path().join("port");
let port_path = CString::new(port_path.as_os_str().as_bytes()).unwrap();

// The USB device goes away when we disable power to it.
// If we open the port dir we can keep a "handle" to it even if the device goes away, so
// we can write `disable=0` with openat() to reenable it.
let port_fd = unsafe { libc::open(port_path.as_ptr(), libc::O_DIRECTORY | libc::O_CLOEXEC) };
if port_fd < 0 {
return Err(std::io::Error::last_os_error().into());
}

// close port_fd on function exit
struct CloseFd(i32);
impl Drop for CloseFd {
fn drop(&mut self) {
unsafe { libc::close(self.0) };
}
Err(e) => bail!("uhubctl failed: {}", e),
}
let _port_fd_close = CloseFd(port_fd);

let disable_path = CString::new("disable").unwrap();

// disable port power
let disable_fd = unsafe { libc::openat(port_fd, disable_path.as_ptr(), libc::O_WRONLY | libc::O_TRUNC) };
if disable_fd < 0 {
return Err(std::io::Error::last_os_error().into());
}
unsafe { File::from_raw_fd(disable_fd) }.write_all(b"1")?;

// sleep
sleep(Duration::from_secs_f64(cycle_delay_seconds));

// enable port power
let disable_fd = unsafe { libc::openat(port_fd, disable_path.as_ptr(), libc::O_WRONLY | libc::O_TRUNC) };
if disable_fd < 0 {
return Err(std::io::Error::last_os_error().into());
}
unsafe { File::from_raw_fd(disable_fd) }.write_all(b"0")?;

Ok(())
}

fn to_hex(s: &str) -> String {
use std::fmt::Write;
s.as_bytes().iter().fold(String::new(), |mut s, b| {
let _ = write!(s, "{b:02X}"); // Writing a String never fails
s
})
}

0 comments on commit 9df8c9e

Please sign in to comment.