Back to Serial Studio

Raw USB Driver (Pro)

doc/help/Drivers-USB.md

4.0.111.6 KB
Original Source

Raw USB Driver (Pro)

The Raw USB driver talks to USB devices directly through libusb, bypassing the operating system's class drivers. It is the right driver when a device exposes vendor-specific bulk, control, or isochronous endpoints rather than presenting itself as a serial port (CDC), an HID device, or a mass-storage volume.

If the device shows up as a virtual COM port, use the UART driver. For gamepads, joysticks, or HID firmware, use the HID driver. This page covers everything else: logic analysers, oscilloscopes, custom data-acquisition boards, vendor-specific scientific instruments, and any device whose datasheet says "uses bulk endpoints".

What is USB, really?

USB is layered. From the bottom up:

  • Physical layer. Differential pair (D+/D-) plus power and ground. Speeds: 1.5 Mbps (Low Speed), 12 Mbps (Full Speed), 480 Mbps (High Speed), 5 Gbps (USB 3.0 SuperSpeed), and faster.
  • Packet layer. A host-driven token/data/handshake protocol. Devices never initiate transfers; the host always polls.
  • Transfer layer. Four transfer types (control, bulk, interrupt, isochronous) sit on top of packets.
  • Class layer. Standard classes (HID, CDC, Mass Storage, Audio, Video) define the device category.

Almost every consumer USB device implements one of the standard classes. The OS bundles a generic driver for each class, so plugging in a USB-CDC microcontroller exposes a COM port without any extra install.

Vendor-specific USB devices skip the standard classes. They expose endpoints with custom semantics that the OS does not know how to interpret. libusb is a userspace library that lets an application open such a device and talk to its endpoints directly.

Endpoints and transfer types

Every USB device has at least one endpoint (endpoint 0, the control endpoint). Most devices have several. Endpoints are unidirectional buffers identified by a number and a direction (IN = device to host, OUT = host to device).

Each endpoint is configured for one of four transfer types:

mermaid
flowchart TB
    USB[USB transfer types]
    USB --> CTRL[Control
EP0 always; bidirectional
config, descriptors, setup]
    USB --> BULK[Bulk
large data, no timing guarantee
guaranteed delivery]
    USB --> INT[Interrupt
small periodic data
guaranteed latency]
    USB --> ISO[Isochronous
continuous streaming
no retransmission, no checksum]
  • Control transfers (Endpoint 0) carry configuration data: reading device descriptors, selecting interfaces, sending vendor commands. Always bidirectional through the same endpoint pair.
  • Bulk transfers carry large amounts of data with error detection (CRC) and guaranteed delivery but no bandwidth guarantee. Used for printers, mass storage, and most raw data-acquisition devices. The host fits bulk transfers around higher-priority traffic.
  • Interrupt transfers carry small amounts of data with a guaranteed maximum latency (the polling interval set in the endpoint descriptor). Used for HID devices and other small periodic data. Despite the name there are no real hardware interrupts; the host polls.
  • Isochronous transfers carry time-sensitive streaming data (audio, video) with guaranteed bandwidth but no retransmission. A corrupted packet is lost. Used for USB audio interfaces and webcams.

For Serial Studio's USB driver, the relevant types are bulk (the default, for streaming captured data), isochronous (for high-rate continuous streams where dropped frames are acceptable), and control (vendor-specific commands, available in Advanced mode).

Descriptors

A USB device describes itself through a tree of descriptors:

mermaid
flowchart TD
    Device[Device Descriptor
VID, PID, USB version]
    Device --> Cfg[Configuration Descriptor
power requirements]
    Cfg --> If1[Interface Descriptor
logical function]
    If1 --> EP1[Endpoint Descriptor
EP1 IN, bulk, 64 bytes]
    If1 --> EP2[Endpoint Descriptor
EP2 OUT, bulk, 64 bytes]
    Cfg --> If2[Interface Descriptor
another function]
    If2 --> EP3[Endpoint Descriptor
EP3 IN, interrupt, 8 bytes]

The host queries the device for its descriptor tree on enumeration, decides what to do with it, and binds drivers accordingly. A vendor-specific bulk device usually has one configuration, one interface, and a couple of endpoints (one IN for reading, one OUT for writing).

VID and PID

Every USB device has a Vendor ID (VID) and Product ID (PID), both 16-bit. VIDs are assigned by the USB-IF; PIDs are chosen by the vendor. Together they uniquely identify a device model. Serial Studio lists USB devices as VID:PID, followed by the manufacturer and product strings when the device reports them (for example 1234:5678 – Acme Logic Analyzer).

How Serial Studio uses it

The USB driver wraps libusb. The setup flow is:

  1. Pick a device from the USB Device dropdown. The list updates on libusb hotplug events, with a 2-second rescan fallback on platforms without hotplug support.
  2. Pick a Transfer Mode:
    • Bulk Stream (default): synchronous bulk IN/OUT. Works for most devices.
    • Advanced (Bulk + Control): bulk transfers plus vendor-specific control transfers. A confirmation dialog appears before enabling this, because vendor-specific control writes can do anything the device firmware allows, up to and including bricking the device.
    • Isochronous: asynchronous isochronous transfers for fixed-rate streaming.
  3. Connect. The IN Endpoint and OUT Endpoint dropdowns appear once connected, read from the device's active configuration descriptor and labeled like EP 0x81 – Bulk IN (IF0, max 64 B). The first endpoint matching the transfer mode is selected automatically; OUT Endpoint defaults to None (Read-only) when no matching OUT endpoint exists.
  4. In Isochronous mode, a Max Packet Size field (1 to 49152 bytes, default 1024) also appears once connected. Serial Studio pre-fills it with the endpoint's reported maximum packet size; override it only when the device datasheet says otherwise.

Threading

The USB driver runs two dedicated threads:

  • Event thread. Pumps libusb's event loop. Required by libusb's async API and hotplug callbacks.
  • Read thread. Issues synchronous bulk reads (64 KB buffer, 100 ms timeout) in Bulk Stream and Advanced modes. In Isochronous mode, data arrives instead through a pool of eight async transfers (eight packets each) whose completion callbacks run on the event thread.

Each completed transfer carries a timestamp captured at completion time and is queued to the main thread for FrameReader processing. See Threading and Timing Guarantees.

Permissions and OS specifics

USB device access is permission-controlled differently on each OS:

  • Linux. By default only root can open arbitrary USB devices. Add a udev rule granting the user access to a specific VID:PID:
    /etc/udev/rules.d/99-myusbdevice.rules:
    SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
    
    Then run sudo udevadm control --reload-rules && sudo udevadm trigger. Serial Studio enables libusb's auto-detach on Linux, so a kernel class driver bound to the interface (for example cdc_acm) is detached automatically when the interface is claimed and re-attached on release.
  • Windows. A WinUSB driver must be installed for the device. The standard Windows class drivers (HID, CDC, and so on) hold the device open and prevent libusb from claiming it. Use Zadig to replace the driver with WinUSB for the target VID:PID.
  • macOS. Devices that are not already claimed by an OS class driver can be opened directly. When macOS has bound a kernel driver, opening or claiming the interface fails; Serial Studio does not attempt to detach system drivers.

For step-by-step setup, see the Protocol Setup Guides, Raw USB section.

Remote API commands

The TCP API and the in-app AI assistant configure this driver through the io.usb.* scope:

CommandParametersNotes
io.usb.listDevicesnoneReturns devices array and selectedIndex (index 0 is the "Select Device" placeholder)
io.usb.setDeviceIndexdeviceIndex (integer)Selects a device by list index
io.usb.setTransferModemode (0 = Bulk Stream, 1 = Advanced, 2 = Isochronous)
io.usb.setInEndpointIndexendpointIndex (integer)Endpoint lists populate when the device connects
io.usb.setOutEndpointIndexendpointIndex (integer)Index 0 is "None (Read-only)"
io.usb.setIsoPacketSizesize (1 to 49152 bytes)
io.usb.getConfignoneReturns mode, indices, packet size, and both endpoint lists

Transport details and command safety tiers are in the API Reference.

Common pitfalls

  • Device not listed. On Linux, the udev rule is missing or has not taken effect. lsusb shows the device; lsusb -v -d VID:PID shows its descriptor. If lsusb works but Serial Studio does not see the device, it is a permissions problem.
  • Device is listed but will not open. Another driver or application has already claimed it. On Windows, switch to WinUSB through Zadig. On Linux, kernel class drivers are detached automatically when the interface is claimed; if open still fails, check dmesg | grep usb for hints and close other applications using the device.
  • No data on the IN endpoint. Confirm the endpoint number. Vendor documentation always specifies which endpoint streams data; pick the matching one in the dropdown. Also confirm the device is streaming; some devices need a vendor-specific control write to start.
  • Bulk reads time out. Timeouts are not errors: the read loop retries every 100 ms and forwards whatever the device delivers. A connection that stays open with nothing arriving means the device is not sending on the selected IN endpoint.
  • Isochronous mode drops samples. Some hardware/OS combinations cap isochronous bandwidth. Match Max Packet Size to the endpoint's reported maximum; typical values are 192 B for full-speed audio and 1024 B for high-speed devices.
  • Advanced (Bulk + Control) mode warning. Take it seriously. Vendor-specific control transfers can issue any command the firmware understands, including writing to flash, changing calibration, and arbitrary memory writes. Only enable this mode with a full understanding of what is being sent.
  • Permission denied on Linux, even with udev. The udev rule did not reload, or the device was not unplugged and replugged after the rule was added. Trigger a re-enumeration with sudo udevadm trigger.

Further reading

See also