Ubuntu – Udev webcam rule read, but not respected

udevwebcam

I have two usb-webcams on them machine, but at boot they sometimes switch /dev/video number.
The solution to this problem seems to be creating a new udev rule. I have added this rule in
/etc/udev/rules.d/jj-video.rules:

Fix webcam 1

KERNEL=="video1", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d6b", ATTRS{idProduct}=="0001", SYMLINK+="webcam1"

Fix webcam 2

KERNEL=="video2", SUBSYSTEM=="video4linux", ATTR{name}=="Logitech QuickCam Pro 3000", KERNELS=="0000:00:1d.0", SUBSYSTEMS=="pci", DRIVERS=="uhci_hcd", ATTRS{vendor}=="0x8086", ATTRS##{device}=="0x2658", SYMLINK+="webcam2"

but the webcam symlinks are not created. I have tried many different combinations in this file. The present ones are just my latest attempts.

I found the parameters in:

jjk@eee-old:~$ udevadm info -a -p $(udevadm info -q path -p /class/video4linux/video1)  

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-2/2-2:1.0/video4linux/video1':
    KERNEL=="video1"
    SUBSYSTEM=="video4linux"
    DRIVER==""
    ATTR{name}=="Logitech QuickCam Pro 3000"
    ATTR{index}=="0"
    ATTR{button}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0':
    KERNELS=="2-2:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="Philips webcam"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{bAlternateSetting}==" 9"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{bInterfaceClass}=="0a"
    ATTRS{bInterfaceSubClass}=="ff"
    ATTRS{bInterfaceProtocol}=="00"
    ATTRS{supports_autosuspend}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2':
    KERNELS=="2-2"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{bNumInterfaces}==" 3"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bmAttributes}=="a0"
    ATTRS{bMaxPower}=="500mA"
    ATTRS{urbnum}=="371076"
    ATTRS{idVendor}=="046d"
    ATTRS{idProduct}=="08b0"
    ATTRS{bcdDevice}=="0002"
    ATTRS{bDeviceClass}=="00"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{speed}=="12"
    ATTRS{busnum}=="2"
    ATTRS{devnum}=="2"
    ATTRS{devpath}=="2"
    ATTRS{version}==" 1.10"
    ATTRS{maxchild}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{authorized}=="1"
    ATTRS{serial}=="01402100A5000000"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2':
    KERNELS=="usb2"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{configuration}==""
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bMaxPower}=="  0mA"
    ATTRS{urbnum}=="34"
    ATTRS{idVendor}=="1d6b"
    ATTRS{idProduct}=="0001"
    ATTRS{bcdDevice}=="0302"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{speed}=="12"
    ATTRS{busnum}=="2"
    ATTRS{devnum}=="1"
    ATTRS{devpath}=="0"
    ATTRS{version}==" 1.10"
    ATTRS{maxchild}=="2"
    ATTRS{quirks}=="0x0"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{authorized}=="1"
    ATTRS{manufacturer}=="Linux 3.2.0-29-generic uhci_hcd"
    ATTRS{product}=="UHCI Host Controller"
    ATTRS{serial}=="0000:00:1d.0"
    ATTRS{authorized_default}=="1"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0':
    KERNELS=="0000:00:1d.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="uhci_hcd"
    ATTRS{vendor}=="0x8086"
    ATTRS{device}=="0x2658"
    ATTRS{subsystem_vendor}=="0x1043"
    ATTRS{subsystem_device}=="0x82d8"
    ATTRS{class}=="0x0c0300"
    ATTRS{irq}=="23"
    ATTRS{local_cpus}=="ff"
    ATTRS{local_cpulist}=="0-7"
    ATTRS{dma_mask_bits}=="32"
    ATTRS{consistent_dma_mask_bits}=="32"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}==""

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

jjk@eee-old:~$ 

And tested the setup:

sudo udevadm --debug test /sys/class/video4linux/video1

main: runtime dir '/run/udev'
run_command: calling: test
adm_test: version 175
This program is for debugging only, it does not run any program,
specified by a RUN key. It may show incorrect results, because
some values may be different, or not available at a simulation run.

parse_file: reading '/lib/udev/rules.d/40-crda.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-fuse.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-gnupg.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-hplip.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-ia64.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-inputattach.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-libgphoto2-2.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-libsane.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-ppc.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-usb_modeswitch.rules' as rules file
parse_file: reading '/lib/udev/rules.d/40-xserver-xorg-video-intel.rules' as rules file
parse_file: reading '/lib/udev/rules.d/42-qemu-usb.rules' as rules file
parse_file: reading '/lib/udev/rules.d/50-firmware.rules' as rules file
parse_file: reading '/lib/udev/rules.d/50-udev-default.rules' as rules file
parse_file: reading '/lib/udev/rules.d/55-dm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/56-hpmud_support.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-cdrom_id.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-pcmcia.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-alsa.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-input.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-serial.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-storage-dm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-storage-tape.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-storage.rules' as rules file
parse_file: reading '/lib/udev/rules.d/60-persistent-v4l.rules' as rules file
parse_file: reading '/lib/udev/rules.d/61-accelerometer.rules' as rules file
parse_file: reading '/lib/udev/rules.d/64-xorg-xkb.rules' as rules file
parse_file: reading '/lib/udev/rules.d/66-xorg-synaptics-quirks.rules' as rules file
parse_file: reading '/lib/udev/rules.d/69-cd-sensors.rules' as rules file
add_rule: IMPORT found builtin 'usb_id', replacing /lib/udev/rules.d/69-cd-sensors.rules:76
parse_file: reading '/lib/udev/rules.d/69-libmtp.rules' as rules file
parse_file: reading '/lib/udev/rules.d/69-xorg-vmmouse.rules' as rules file
parse_file: reading '/lib/udev/rules.d/69-xserver-xorg-input-wacom.rules' as rules file
parse_file: reading '/etc/udev/rules.d/70-persistent-cd.rules' as rules file
parse_file: reading '/etc/udev/rules.d/70-persistent-net.rules' as rules file
parse_file: reading '/lib/udev/rules.d/70-printers.rules' as rules file
parse_file: reading '/lib/udev/rules.d/70-udev-acl.rules' as rules file
parse_file: reading '/lib/udev/rules.d/75-cd-aliases-generator.rules' as rules file
parse_file: reading '/lib/udev/rules.d/75-net-description.rules' as rules file
parse_file: reading '/lib/udev/rules.d/75-persistent-net-generator.rules' as rules file
parse_file: reading '/lib/udev/rules.d/75-probe_mtd.rules' as rules file
parse_file: reading '/lib/udev/rules.d/75-tty-description.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-ericsson-mbm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-longcheer-port-types.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-nokia-port-types.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-pcmcia-device-blacklist.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-platform-serial-whitelist.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-qdl-device-blacklist.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-simtech-port-types.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-usb-device-blacklist.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-x22x-port-types.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-mm-zte-port-types.rules' as rules file
parse_file: reading '/lib/udev/rules.d/77-nm-olpc-mesh.rules' as rules file
parse_file: reading '/lib/udev/rules.d/78-graphics-card.rules' as rules file
parse_file: reading '/lib/udev/rules.d/78-sound-card.rules' as rules file
parse_file: reading '/lib/udev/rules.d/80-drivers.rules' as rules file
parse_file: reading '/lib/udev/rules.d/80-mm-candidate.rules' as rules file
parse_file: reading '/lib/udev/rules.d/80-udisks.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-brltty.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-hdparm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-hplj10xx.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-keyboard-configuration.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-regulatory.rules' as rules file
parse_file: reading '/lib/udev/rules.d/85-usbmuxd.rules' as rules file
parse_file: reading '/lib/udev/rules.d/90-alsa-restore.rules' as rules file
parse_file: reading '/lib/udev/rules.d/90-alsa-ucm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/90-libgpod.rules' as rules file
parse_file: reading '/lib/udev/rules.d/90-pulseaudio.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-cd-devices.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-keyboard-force-release.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-keymap.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-udev-late.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-dell.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-fujitsu.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-gateway.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-ibm.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-lenovo.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-battery-recall-toshiba.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-csr.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-hid.rules' as rules file
parse_file: reading '/lib/udev/rules.d/95-upower-wup.rules' as rules file
parse_file: reading '/lib/udev/rules.d/97-bluetooth-hid2hci.rules' as rules file
parse_file: reading '/etc/udev/rules.d/jj-video.rules' as rules file
udev_rules_new: rules use 259284 bytes tokens (21607 * 12 bytes), 37913 bytes buffer
udev_rules_new: temporary index used 67520 bytes (3376 * 20 bytes)
udev_device_new_from_syspath: device 0x215103e0 has devpath '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/video4linux/video1'
udev_device_new_from_syspath: device 0x21510758 has devpath '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/video4linux/video1'
udev_device_read_db: device 0x21510758 filled with db file data
udev_device_new_from_syspath: device 0x21510e10 has devpath '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0'
udev_device_new_from_syspath: device 0x21511b10 has devpath '/devices/pci0000:00/0000:00:1d.0/usb2/2-2'
udev_device_new_from_syspath: device 0x215132f8 has devpath '/devices/pci0000:00/0000:00:1d.0/usb2'
udev_device_new_from_syspath: device 0x21513650 has devpath '/devices/pci0000:00/0000:00:1d.0'
udev_device_new_from_syspath: device 0x21513980 has devpath '/devices/pci0000:00'
udev_rules_apply_to_event: GROUP 44 /lib/udev/rules.d/50-udev-default.rules:29
udev_rules_apply_to_event: IMPORT 'v4l_id /dev/video1' /lib/udev/rules.d/60-persistent-v4l.rules:7
udev_event_spawn: starting 'v4l_id /dev/video1'
spawn_read: 'v4l_id /dev/video1'(out) 'ID_V4L_VERSION=2'
spawn_read: 'v4l_id /dev/video1'(out) 'ID_V4L_PRODUCT=Logitech QuickCam Pro 3000'
spawn_read: 'v4l_id /dev/video1'(out) 'ID_V4L_CAPABILITIES=:capture:'
spawn_wait: 'v4l_id /dev/video1' [2609] exit with return code 0
udev_rules_apply_to_event: IMPORT builtin 'usb_id' /lib/udev/rules.d/60-persistent-v4l.rules:9
builtin_usb_id: /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0: if_class 10 protocol 0
udev_builtin_add_property: ID_VENDOR=046d
udev_builtin_add_property: ID_VENDOR_ENC=046d
udev_builtin_add_property: ID_VENDOR_ID=046d
udev_builtin_add_property: ID_MODEL=08b0
udev_builtin_add_property: ID_MODEL_ENC=08b0
udev_builtin_add_property: ID_MODEL_ID=08b0
udev_builtin_add_property: ID_REVISION=0002
udev_builtin_add_property: ID_SERIAL=046d_08b0_01402100A5000000
udev_builtin_add_property: ID_SERIAL_SHORT=01402100A5000000
udev_builtin_add_property: ID_TYPE=generic
udev_builtin_add_property: ID_BUS=usb
udev_builtin_add_property: ID_USB_INTERFACES=:0aff00:010100:010200:
udev_builtin_add_property: ID_USB_INTERFACE_NUM=00
udev_builtin_add_property: ID_USB_DRIVER=Philips webcam
udev_rules_apply_to_event: LINK 'v4l/by-id/usb-046d_08b0_01402100A5000000-video-index0' /lib/udev/rules.d/60-persistent-v4l.rules:10
udev_rules_apply_to_event: IMPORT builtin 'path_id' /lib/udev/rules.d/60-persistent-v4l.rules:16
udev_builtin_add_property: ID_PATH=pci-0000:00:1d.0-usb-0:2:1.0
udev_builtin_add_property: ID_PATH_TAG=pci-0000_00_1d_0-usb-0_2_1_0
udev_rules_apply_to_event: LINK 'v4l/by-path/pci-0000:00:1d.0-usb-0:2:1.0-video-index0' /lib/udev/rules.d/60-persistent-v4l.rules:17
udev_rules_apply_to_event: RUN 'udev-acl --action=$env{ACTION} --device=$env{DEVNAME}' /lib/udev/rules.d/70-udev-acl.rules:74
udev_rules_apply_to_event: LINK 'webcam1' /etc/udev/rules.d/jj-video.rules:2
udev_event_execute_rules: no node name set, will use kernel supplied name 'video1'
udev_node_add: creating device node '/dev/video1', devnum=81:1, mode=0660, uid=0, gid=44
udev_node_mknod: preserve file '/dev/video1', because it has correct dev_t
udev_node_mknod: preserve permissions /dev/video1, 020660, uid=0, gid=44
node_symlink: preserve already existing symlink '/dev/char/81:1' to '../video1'
link_find_prioritized: found 'c81:2' claiming '/run/udev/links/v4l\x2fby-id\x2fusb-046d_08b0_01402100A5000000-video-index0'
udev_device_new_from_syspath: device 0x21516748 has devpath '/devices/pci0000:00/0000:00:1d.1/usb3/3-2/3-2:1.0/video4linux/video2'
udev_device_read_db: device 0x21516748 filled with db file data
link_find_prioritized: found 'c81:1' claiming '/run/udev/links/v4l\x2fby-id\x2fusb-046d_08b0_01402100A5000000-video-index0'
link_update: creating link '/dev/v4l/by-id/usb-046d_08b0_01402100A5000000-video-index0' to '/dev/video1'
node_symlink: atomically replace '/dev/v4l/by-id/usb-046d_08b0_01402100A5000000-video-index0'
link_find_prioritized: found 'c81:1' claiming '/run/udev/links/v4l\x2fby-path\x2fpci-0000:00:1d.0-usb-0:2:1.0-video-index0'
link_update: creating link '/dev/v4l/by-path/pci-0000:00:1d.0-usb-0:2:1.0-video-index0' to '/dev/video1'
node_symlink: preserve already existing symlink '/dev/v4l/by-path/pci-0000:00:1d.0-usb-0:2:1.0-video-index0' to '../../video1'
link_find_prioritized: found 'c81:1' claiming '/run/udev/links/webcam1'
link_update: creating link '/dev/webcam1' to '/dev/video1'
node_symlink: preserve already existing symlink '/dev/webcam1' to 'video1'
udev_device_update_db: created db file '/run/udev/data/c81:1' for '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/video4linux/video1'
ACTION=add
COLORD_DEVICE=1
COLORD_KIND=camera
DEVLINKS=/dev/v4l/by-id/usb-046d_08b0_01402100A5000000-video-index0 /dev/v4l/by-path/pci-0000:00:1d.0-usb-0:2:1.0-video-index0 /dev/webcam1
DEVNAME=/dev/video1
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/video4linux/video1
ID_BUS=usb
ID_MODEL=08b0
ID_MODEL_ENC=08b0
ID_MODEL_ID=08b0
ID_PATH=pci-0000:00:1d.0-usb-0:2:1.0
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_2_1_0
ID_REVISION=0002
ID_SERIAL=046d_08b0_01402100A5000000
ID_SERIAL_SHORT=01402100A5000000
ID_TYPE=generic
ID_USB_DRIVER=Philips webcam
ID_USB_INTERFACES=:0aff00:010100:010200:
ID_USB_INTERFACE_NUM=00
ID_V4L_CAPABILITIES=:capture:
ID_V4L_PRODUCT=Logitech QuickCam Pro 3000
ID_V4L_VERSION=2
ID_VENDOR=046d
ID_VENDOR_ENC=046d
ID_VENDOR_ID=046d
MAJOR=81
MINOR=1
SUBSYSTEM=video4linux
TAGS=:udev-acl:
UDEV_LOG=6
USEC_INITIALIZED=18213768
run: 'udev-acl --action=add --device=/dev/video1'
jjk@eee-old:~$ 

(and correspondingly for video2)

It looks to me like my rules are read, but not respected.
What am I doing wrong?

Best Answer

It looks like you have two problems.

The first is an ordering problem. Reading the kernel.org manpage on udev, it mentions:

All rules files are ... processed in lexical order

Meaning rules are processed in alphabetical order. The first rule that triggers for a device creates the device file. It looks like you've named your rule file jj-video.rules. Looking at the output you provided of udevadm --debug test, it shows that this file is parsed LAST of all the rule files on your system.

Try renaming your file 00-jj-video.rules. That should put it first in the udev rule list, and make it trigger before the other rules.

The second issue is with the rules themselves. The rules read that you're trying to create /dev/webcam2 when the Logitech QuickCam Pro 3000 is plugged in and when the kernel puts it on /dev/video2. From what you said, you don't want to match on the device name. Another problem is that you aren't always using hexadecimal notation in your ATTRS.

This rule should work for the Logitech QuickCam Pro 3000, based on the information you provided in the question:

SUBSYSTEM=="video4linux", ATTRS{idVendor}=="0x046d", ATTRS{idProduct}=="0x08b0", SYMLINK+="webcam2"

The following may work for the other webcam. I can't be sure, because you didn't provide full information on it:

SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTRS{idVendor}=="0x1d6b", ATTRS{idProduct}=="0x0001", SYMLINK+="webcam1"

The most useful tutorial I've found on writing udev rules is http://www.reactivated.net/writing_udev_rules.html.

Related Question