clinuxunixusbsystemd

Is there a systemd-independent function for getting a USB device model?


I want to get a ID_MODEL and ID_VENDOR properties of a USB device in Linux. I know its /dev/bus/usb/xxx/yyy path.

I can do it with this code that depends on systemd (cc -lsystemd test.c):

#include <assert.h>
#include <stdio.h>
#include <systemd/sd-device.h>

int main() {
    sd_device* dev;

    // This function is used in udevadm too.
    // That means udevadm depends on systemd.
    int r = sd_device_new_from_path(&dev, "/dev/bus/usb/xxx/yyy");
    assert(r >= 0);

    const char* val;
    sd_device_get_property_value(dev, "ID_MODEL", &val);
    printf("ID_MODEL: %s\n", val);
}

Output:

ID_MODEL: Android

However, my application should work on OpenRC-based distributions (like Atrix or Gentoo). Because of that the systemd dependency is not acceptable for me.

How can I get this information without it?

I would like a solution that does not rely on parsing lsusb output.


Solution

  • Option 1: Use libusb

    Can you depend on libusb? There is a similar question and answer over on the Linux and Unix StackExchange, with this sample code:

    #include <stdio.h>
    #include <usb.h>
    main(){
        struct usb_bus *bus;
        struct usb_device *dev;
        usb_init();
        usb_find_busses();
        usb_find_devices();
        for (bus = usb_busses; bus; bus = bus->next)
            for (dev = bus->devices; dev; dev = dev->next){
                printf("Trying device %s/%s\n", bus->dirname, dev->filename);
                printf("\tID_VENDOR = 0x%04x\n", dev->descriptor.idVendor);
                printf("\tID_PRODUCT = 0x%04x\n", dev->descriptor.idProduct);
            }
    }
    

    Option 2: Parse udevadm info

    If you can use udev there is a different lsusb-like approach that may point you in the right direction. Using this answer from the Linux and Unix StackExchange, the udevadm program may help. First, get the 'raw' device path:

    $ udevadm info -q path -n /dev/usb/hiddev0
    /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.6/1-4.6:1.0/usbmisc/hiddev0
    

    And then you can query it:

    $ udevadm info [-q <type>] -p /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.6/1-4.6:1.0/usbmisc/hiddev0
    

    I don't have any actual USB devices to show you the output, but type can be one of: name, symlink, path, property, or all. You would have to parse the output, but it is relatively easy given output resembling:

    P: /devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5.6/1-5.6:1.0/usbmisc/hiddev1
    N: usb/hiddev1
    L: 0
    E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5.6/1-5.6:1.0/usbmisc/hiddev1
    E: DEVNAME=/dev/usb/hiddev1
    E: MAJOR=180
    E: MINOR=1
    E: SUBSYSTEM=usbmisc
    

    In your C code you'd have to popen() the commands and parse them like you're reading a file. Unfortunately if you want to operate at an even-lower level I'd have to suggest looking to the source code for udevadm.

    Option 3: Maybe the pavlinux udev?

    As you mentioned, a while ago systemd did take over udev development; I forget exactly when that happened. However, this udev fork from pavlinux was created that does not depend on systemd. It may also be helpful. The file src/udev-builtin-usb_id.c is parsing vendor information so maybe that's a better route?

    I'm making an assumption that this is simply a copy of udev before the integration with systemd, but I cannot attest to that. You may feel more comfortable checking out an older version of udev before the integration happened.