whatcable 0.4.0

Tells you what each USB cable / device on Linux can actually do. Rust port of WhatCable.
Documentation

WhatCable

What can this USB cable actually do?

A command-line tool, and a Rust library, that tell you in plain English what each USB device plugged into your Linux machine can actually do.

WhatCable is a Linux port of WhatCable, a macOS menu-bar app by Darryl Morley. This port covers all USB devices, not just USB-C, while preserving the rich USB-C Power Delivery diagnostics from the original.

The Rust rewrite is forked from Zetaphor/whatcable-linux (originally C++ / CMake).

What it shows

All USB devices

  • Identity: vendor, product name, serial number
  • Speed: negotiated link speed (1.5 Mbps to 20 Gbps)
  • USB version: 1.1, 2.0, 3.0, 3.1, 3.2
  • Power draw: how much power the device is consuming
  • Device type: HID, Audio, Mass Storage, Hub, etc.
  • Driver: which kernel driver is handling the device
  • Topology: hub hierarchy showing what's plugged into what

USB-C ports (additional detail)

  • Port roles: data role (host/device), power role (source/sink)
  • Cable e-marker info: cable speed capability, current rating (3A/5A), active vs passive, cable vendor
  • Charger PDO list: every voltage / current profile the charger advertises, with the active profile highlighted
  • Charging diagnostics: identifies bottlenecks — cable limiting speed, charger undersized, etc.
  • Live wattage (UCSI): voltage_now × current_now, exposed as negotiatedPowerMW in JSON output and via TypeCPowerSupply::negotiated_power_mw() in the library.
  • Partner identity: decoded from PD Discover Identity VDOs

Install

From crates.io

cargo install whatcable

Build from source

Requires Rust 1.85+ (rustup) and libudev development headers (for --watch hotplug support, on by default).

# Ubuntu / Debian
sudo apt install libudev-dev pkg-config

# Fedora
sudo dnf install systemd-devel pkgconf-pkg-config

# Arch / Manjaro
sudo pacman -S --needed systemd-libs pkgconf
cargo build --release                                  # default (with --watch)
cargo build --release --no-default-features --features cli,sysfs    # no libudev
sudo install -Dm755 target/release/whatcable /usr/local/bin/whatcable

Tests

cargo test                                # full suite (requires libudev-dev)
cargo test --no-default-features          # pure-decoder subset, no libudev

CLI usage

whatcable                                  # human-readable summary of every USB device
whatcable --json                           # structured JSON output
whatcable --watch                          # stream updates as devices come and go
whatcable --raw                            # include raw sysfs attributes
whatcable --sysfs-root /tmp/fixture-root   # run against a captured tree (testing)
whatcable --version
whatcable --help

Library usage

The crate has three optional layers, each behind a Cargo feature so consumers pull in only what they need.

Feature Default Adds
(none) always Data types, USB-PD VDO decoders, diagnostics, summaries — IO-free.
sysfs yes Sysfs handle + DeviceManager — Linux /sys enumeration with injectable root.
watch yes libudev hotplug monitor: watch::Watcher + watch::run_loop.
cli yes The whatcable binary (clap + JSON / text rendering).

Library-only consumers can drop the binary deps:

# Pure decoders, no IO, no libudev:
whatcable = { version = "0.3", default-features = false }

# Add /sys enumeration:
whatcable = { version = "0.3", default-features = false, features = ["sysfs"] }

# Add hotplug too:
whatcable = { version = "0.3", default-features = false, features = ["watch"] }

Decode a Cable VDO

use whatcable::pd::{decode_cable_vdo, CableSpeed, CableCurrent};

let v = decode_cable_vdo(2 | (2 << 5) | (3 << 9), false);
assert_eq!(v.speed, CableSpeed::Usb32Gen2);
assert_eq!(v.current_rating, CableCurrent::FiveAmp);
assert_eq!(v.max_watts, 250);

Enumerate the system's USB tree

use whatcable::DeviceManager;

let mut mgr = DeviceManager::new();
mgr.refresh();
for s in mgr.devices() {
    println!("{}: {}", s.headline, s.subtitle);
}

Run a debounced render loop on hotplug events

use std::time::Duration;
use whatcable::{watch::run_loop, DeviceManager};

let mut mgr = DeviceManager::new();
run_loop(Duration::from_millis(500), |_reason| {
    mgr.refresh();
    println!("snapshot has {} devices", mgr.devices().len());
    Ok(())
}).unwrap();

See examples/ for more.

How it works

WhatCable reads three areas of the Linux sysfs filesystem. No root access required for basic info:

sysfs path Provides
/sys/bus/usb/devices/ All USB devices: vendor, product, speed, power, class, interfaces, topology
/sys/class/typec/ USB-C port state: connection, roles, cable e-marker, partner identity
/sys/class/usb_power_delivery/ PD negotiation: PDO list from charger, active profile, PPS ranges
/sys/class/power_supply/ucsi-source-psy-* Live voltage_now × current_now charging readout

Hotplug uses libudev to detect connect/disconnect events in real time.

Cable speed and power decoding follow the USB Power Delivery 3.x spec, ported from the original WhatCable's Swift implementation.

Caveats

  • USB-C / PD data availability varies by hardware. The Type-C connector class and USB PD sysfs interfaces depend on the kernel driver (UCSI, TCPM, platform-specific). Some systems expose full PD negotiation data; others expose only basic port info or nothing at all.
  • Cable e-marker info only appears for cables that carry one. Same as the original — most USB-C cables under 60W are unmarked.
  • WhatCable trusts the e-marker. Counterfeit or mis-flashed cables can lie about their capabilities.
  • Vendor name lookup is not exhaustive. Common vendors are recognized; others show the hex VID.

Credits

Upstream Linux/KDE codebase: Zetaphor/whatcable-linux.

WhatCable is a port of WhatCable by Darryl Morley. The USB Power Delivery decoding logic, charging diagnostics, vendor database, and plain-English summary approach are derived from the original macOS app.

License

MIT