Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

USBview

USBview app icon

USB controller, port, hub and device inspector for macOS

Descriptor detail, power budgets, connector negotiation and port fault counters, read from the IORegistry and presented as a navigable tree.


Version Swift SwiftUI IOKit Platform Status

USBview main window

Overview

macOS provides three ways to inspect USB, each with a significant gap:

  • System Information is shallow and read-only, and its USB pane has lost detail across recent releases.
  • system_profiler SPUSBDataType returns an empty array on macOS 26. The key was replaced by SPUSBHostDataType, and a good deal of existing tooling still queries the old one.
  • ioreg exposes everything, but with no structure or interpretation.

The IORegistry also carries data that none of the three surfaces: per-port overcurrent and enumeration-failure counters, link error counts, USB-C transport negotiation, and per-power-state dwell times. USBview reads and presents all of it.

The intended audience is developers and IT staff diagnosing unreliable hardware, though the interface does not assume prior knowledge of USB descriptors.


Features

Accurate topology Controllers, physical ports, hubs and devices, correctly nested. A USB-C connector appears as one port rather than the two registry nodes, USB 2 and USB 3, that macOS reports.
Link speed diagnosis When a device advertises SuperSpeed in its BOS descriptor but negotiates a slower link, USBview reports the discrepancy. The cause is almost always a USB 2 cable, an intermediate hub, or a port wired for USB 2 only. No other macOS tool reports this.
Port health Connect count, overcurrent events, enumeration and address failures, link errors, and time spent in each power state. Non-zero fault counters are flagged.
USB-C connector state (Apple Silicon) What the connector supports against what it has negotiated: USB 2, USB 3, Thunderbolt or DisplayPort, with cable type and connector-level overcurrent.
Storage and network Volumes, capacity and free space for USB drives. IP addresses, MAC address, link rate and traffic counters for USB network adapters. Both update live.
Accurate power reporting Requested power and port budget, each labelled for what it is. macOS exposes no measured draw, so USBview reports none. See Limitations.
Internal device filtering (Intel) The T2 places the camera, keyboard, Touch Bar, headset and ambient light sensor on a virtual USB bus. On affected Macs these permanently attached devices outnumber the physical ones. Hidden by default, with one setting to restore them.
Headless operation The enumeration core runs without a window through make dump, --watch and --descriptors. Output is JSON or plain text, suitable for piping, diffing, or attaching to a bug report.
Offline by default Vendor and product names come from a bundled usb.ids. A single opt-in setting can access the network, and it ships disabled.

Screenshots

Light Dark
USBview in light mode USBview in dark mode
USBview settings, Privacy tab

What it reports

Device detail. Vendor, product and serial strings, VID/PID, USB specification version, negotiated link speed, device class, address, location ID, configurations, interfaces, alternate settings, endpoints, SuperSpeed burst sizes, class-specific descriptors and driver binding. Devices that enumerated but were never configured are flagged, which normally indicates that no driver matched.

Power. Requested power (bMaxPower), the port's current budget awake and asleep, and bus- against self-powered status from the configuration descriptor's bmAttributes.

Connector (Apple Silicon). Separates a slow device from a saturated connector. A connector running DisplayPort is using the lanes SuperSpeed requires, which is a different problem from a bad cable, and the two are reported distinctly. A port with a display attached is never reported as empty.

Internal devices (Intel). A Mac with a T2 attaches its own camera, keyboard and trackpad, Touch Bar display and backlight, headset and ambient light sensor to a virtual USB bus, alongside the T2 itself. On a MacBookPro15,2 that accounts for seven of the machine's thirteen USB devices, all permanently attached and unreachable by cable. They are hidden by default, and Settings → Sidebar → Show internal devices restores them.

Their ports are labelled Virtual rather than assigned a USB generation, because the registry node for a virtual port carries no protocol flags at all. The devices on it do negotiate 480 Mb/s, but that is the device's speed rather than the port's capability, and USBview does not blur that distinction elsewhere. Apple Silicon has no equivalent bus, since its built-in peripherals are not on USB, so the setting is disabled there with that explanation rather than left active and inert.

Storage. Disks, partitions, volumes, capacity, free space, filesystem and mount points, updating live as volumes mount and unmount. Figures match Disk Utility.

Network. Interface name, MAC address, Ethernet link rate and state, IPv4 and IPv6 addresses with prefix lengths, router, MTU and traffic counters. The Ethernet link rate is kept distinct from the USB one: a 2.5G adapter on a 5 Gb/s USB connection is doing 2.5 Gb/s of Ethernet, and conflating the two misattributes the bottleneck.

Diagnostic log. Attach and detach events, enumeration failures and action results, collapsed at the foot of the detail pane until expanded.

Native throughout. SwiftUI, following the system appearance, fully keyboard navigable with ⌘F to search and arrow keys to move, and labelled for VoiceOver.


Requirements

  • macOS 15 Sequoia or later
  • Xcode 16 or later (macOS 15 SDK)
  • XcodeGen, installed with brew install xcodegen

Getting started

git clone https://github.com/fuckmaz/USBview.git
cd USBview
make run

This generates the Xcode project, builds it, and launches the app. No .xcodeproj is checked in; it is generated from project.yml, so every build starts from the same specification.

Build and test

make build
make test

Make targets

Command Purpose
make build Build the app
make run Build and launch
make test Run the unit tests
make dump Print the USB topology as JSON, without a window
make tree Print the USB topology as an indented tree, without a window
make generate Regenerate USBview.xcodeproj from project.yml
make clean Remove build artefacts
make help List every target

Headless mode

Core/ never imports SwiftUI, so everything the app knows is reachable from a terminal:

Command Output
USBview --dump-json The full topology as JSON
USBview --dump-tree The topology as an indented tree
USBview --watch A live stream of attach and detach events
USBview --descriptors Full descriptors for every device, comparable to lsusb -v
USBview --storage USB disks and their volumes
USBview --network USB network adapters and their addresses
USBview --dump-registry The raw IORegistry subtree, for bug reports

None of these are filtered by the app's display settings. Hiding internal devices changes the window and never a capture, since a capture missing a third of the bus would mislead whoever reads it.

make tree prints the topology as a tree:

Mac17,2 · Version 26.6.2 (Build 25G83)
3 controllers · 3 ports · 1 device

USB 3.1 Bus · AppleT8142USBXHCI · 0x01000000
└── Port 2 — USB 2.0 + USB 3.x · 3000 mA budget
    └── USB 10/100/1G/2.5G LAN · 0BDA:8156 · 5 Gb/s · Composite · Realtek

--watch prints the topology and then narrates every attach, detach and re-enumeration, with timings:

17:44:12.927     Found 3 controllers, 3 ports, 1 device
                Enumerated in 5.3 ms
17:44:19.104  +  Attached: SanDisk Ultra on Port 1
                ID 0781:5583 · SuperSpeed · 5 Gb/s

This is intended for intermittent faults: the stream can be left running and reviewed afterwards.

Reporting a bug

USBview --dump-registry > capture.json writes the raw IORegistry subtree. Attaching one to an issue makes the problem reproducible on hardware the maintainer has no access to, and the test suite runs directly against these captures.


Installing

Prebuilt disk images are on the releases page:

Download Target
USBview-<version>-Universal.dmg Any Mac. Recommended.
USBview-<version>-AppleSilicon.dmg Apple Silicon only (M1 and later)
USBview-<version>-Intel.dmg Intel only

The universal image runs everywhere and is about a megabyte larger. The single-architecture builds exist for anyone wanting the smaller file, or needing to pin an architecture while diagnosing something.

USBview is not code signed, as there is no Developer ID behind it, so macOS quarantines a downloaded build and refuses to open it the first time. Clearing the attribute once resolves this:

xattr -dr com.apple.quarantine /Applications/USBview.app

Apply that only to a binary built locally or otherwise trusted. Building from source avoids the step entirely, and every release publishes SHA-256 checksums for verifying a download instead.

The Intel build is cross-compiled on Apple Silicon and smoke-tested under Rosetta rather than on Intel hardware. The enumeration core is covered by tests against a captured Intel-style xHCI registry, and a bug report with USBview --dump-registry attached is the fastest route to fixing anything that slips through.


Limitations

USBview reports what macOS exposes, and documents what it does not. Several capabilities that a USB inspector might reasonably be expected to offer have no public API at any privilege level:

Reason
No live current draw macOS reports no measured per-device current anywhere in the IORegistry. USBview shows the device's requested power (bMaxPower) and the port's budget (kUSBWakePortCurrentLimit), each labelled as such. It is not an ammeter and does not present itself as one.
No port disable or enable No public API exists.
No USB controller restart No public API exists, and SIP blocks kext-level workarounds even as root.
Device reset works, and is destructive Testing confirmed that IOUSBHostDevice.reset() succeeds even on a device with a matched kernel driver, so this is a genuine re-enumeration rather than a no-op. On a drive with a mounted volume it is equivalent to unplugging the cable, so USBview asks for confirmation and says so. Refusals are reported rather than swallowed.

The first three still appear in the interface, dimmed and marked with an information symbol that explains the underlying restriction when clicked. An explanation is more useful than an unexplained absence, particularly for the capabilities people most often open a USB inspector to find.

A privileged helper daemon was considered and rejected. SIP prevents it from unlocking any of the above, so it would add signing and lifecycle complexity for no gain in capability.


Project layout

┌──────────────────────────────────────────────────────────┐
│  Sources/UI      Sidebar · detail pane · settings        │  ← SwiftUI
├──────────────────────────────────────────────────────────┤
│  Sources/App     Entry point · headless CLI · app state  │  ← bridge
├──────────────────────────────────────────────────────────┤
│  Sources/Core    IOKit · descriptors · storage · network │  ← no SwiftUI
│                  actions · metadata · settings           │
└──────────────────────────────────────────────────────────┘
Path Role
project.yml XcodeGen specification, and the source of truth for the build
Makefile generate, build, run, test, dump
Sources/Resources/ Assets, app icon, bundled usb.ids
Tests/CoreTests/ Fixture-driven, requiring no hardware

The architectural constraint behind that split: Core/ never imports SwiftUI, and everything it produces must be reachable through make dump. That is what keeps the enumeration core testable without the interface, and what makes the headless commands possible at all.


Privacy

USBview operates entirely offline. Vendor and product names come from a bundled copy of usb.ids.

One opt-in, off-by-default setting allows USBview to access the network, for downloading vendor logos. While enabled, USBview sends the manufacturer's company name, resolved locally from the bundled usb.ids, to wikidata.org and commons.wikimedia.org. No device identifier, product ID, serial number or machine information is transmitted.

While disabled, no network client is ever constructed. The gate is structural rather than a flag checked before sending, and the test suite asserts that zero clients are built rather than merely zero requests. lsof -i against the running app under default settings shows no connections at all.

Results are cached on disk for a month, including negative results, and the Privacy tab lists what is stored alongside a button to clear it.


Contributing

Issues and pull requests are welcome. Run make test before opening a pull request. CI runs the same build, test and headless-dump steps on every push.


Credits

  • Written by maz, github.com/fuckmaz
  • USB vendor and product names from usb.ids, maintained by Stephen J. Gowdy. The upstream project distributes it under the GNU General Public Licence v2 or later, or the 3-clause BSD licence. A copy is bundled at Sources/Resources/usb.ids, and the About panel reports which version.
  • No vendor logos are bundled. Company logos are trademarks, so USBview draws vendor initials instead. Sources/Resources/VendorLogos/README.md documents how to add logos to a local build.
  • Built with SwiftUI, IOKit, DiskArbitration and SystemConfiguration.

License

MIT. See LICENSE.

Releases

Packages

Contributors

Languages