4

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.

1
  • ID_MODEL is not a USB device attribute. It is composed by udev from product attribute by default (but differently for SCSI/ATA devices), and may be overriden by installed udev rules. ID_VENDOR is taken from manufacturer attribute by default. Explore your /sys/bus/usb/devices/. Perhaps you can read attributes directly. Commented Jan 4 at 7:32

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Unfortunately, I cannot iterate through devices because I get the /dev path from a symlink. The symlink is not created by my application and the devices that match udev rules do not have a common property (or interface) to check against.
Hmm. I don't understand the udev matching rules issue you mention, but I added an answer with an approach using udevadm. It's like lsusb but may be a reasonable solution given the symlink you are using.
I mean a udev rule creates a symlink to /dev/bus/usb/.... However, I cannot replicate its behaviour in my code to filter unneeded devices during an iteration (like in libusb example). As for the udevadm, I checked its source code and it depends on systemd. Thank you anyway.
In your question you said you had /dev/bus/usb/xxx/yyy; I was assuming that you could use xxx and yyy as the filters. udev was not always part of systemd; I added a link to a non-systemd variation I found after a quick search and sanity check. Does that help? Sorry I'm getting out of my depth here.
Actually, what? You say the symlink is created by udev. If so, you should have a suitable udev on your system to create udevadm output that can be parsed. Are you actually using mdev or busybox?
|
1

Using the link to an older version of libudev @randchris provided, I managed to get it working without depending on systemd.

The example below is based on what udevadm used to do before the systemd integration and works fine with the latest libudev (see udev source):

#include <assert.h>
#include <stdio.h>
#include <sys/stat.h>

#include <libudev.h>

int main() {
    struct stat stat_buf;

    // Stat a device file
    int r = stat("/dev/bus/usb/xxx/yyy", &stat_buf);
    assert(r >= 0 && "stat() failed.");

    // Init a udev context
    struct udev* udev_ctx = udev_new();
    assert(udev_ctx != NULL && "udev_new() failed.");

    // Store the st_mode as a character for udev
    char type;
    if (S_ISBLK(stat_buf.st_mode)) {
        type = 'b';
    } else if (S_ISCHR(stat_buf.st_mode)) {
        type = 'c';
    } else {
        assert(0 && "Unknown device type.");
    }

    // Get a device using type and st_rdev
    struct udev_device* device = udev_device_new_from_devnum(udev_ctx, type, stat_buf.st_rdev);
    assert(device != NULL && "Unable to get a udev device.");

    printf(
        "ID_MODEL: %s\n",
        udev_device_get_property_value(device, "ID_MODEL")
    );

    udev_device_unref(device);
    udev_unref(udev_ctx);
}

Can be easily compiled with cc -ludev test.c.

1 Comment

Congrats! Glad I could help you find a working solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.