Necktwi
Necktwi

Reputation: 2617

Find bus number and device number with device file symlink

I have a device file(SYMLINK) /dev/CDMAModem generated by a udev rule. I want to find the bus number and device number of the actual device. Actually I want to perform USBDEVFS_RESET ioctl on device /dev/bus/usb/BUS_NO/DEVICE_NO in my C++ program.

----udev rule----

SUBSYSTEMS=="usb", ACTION=="add", DRIVERS=="zte_ev", ATTRS{bNumEndpoints}=="03", SYMLINK+="CDMAModem"
SUBSYSTEMS=="usb", ACTION=="remove", DRIVERS=="zte_ev", ATTRS{bNumEndpoints}=="03", SYMLINK-="CDMAModem"

Upvotes: 6

Views: 4884

Answers (5)

Charles Pehlivanian
Charles Pehlivanian

Reputation: 2133

I think libudev will give you that:

#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>

int main(int argc, char **argv)
{

    struct udev *udev;
    struct udev_enumerate *enumerate;
    struct udev_list_entry *devices, *dev_list_entry;
    struct udev_device *dev;

    udev = udev_new();

    enumerate = udev_enumerate_new(udev);
    udev_enumerate_add_match_subsystem(enumerate, "CDMAModem");
    udev_enumerate_scan_devices(enumerate);
    devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(dev_list_entry, devices) {
        const char *path;
    
        path = udev_list_entry_get_name(dev_list_entry);
        dev = udev_device_new_from_syspath(udev, path);

        fprintf(stderr, "devnum: %s\n",
            udev_device_get_sysattr_value(dev, "devnum"));
        fprintf(stderr, "busnum: %s\n",
            udev_device_get_sysattr_value(dev, 'busnum:));
        udev_device_unref(dev);
    }

    udev_enumerate_unref(enumerate);
    udev_unref(udev);

    return 0;
}

You can then use this information with ioctl() as in:

[charles@localhost 2-1]$ cd /sys/class/mem/random
[charles@localhost 2-1]$echo $PWD
/sys/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1
             
            

Upvotes: 5

Necktwi
Necktwi

Reputation: 2617

~$ sudo udevadm info -a -p $(sudo udevadm info -q path -n /dev/CDMAModem)
[sudo] password for gowtham:

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="zte_ev"
    ATTRS{port_number}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0':
    KERNELS=="2-1.2:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="zte_ev"
    ATTRS{bInterfaceClass}=="ff"
    ATTRS{bInterfaceSubClass}=="ff"
    ATTRS{bInterfaceProtocol}=="ff"
    ATTRS{bNumEndpoints}=="03"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{interface}=="Data Interface"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2':
    KERNELS=="2-1.2"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devpath}=="1.2"
    ATTRS{idVendor}=="19d2"
    ATTRS{speed}=="12"
    ATTRS{bNumInterfaces}==" 6"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{busnum}=="2"
    ATTRS{devnum}=="8"
  ATTRS{busnum}=="2"
  ATTRS{devnum}=="8"

Though this is ugly but works. From C++ program call udevadm and filter for busnum and devnum attributes from the output. I hope some developer who involved in libudev development shall help and may be udev mailing-list will help.

Upvotes: 1

CijcoSistems
CijcoSistems

Reputation: 266

If all you want to do is resolve the link, you can use readlink and parse the information later with string functions.

Function: ssize_t readlink (const char *filename, char *buffer, size_t size)

The readlink function gets the value of the symbolic link filename. The file name that the link points to is copied into buffer. This file name string is not null-terminated; readlink normally returns the number of characters copied. The size argument specifies the maximum number of characters to copy, usually the allocation size of buffer.

Upvotes: 1

Jeff Taylor
Jeff Taylor

Reputation: 326

You can perform an ioctl on the file represented by the symlink /dev/CDMAModem as you would on the file under the /dev/bus/ structure.

#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>

int f = open("/dev/CDMAModem", O_RDWR);
ioctl(f, USBDEVFS_RESET);

If you actually want to find where this link is pointing, the file command will tell you.

> file /dev/CDMAModem
/dev/CDMAModem: symbolic link to `bus/usb/BUS/DEV'

Upvotes: 2

twalberg
twalberg

Reputation: 62439

I think the stat() library call would be a good place to start... Along with libusb.

Upvotes: 1

Related Questions