Logitech c930e focus reverts to default settings even with custom udev rules

Crossposted from Ask Ubuntu since issue appears in Fedora 35 as well

OS: Fedora 35 Workstation

I recently bought a Logitech c930e camera for use in online proctored exams where the invigilator may ask for a government-issued ID to be shown. The camera has an autofocus functionality which can initially focus on close objects (if one is placed in front of the camera as it is started), but once it shifts its focus to distant objects, it cannot re-detect and refocus on close objects subsequently placed in front of the camera. This causes the text on government-issued IDs placed in front of the camera to become illegible.

After some initial research, it turns out I can install v4l-utils and obtain a list of parameters I can tune on the camera as follows (assuming the associated device node is /dev/video0):

$ v4l2-ctl -d /dev/video0 --list-ctrls

Filtering the output to only include focus-related options gives:

$ v4l2-ctl -d /dev/video0 --list-ctrls | grep focus
                 focus_absolute 0x009a090a (int)    : min=0 max=255 step=5 default=0 value=0 flags=inactive
     focus_automatic_continuous 0x009a090c (bool)   : default=1 value=1

So focus_automatic_continuous is set to 1 and focus_absolute to 0 by default, which can also be seen by running the following commands:

$ v4l2-ctl -d /dev/video0 --get-ctrl focus_automatic_continuous
focus_automatic_continuous: 1
$ v4l2-ctl -d /dev/video0 --get-ctrl focus_absolute
focus_absolute: 0

After some manual experimentation, it seems that focus_automatic_continuous: 0 and focus_absolute: 75 gives a good balance of making close-up text clear enough while not blurring distant objects too much:

$ v4l2-ctl -d /dev/video0 --set-ctrl focus_automatic_continuous=0
$ v4l2-ctl -d /dev/video0 --set-ctrl focus_absolute=75

So I write a udev rules file /etc/udev/rules.d/90-logitech-c930e.rules for applying these settings:

KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0843", RUN+="/usr/bin/v4l2-ctl -d $devnode --set-ctrl focus_automatic_continuous=0", RUN+="/usr/bin/v4l2-ctl -d $devnode --set-ctrl focus_absolute=75"

This file can also be found on GitHub

The idVendor: 046d and idProduct: 0843 I obtained with lsusb:

$ lsusb | grep Logitech
Bus 001 Device 002: ID 046d:0843 Logitech, Inc. Webcam C930e

Then I restart systemd-udevd.service:

$ sudo systemctl restart systemd-udevd.service

Unplug the camera, and plug it back in. For the first few seconds, focus_automatic_continuous is set to 0 and focus_absolute to 75 as expected:

$ v4l2-ctl -d /dev/video0 --get-ctrl focus_automatic_continuous
focus_automatic_continuous: 0
$ v4l2-ctl -d /dev/video0 --get-ctrl focus_absolute
focus_absolute: 75

But after a few dozen seconds at most, the settings are reverted to their defaults:

$ v4l2-ctl -d /dev/video0 --get-ctrl focus_automatic_continuous
focus_automatic_continuous: 1
$ v4l2-ctl -d /dev/video0 --get-ctrl focus_absolute
focus_absolute: 0

Why might that be? Is it possible to disable this behavior? If so, how?

May be you want to try to install guvcview it will give same control as v4l2-ctl (but not sure about auto focus since I don’t have webcam with auto focus). Also at least for me the config is persistent.

Thanks, guvcview settings seems to be persistent as long as the camera remains plugged in, but the custom settings are lost when I unplug and re-plug the camera, so I’d have to manually open guvcview and tweak the settings every time I plug in the camera.

After some extra research, it seems that automatically restoring default settings is A Feature, Not A Bug ™: https://www.reddit.com/r/obs/comments/fflg5g/logitech_cam_keeps_resetting_video_settings_back/

So I came up with an idea: what if I could set the focus every few seconds or so, to prevent the settings from drifting back to defaults? It turns out this is possible, though not directly with udev since it’s a long-running process and udev rules are blocking. Instead, I used a combination of udev rules, systemd device units and service units.

The high-level idea is as follows:

  • Create a udev rules file that tags systemd so that the associated device unit is created, and set SYSTEMD_WANTS to point to a service template unit file, passing the minor device number to the template
  • The service template unit runs a script, passing its argument (the minor device number) to the script
  • The script uses the minor device number to refer to the correct camera and set the focus parameters repeatedly on that camera, on an interval of 5 seconds

The detailed solution can be found in this gist.