Setting Up a Scanner on Freebsd

Apr 27, 2024 · 1347 words · 7 minute read

Scanning is possible on FreeBSD, and well described in the handbook, but doesn't explain how to make scanning work for non-root users, which is what I wanted, as I don't want to running as root for day-to-day tasks.

I'm trying out FreeBSD as a daily driver, and one of the things it needs to be able to so is scan images, mostly for boring record keeping purposes, but it's something that my 'main' laptop can't do1.

This is done on a Lenovo X220 running FreeBSD 14.0-RELEASE, with Xorg and the XFCE4 desktop environment. The scanner is a Canon MP240. To get the scanner to work for a normal user, we need to do the following:

  1. Detect the scanner, install and configure SANE back end
  2. Install graphical scanner software and make sure it works as root
  3. Set permissions so non-root users can also use the scanner

Usual rules apply; # means run command as root, $ as user. If you break it, you get to keep both parts. If I end up on a longer tangent while trying to understand something, it'll be hidden under a collapsed "More Details" heading, and are mostly notes for my own understanding and can be ignored if all you want to do is get a scanner working.

1 Detect Scanner, install and configure SANE Back End

These first steps are the same as those in the FreeBSD Handbook2, and all need to be performed as root.

Connect the scanner and turn it on (I forgot this at first, and obviously, the system didn't detect an unpowered scanner):

# usbconfig list

Which returns a list of USB hubs and devices, including in my case:

ugen0.6: <Canon MP240 series> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (2mA)

The number after ugen (USB Generic Device Support) is its unit.address. Depending on the USB port you use, this can change, and will be different from computer to computer. You can also see these listed in /dev in the format ugen0.1 etc.

Get the details of the device by using dump_device_desc and specifying the specific device (-d) unit and address:

# usbconfig -d 0.6 dump_device_desc

I get the following, the important details are idVendor and idProduct.

ugen0.6: <Canon MP240 series> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON (2mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0200 
  bDeviceClass = 0x0000  <Probed by interface class>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x04a9 
  idProduct = 0x1732 
  bcdDevice = 0x0106 
  iManufacturer = 0x0001  <Canon>
  iProduct = 0x0002  <MP240 series>
  iSerialNumber = 0x0003  <23A096>
  bNumConfigurations = 0x0001

Install the SANE (Scanner Access Now Easy) backends.

# pkg install sane-backends

Create (if not existing) the /usr/local/etc/devd/saned.conf file with the following details. "vendor" and "product" should match those seen in the device description above.

  notify 100 {
        match "system" "USB";
        match "subsystem" "INTERFACE";
        match "type" "ATTACH";
        match "cdev" "ugen[0-9].[0-9]";
        match "vendor" "0x04a9"; 
        match "product" "0x1732"; 
        action "chown -L cups:saned /dev/\$cdev && chmod -L 660 /dev/\$cdev";
};
/usr/local/etc/devd/saned.conf

More Details

The above isn't SANE specific, but rather devd (Device State Change Daemon) directory, and follows the devd.conf format. devd watches for new devices being connected, and then performs actions based on that. In the above case what (I think) is happening is that when a device that has properties that match all regex listed above, the action is run. In this case changing the ownership and permissions of the /dev/ugen?.? device to allow the cups:sane group read and write access.

The FreeBSD Wiki page for devd has some good examples.

Then you need to restart the devd service with:

# service devd restart

You can test if the device is available to scan with by listing (-L) the devices:

# scanimage -L

and hopefully you see something like:

device `pixma:04A91732_23A096' is a CANON Canon PIXMA MP240 multi-function peripheral

Then make sure the service is enabled at boot time with:

# sysrc saned_enable="YES"

2 Install Graphical Scanner Software and Test

scanimage does allow you to scan from the command line, but that's not so practical. The Handbook lists three graphical scanning applications, and of those I installed Xsane as it's one I already know.

More Details

Currently it looks like the xsane.org website is down/redirecting to a German Ubuntu page.

I did try to install GNOME Simple Scan, but when trying to run it got the error

Unknown key gtk-applications-prefer-dark-theme in /home/$USER/.config/gtk-3.0/settings.ini
ld-elf.so.1: /usr/local/lib/libhandy-1.so.0: Undefined symbol "g_once_init_enter_pointer"

Which I think is related to some setting in XFCE confusing it. I didn't try to fix it any more.

Once installed run the software with sudo or doas for a user running a X windows session.

$ doas xsane

This is going to launch Xsane along with a warning that you're running as root, which is ok, click away, and test the scanner, which will hopefully work.

More Details

You need to run Xsane as root so that it can access the device, if you launch Xsane as a normal user you'll get an error that no device is found. If you run it as root, but you're in the Xsession as a normal user, you will probably see the following error:

(xsane:49757): Gtk-WARNING **: 22:10:16.313: cannot open display:

as root currently doesn't have access to a display.

But running things as root on a regular basis isn't a good idea, and this case actually really awkward; so we need to give your user permissions to access the device.

3 Set Permissions for Non-Root Users

At this point hopefully the scanner works, but you have to run it work root permissions. This is where the devfs(8) utility comes it. It allows the creation of 'rulesets' that can be used to manage the permissions of nodes in the /dev Device File System (DevFS).

We'll create a rule that allows members of the group "usb" to access the USB device nodes. First create a group with pw:

# pw groupadd usb

Add your user into the group

# pw groupmod usb -m <username>

More Details

You can check if this has worked by running:

# id <username>

and seeing the groups listed. You could also confirm by looking in /etc/group with grep ^usb /etc/group.

I tried to check by running id from my user or running groups, but then didn't immediately see the group appear, which I found strange. After a reboot (perhaps just a new login) they were visible.

Then create a ruleset in /etc/devfs.rules that will allow members of this group to see and access the USB devices:

[usbscanner=10]
add path 'ugen*' unhide
add path 'usb/*' unhide
add path 'ugen*' mode 0660 group usb
add path 'usb/*' mode 0666 group usb
/etc/devfs.rules

More Details

The devfs.rules file contains rules that run commands when devicies chang

devfs.rules is confusingly similar to devfs.conf, which performs a similar role, but only during boot/single user mode. We want the former.

Then add into /etc/rc.conf the following line to run the ruleset, where the name has to be the same as the ruleset heading in devfs.rules:

devfs_system_ruleset="usbscanner"

After a restart you should be able to run the scanner as any member of the "usb" group, or for the impatient:

# service devfs restart

Because you've now run Xsane with root permissions it will have created preferences files in ~/.sane, but as a normal user you don't have permission to change these. That will lead to errors like: "Failed to create file: Permission denied" when closing Xsane. Delete that folder as root, and then next time Xsane won't complain.

# rm -rf /home/<username/.sane

References

These are the main sources which I used to understand the steps above, as always thanks to all those people writing documentation.


1

Because my other laptop is a Mac that no longer runs the 32-bit drivers needed to make it work.

2

I'm repeating them here so all the steps are in one place for later, with other additional details I found interesting.