tech-notes

If you’ve got a wireless printer/scanner combination by HP, chances are it will work, even if the device model is not on the working devices list. In my case, we have an HP Laser MFP 135wg in the household. I thought it might be difficult to get working, especially with my non-standard setup (above all things, archlinux is custom, after all). Once I looked at the SANE page, I thought I was in for a long one, since it doesn’t really talk about scanners on the network, but it was actually quite easy. So, after all these long-winded blogposts about me trying to get something working without really understanding them, here’s a quick one about something that was surprisingly easy to get working.

Step one: install the base minimum

I installed:

  • hplip these are HP linux drivers, but later testing showed they’re not needed when using the device over the network
  • sane “scanner access now easy”, which includes the scanimage program (for commandline scanning)
  • cups previously known as “common unix printing system” – because I may want to print with this too
  • sane-airscan which is needed to ensure discovery of network scanning devices

Step two: find scanner

Use airscan-discover (if you configured a host firewall, you may need to make changes, see archwiki), which outputs something like (where the X’s are numbers):

[devices]

HPBXXXXAXXXXAA (HP Laser MFP 131 133 135-138) = http://192.168.XXX.XXX:80/eSCL/, eSCL

HPBXXXXAXXXXAA (HP Laser MFP 131 133 135-138) = https://192.168.XXX.XXX:443/eSCL/, eSCL

Try lookng for the scanner:

scanimage -L

Step three: configure sane

I did this before actually checking whether it is needed, so it might even work without this step, but if you need to: open up the configuration file for sane-airscan, which by default is /etc/sane.d/airscan.conf and add the device:

“HPBXXXXAXXXXAA (HP Laser MFP 131 133 135-138)” = https://192.168.XXX.XXX:443/eSCL/, eSCL

Confirm your device is detected. Note that you can also exclude network devices here, if devices show up that you don’t want to see.

Step four: scan!

For a test-scan, use:

sanimage --format=png --output-file test.png --progress --device "escl:https://192.168.XXX.XXX:443"

of course, you can also use graphical interfaces to test – using scanimage directly eliminates some potential sources of error if things aren’t working.

Sometimes you think you have a rough idea of how things are supposed to work, and then somehow they don’t. This post is being written as I figure out how this works and maybe this helps someone trying to learn about this. The goal is to allow a user to control the keyboard backlight (and probably later on other stuff). It involves: a recent archlinux, sysfs, systemd, udev, seats.

I already knew that sysfs is a mechanism used in Linux to control hardware through the filesystem. This means that under /sys/, there are lots of files that control devices by reading or writing things into the respective files. Some of these are specific to the manufacturer, because hard- and firmware handle things differently. On this (Lenovo Thinkpad) machine, /sys/class/leds/ contains a bunch of different leds:

$ls -la /sys/class/leds/

input4::capslock/ phy0-led/ tpacpi::kbd_backlight/ tpacpi::standby/ input4::numlock/ platform::micmute/ tpacpi::lid_logo_dot/ tpacpi::thinklight/ input4::scrolllock/ platform::mute/ tpacpi::power/ tpacpi::thinkvantage/

In this case, we’re interested in tpacpi::kbd_backlight (keyboard backlight). The folder is a symbolic link to the corresponding device in the sysfs:

$ls -la /sys/class/leds/tpacpi\:\:kbd_backlight

lrwxrwxrwx 1 root root 0 Jan 7 10:19 /sys/class/leds/tpacpi::kbd_backlight -> ../../devices/platform/thinkpad_acpi/leds/tpacpi::kbd_backlight

I checked – we can set the brightness (respecting the value in /sys/class/leds/tpacpi\:\:kbd_backlight/max_brightness – 2 on my system) of the backlight via:

echo 1 > /sys/class/leds/tpacpi\:\:kbd_backlight/brightness

but this only works as root, and indeed:

ls -la /sys/class/leds/tpacpi\:\:kbd_backlight/brightness

-rw-r--r-- 1 root root 4096 Jan 7 09:36 /sys/class/leds/tpacpi::kbd_backlight/brightness

First idea: change this to root:light, so that users in the light group can modify the file and done. Changing this directly doesn’t work, because sysfs is not really a filesystem, i.e., the permissions are not modifiable, they’re set by the system (I’m not sure if it is the kernel, some firmware or systemd). Some googling suggests that udev and its rules are the way to modify these permissions, see e.g. archwiki on (monitor) backlight. This page suggests changing access via a RUN+= rule, but reading the udev article, there are builtin commands. So I thought I’d just write one. I learned that standard udev rules are located in /usr/lib/udev/rules.d and that there is a difference between system and non-system groups (see man groupadd – in short this works via the groupid). I should have read the README first, but instead I wrote a short rule:

cat /etc/udev/rules.d/00-light.rules

# allow users in light to control backlights KERNEL=="tpacpi::kbd_backlight" SUBSYSTEM=="leds" GROUP="light" MODE="0660"

The KERNEL and SUBSYSTEM information can be found via the command udevadm info, e.g.: udevadm info --attribute-walk /sys/class/leds/tpacpi::kbd_backlight, though it would have been smarter to leave out the attribute walk option, since the info command lists other info as well:

udevadm info /sys/class/leds/tpacpi::kbd_backlight

P: /devices/platform/thinkpad_acpi/leds/tpacpi::kbd_backlight M: tpacpi::kbd_backlight J: +leds:tpacpi::kbd_backlight U: leds E: DEVPATH=/devices/platform/thinkpad_acpi/leds/tpacpi::kbd_backlight E: SUBSYSTEM=leds E: USEC_INITIALIZED=XXXXXXXX E: ID_PATH=platform-thinkpad_acpi E: ID_PATH_TAG=platform-thinkpad_acpi E: ID_FOR_SEAT=leds-platform-thinkpad_acpi E: SYSTEMD_WANTS=systemd-backlight@leds:tpacpi::kbd_backlight.service E: TAGS=:systemd:seat: E: CURRENT_TAGS=:systemd:seat:

as the README file says, we can check the active udev configuration via:

systemd-analyze cat-config udev/rules.d

which outputs all active rules. I tried to use the rule I created above, but this did not change the permissions after reloading thee config (it is possible a reboot may have been required). However, I then found the “allowing regular users to use devices” section, which at the end explains that one should use a uaccess tag (for user access). /usr/lib/udev/rules.d/73-seat-late.rules is then the magic glue that should set the permission. Since rules in /etc/ are applied before these rules (at least according to udevadm test), we can hopefully just change our rule to:

ACTION!="remove", SUBSYSTEM=="leds", MODE="0660", TAG+="uaccess"

Make sure to udevadm test /sys/class/leds/tpacpi::kbd_backlight and verify there are no errors (such as a double = for mode, which shows an error, or missing commas, which means the tag doesn’t show up in the simulated config) and then udevadm control --reload. Unfortunately:

echo 1 > /sys/class/leds/tpacpi\:\:kbd_backlight/brightness

bash: /sys/class/leds/tpacpi::kbd_backlight/brightness: Permission denied

I tried to use temporary debugging to find out why, using udevadm control --log-priority=debug and then journalctl -f, which after an explicit trigger of the device udevadm trigger /sys/class/leds/tpacpi::kbd_backlight only showed output for the MODE command (and it looks like it isn’t triggering the rules for uaccess). However, the mode command also doesn’t seem to actually change the mode. I don’t know what to do here, but a reboot may work.

After reboot: it did not. Searching on, I learned that I can verify whether the tag is applied correctly via udevadm monitor -t uaccess -u -p -k followed by a udevadm trigger /sys/class/leds/tpacpi::kbd_backlight. The monitor then indeed outputs that the tags and current tags include uaccess. Similarly, journalctl shows that the rule (which I have moved to check whether the execution sequence was the issue) is executed after the trigger:

Jan 07 13:41:45 namnatop (udev-worker)[3395]: tpacpi::kbd_backlight: /etc/udev/rules.d/72-light.rules:3 GROUP:="light": Set group ID: 965 Jan 07 13:41:45 namnatop (udev-worker)[3395]: tpacpi::kbd_backlight: /etc/udev/rules.d/72-light.rules:3 MODE="0660": Set mode: 0660

but there is no file with this group:

find /sys -group light

This might suggest it is overwritten later. I thought that in theory, the seat mechanism is the intended way to get access, so I figured I’d try that way. This question notes that loginctl is supposed to be the place where the seat magic happens (see man sd-login(3) if you, like me, first need an intro on what seats are). Checking:

loginctl seat-status seat0 | grep -C1 kbd_b

│ leds:platform::mute ├─/sys/devices/platform/thinkpad_acpi/leds/tpacpi::kbd_backlight │ leds:tpacpi::kbd_backlight ├─/sys/devices/platform/thinkpad_acpi/leds/tpacpi::lid_logo_dot

Since this suggests it should already work, I deleted my custom rule. After also looking at the list-seats, list-sessions and list-users commands, I thought the issue might be that my window manager is doing something weird, but even in a simple login shell, I still get an access denied. Going back to the arch wiki on udev again, this explains how the uaccess is supposed to work by pointing at the systemd udev code. As far as I can tell, this should print errors if it fails, unless it things systemd-logind isn’t running. But is this builtin ever used..?

grep -r -E "builtin.*uaccess" /usr/lib/udev/

/usr/lib/udev/rules.d/73-seat-late.rules:TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"

Indeed this also matches the systemd-analyze output.

Some solutions for “I want to write this thing” suggest using chgrp and chown instead:

ACTION!="remove", SUBSYSTEM=="leds", RUN+="/bin/chgrp seat $sys$devpath/brightness", RUN+="/bin/chmod g+w $sys$devpath/brightness"

Interestingly, this works fine. I still have no idea why the intended mechanism (uaccess) doesn’t work, though…