Determining physical topology of hotplugged USB devices with udev

As part of a bigger project of mine, I wanted to answer the following questions: How can one tell which physical port a hotplugged usb-device is plugged into on Linux? Can one do this when the device is plugged into an usb-hub (i.e: non-root hub) too? Since this is potentially useful to others out there, I share my findings here.

The usual “lsusb” or “lsusb -v” gives no indication that the kernel knows about the physical topology of the devices connected to an USB bus. However “lsusb -t” does indeed show the topology, so the kernel does know about it and make this info available to user-space. So, how to use this in udev? Prodding some USB storage class devices with udevinfo, shows that there is no attribute that gives this information directly. But, the DEVPATH attribute for USB devices actually contains this information.

For instance, an USB mass storage device connected to port 2 of the root-hub with bus number 2 has the following devpath: “/devices/pci0000:00/0000:00:1d.7/usb2/2-2/2-2:1.0/host12/target12:0:0/12:0:0:0/block/sdb” and a USB mass storage device connected to port 5 of the root-hub with bus number 1 has the devpath “/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host5/target5:0:0/5:0:0:0/block/sdb”. It becomes a bit confusing when there are USB hubs involved: “/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1.1/2-1.1.1/2-1.1.1:1.0/host16/target16:0:0/16:0:0:0/block/sdb” refers to a device plugged into port 1 of an USB hub plugged into port 1 of a second hub which is plugged into port 1 of the root-hub with bus number 2.

We see that in all cases the information about the physical topology is in the substring just before “:1.0”; “2-2”, “1-5” and “2-1.1.1”, respectively for the given examples. And that the format is “$busnumber-$port” for the simple case of connecting the device directly to the root-hub, and $busnumber-$port1.$port2  ->   .$portN for the general case, with N-1 usb-hubs between the device and the root-hub.

Just to show that this works, here is a quick and dirty udev rule and helper python script which parses the devpath and creates a symlink to the first partition of a USB mass storage device under /dev/usb based on the bus and port the device is connected to. Results might look like this:

tree /dev/usb/                                                                    Thu Jul 29 17:34:38 2010

/dev/usb/
`-- 2
    `-- 2
        |-- 2 -> ../../../sdc1
        `-- 4
            `-- 4 -> ../../../../sdb1

A more useful thing to do might for instance be passing through all devices connected to a certain physical usb-port to a certain virtual machine. Or maybe letting an embedded system responding differently to USB devices plugged into a physically secured root-hub than to one that is available to the user.

Introducing MassifG 0.1

MassifG is an application for visualizing the output of valgrinds massif tool. I am writing it as part of my trainee program here at Openismus. MassifG is free software, available under GPLv3. You will find the git repository at gitorious, and the tarball release for 0.1 here. Packages for Arch are available in AUR.

Used together with massif MassifG gives you a nice, simple graph of allocated heap memory over time which you can use to help evaluate if your program is leaking memory or not:

Current status

With release 0.1 MassifG can:MassifG 0.1

  • Parse and display a simple graph from massif output.
  • Open files selected on the command line or from the UI.
  • Print out the graph to a printer or file. This code was contributed by Murray Cumming – thanks!

I was hoping that I could make version 0.1 the “minimally useful” release, but I have to admit that it is not quite there yet. Instead I nickname release 0.1 “getting it out there”. The main issue is that there is massif output that breaks the graph, see under tests/ for an example.

Implementation

MassifG is written in C, and uses GTK+ for its graphical user interface. Once the ability to export the graph is implemented, it should be easy to allow the application to build and run without GTK, if anyone wants to do that.

The parser is a simple state-machine. It might have been wiser to use bison+flex, and I’ll reevaluate that when I need to implement parsing of the detailed output.

The graph functionality is implemented using Cairo and Pango. While Cairo and Pango are excellent technologies with nice APIs, they are quite low-level and that means having to care about all the details. As that brings no benefit to this application I might rewrite the graphing functionality to use the goffice library instead. Sadly there seems to be little high-level or introductionary documentation to goffice APIs, so I might have to fix that along the way.

Roadmap

In the short term I will:

  • Implement parsing and graphing of the detailed massif output
  • Fix the graphing bug(s)
  • Add the ability to export/save a graph as PNG,PDF et.c. without having to go by the printing dialog

In the longer term I’m considering:

  • Adding the ability to run massif on a program directly from MassifG
  • An interactive mode which updates the graph while the program to be analyzed is still running
  • Making a custom widget which other applications can embed

In the meantime, please do give the current version some testing, and report bugs and other issues.