Quick script to add BDF to lsusb output to find controller for devices on KVM/HUB

For what it may be worth to others, I became sick of using a manual process to find the PCI controller (BDF) associated with a USB device that is connected via an internal or external hub, including those in a KVM

The below script should work as-is and will add an additional field to the output of lsusb that provides the BDF for the device - it will be the actual PCI controller that the device ultimately connects to the system through

This is most useful when working with a fresh install on new hardware, especially when some devices are connected via hubs (thanks @apparatus for helping to determine the subtlety with lsusb output for hub-connected devices)

Example use/output (run in dom0 only):

[user@dom0 ~]$ lsbdf
BDF: f4:00.4 Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
BDF: f4:00.4 Bus 001 Device 002: ID 0b95:772b ASIX Electronics Corp. AX88772B
...

Disclaimer: This is provided “as-is”. It works on my computer, but may not work on yours. This, and that, and yada blablabla. Shortened version:

#!/bin/bash
# Based on manual steps @ https://www.qubes-os.org/doc/how-to-use-usb-devices/
set -eu
lsusb | while IFS= read -r l; do
  b=$(printf "%d" $(echo $l | grep -Po '(?<=Bus )\d+' | sed -e 's|0||g'))
  dv=$(printf "%d" $(echo $l | grep -Po '(?<=Device )\d+' | sed -e 's|0||g'))
  ds=$(echo $l | grep -Po '(?<=[0-9a-f]{4}:[0-9a-f]{4} ).*')
  f=$(readlink /sys/bus/usb/devices/usb$b)
  p=$(dirname "$f")
  bf=$(basename $p | cut -d ':' -f 2-)
  printf "BDF: %s %s\n" "$bf" "$l"
done

If this is not appropriate or belongs in another forum section, please notify me and I will do my best to resolve the issue

Thank you

The BDF is present in the lsusb --verbose output:

lsusb -v 2>/dev/null | grep '^Bus\|iSerial'

The BDF is present in the lsusb --verbose output:

lsusb -v 2>/dev/null | grep '^Bus\|iSerial'

That is surely more convenient than the sillyness I was doing with readlink

Though I do like being able to have all of it on one line for grep purposes

Counterpoint: lsusb -v 2>/dev/null | grep '^Bus\|iSerial' | grep "Logitech Stupid Mouse" -A 1 is friendly enough for 99.99% of human beings

Should I remove the post, I wonder? I can’t see anyone wanting to copy/paste + pull the script I have into their dom0 when they can manage with what you suggested

EDIT: At first glance, I don’t believe posts can be deleted, if anyone knows otherwise and can advise, I will probably remove the noise

You can do it like this:

lsusb -v 2>/dev/null | grep '^Bus\|iSerial' | sed -z 's/[[:space:]]\+iSerial[[:blank:]]\+[[:digit:]]\+[[:blank:]]\+/\tBDF /g'
1 Like

So I’m noticing now, the more straightforward way you recommended works great for directly attached USB devices, but it doesn’t seem to work for the devices I actually needed to look up - these are devices connected via standalone external USB hubs or KVM-hosted USB hubs

Check out what I see for those - I assume it’s only slightly unexpected

For a keyboard, mouse and ASIX-based 10/100/1000 NIC I have, connected via a KVM (keyboard, mouse) and a hub (NIC), I see only null or nonsense iSerial values:

$ sudo lsusb -v | grep '^Bus\iSerial'
...
Bus 001 Device 002: ID 0b95:772b ASIX Electronics Corp. AX88772B
  iSerial                 3 217DFC
Bus 003 Device 010: ID 046d:c52b Logitech, Inc. Unifying Receiver
  iSerial                 0 
Bus 003 Device 007: ID 0c45:7681 Microdia USB Keyboard
  iSerial                 0 
...

Contrasted with the more convoluted approach I used in the script:

BDF: f4:00.4 Bus 001 Device 002: ID 0b95:772b ASIX Electronics Corp. AX88772B
BDF: 05:00.4 Bus 003 Device 010: ID 046d:c52b Logitech, Inc. Unifying Receiver
BDF: 05:00.4 Bus 003 Device 007: ID 0c45:7681 Microdia USB Keyboard

I wrote this script a week or so ago, it must be that this is the reason I did it. I think you can do a tree listing and trace it up, but that’s what started to annoy me before I wrote this :slight_smile:

With this more clearly in light, it might be more appropriate for the script to call it something like “Root BDF”, “Authoritative BDF”, “Parent BDF”, etc. But meh.

Are you aware of any ways to work around this in one command? Effectively identifying the real USB controller behind an external USB hub?

I’m not as concerned with combining the two lines into one, though I do appreciate the sed expression. I’ve never done multi-line stuff with sed before

Note: There’s something slightly off with the sed expression by the way- it gives me no output, I didn’t dig into why- I assume a typo or other minor mistake

Don’t feel obligated to put time into investigating a solution for either of these issues, it’s not a huge problem for me. Maybe it will be helpful for someone else though

No easy way to do it in one line.
Basically you can read the lsusb -v command output, get the iSerial value for root hub device and then add this value to all the devices on the same bus.

It works for me:

$ lsusb -v 2>/dev/null | grep '^Bus\|iSerial' | sed -z 's/[[:space:]]\+iSerial[[:blank:]]\+[[:digit:]]\+[[:blank:]]\+/\tBDF /g'
Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd QEMU Tablet	BDF 42
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub	BDF 0000:00:05.0

It’s possible there’s a typo on my side

Well, now I’m curious what a QEMU Tablet is :slight_smile:

It’s a virtual pointer device used by QEMU in VM.

Ah, I see

I’ve changed the name of the topic to include something about “KVM/Hub”, so maybe it will be useful for those who have devices on KVM/hubs that are too lazy to semi-manually follow the chain to the controller but not so lazy that copy/pasting this is a show-stopper

Thanks for your input, I’m happy you chimed in because I wouldn’t have realized the subtle difference in behavior/output of lsusb in the hub case

Also, you’re probably not surprised to know that you’re correct, the sed expression was correct, it worked when I copied/pasted - I had triple-checked it when typing manually into the dom0 terminal, but clearly made a mistake