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.
libusbCan 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);
}
}
udevadm infoIf 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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With