Assign v4l2 device a static name
Motivation
If you have multiple v4l2 devices: /dev/video0
and /dev/video1
plugged in via USB. Sometimes the enumeration of the devices will swap - The device that was /dev/video0
now becomes /dev/video1
and visa versa. If you treat each device differently this can break things after a reboot
Solution Architecture
To fix this we are going to use udev rules to create a v4l2 device, like /dev/video_front
, which is a symlink to the original, but identified by hardware properties that do not change if device enumeration is different
Solution
First we are going to get the information to identify our device:
udevadm info -a /dev/video2
This returns a bunch of information, in particular we are looking for the top level ATTR{index}
and ATTR{name}`:
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:14.0/usb1/1-8/1-8:1.0/video4linux/video2':
KERNEL=="video2"
SUBSYSTEM=="video4linux"
DRIVER==""
ATTR{dev_debug}=="0"
ATTR{index}=="0"
ATTR{name}=="Integrated Camera: Integrated C"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0':
KERNELS=="1-8:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="uvcvideo"
ATTRS{authorized}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceClass}=="0e"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{bInterfaceSubClass}=="01"
ATTRS{bNumEndpoints}=="01"
ATTRS{iad_bFirstInterface}=="00"
ATTRS{iad_bFunctionClass}=="0e"
ATTRS{iad_bFunctionProtocol}=="00"
ATTRS{iad_bFunctionSubClass}=="03"
ATTRS{iad_bInterfaceCount}=="02"
ATTRS{interface}=="Integrated Camera"
ATTRS{supports_autosuspend}=="1"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8':
KERNELS=="1-8"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="ef"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bDeviceSubClass}=="02"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="500mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 2"
ATTRS{bcdDevice}=="5420"
ATTRS{bmAttributes}=="80"
ATTRS{busnum}=="1"
ATTRS{configuration}==""
ATTRS{devnum}=="4"
ATTRS{devpath}=="8"
ATTRS{idProduct}=="2115"
ATTRS{idVendor}=="5986"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="SunplusIT Inc"
ATTRS{maxchild}=="0"
ATTRS{product}=="Integrated Camera"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="fixed"
ATTRS{rx_lanes}=="1"
ATTRS{speed}=="480"
ATTRS{tx_lanes}=="1"
ATTRS{urbnum}=="5782367"
ATTRS{version}==" 2.01"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
KERNELS=="usb1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{authorized_default}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="09"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="0mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bcdDevice}=="0504"
ATTRS{bmAttributes}=="e0"
ATTRS{busnum}=="1"
ATTRS{configuration}==""
ATTRS{devnum}=="1"
ATTRS{devpath}=="0"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
ATTRS{interface_authorized_default}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Linux 5.4.0-107-generic xhci-hcd"
ATTRS{maxchild}=="12"
ATTRS{product}=="xHCI Host Controller"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="unknown"
ATTRS{rx_lanes}=="1"
ATTRS{serial}=="0000:00:14.0"
ATTRS{speed}=="480"
ATTRS{tx_lanes}=="1"
ATTRS{urbnum}=="364"
ATTRS{version}==" 2.00"
looking at parent device '/devices/pci0000:00/0000:00:14.0':
KERNELS=="0000:00:14.0"
SUBSYSTEMS=="pci"
DRIVERS=="xhci_hcd"
ATTRS{ari_enabled}=="0"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x0c0330"
ATTRS{consistent_dma_mask_bits}=="64"
ATTRS{d3cold_allowed}=="1"
ATTRS{dbc}=="disabled"
ATTRS{device}=="0x9d2f"
ATTRS{dma_mask_bits}=="64"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="129"
ATTRS{local_cpulist}=="0-7"
ATTRS{local_cpus}=="ff"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{revision}=="0x21"
ATTRS{subsystem_device}=="0x2258"
ATTRS{subsystem_vendor}=="0x17aa"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
Then we are going to create a file with a the udev rule in it:
sudo vi /etc/udev/rules.d/99-my-cams.rules
SUBSYSTEM=="video4linux", ATTR{index}=="0", ATTR{name}=="Integrated Camera: Integrated C", SYMLINK+="video_front"
This will create a v4l2 device named /dev/video_front
which always links to this device, despite USB enumeration order.
To reload and make the changes effective:
sudo udevadm control --reload-rules && sudo udevadm trigger
Updated about 1 year ago