Diary and notebook of whatever tech problems are irritating me at the moment.

20090815

Writing UDEV rules to get a SCSI scanner working on Ubuntu

I'm building some Ubuntu 9.04 (Jaunty Jackalope) systems for relatives and using them as a way to get rid of a lot of old hardware that has been taking up space in my office. This includes several old USB, parallel port, and SCSI scanners. SCSI scanners pretty much ruled in the days before USB as they were much faster than parallel ports. However, they were a pain to configure and required heavy (and usually short) cables which made them difficult to fit into your work area. I tested a Microtek ScanMaker E3 (MRS-600E3) and UMAX Vista S8 scanner first. They worked without problems although the former was picky about termination. Unfortunately a Hewlett-Packard ScanJet 6100C (Q2950A) didn't work at all. Checking the kernel messages indicated that it was represented by /dev/sg7 but the permissions were 0660 root:root so sane couldn't access it. Changing the permissions solved the problem but the /dev directory is a virtual filesystem controlled by udev and the changes are lost after reboot. I could just put a chmod comand in /etc/rc.local but that is the wrong way to fix it. A search on launchpad found bug #378989 which describes the problem with this model. I'm not sure if the fault lies with udev or HAL but creating a udev rule is a simple enough way to fix it for now. I'll describe how to create such a rule using this as an example but udev rules can do much more than just change device permissions.

First you need to be root. Either add "sudo" to the beginning of the following commands or switch to a root shell with "sudo su". Next install lsscsi which makes it easy to identify device node assignments:

apt-get install lsscsi

Then run it to get a list of SCSI devices:

lsscsi -g
[0:0:0:0] disk ATA Maxtor 33073U4 BAC5 /dev/sda /dev/sg0
[0:0:1:0] cd/dvd LITE-ON COMBO SOHC-4836V SG$4 /dev/sr0 /dev/sg1
[4:0:5:0] process HP C2520A 3644 - /dev/sg7
[5:0:0:0] disk USB 2.0 Flash Disk 0.00 /dev/sdb /dev/sg2
[6:0:0:0] disk Generic USB SD Reader 1.00 /dev/sdc /dev/sg3
[6:0:0:1] disk Generic USB CF Reader 1.01 /dev/sdd /dev/sg4
[6:0:0:2] disk Generic USB SM Reader 1.02 /dev/sde /dev/sg5
[6:0:0:3] disk Generic USB MS Reader 1.03 /dev/sdf /dev/sg6

Note that the scanner is at /dev/sg7. With this information you can then use udevadm to find out what is known about the device in the udev database and where in hierarchy of systems it lies:

udevadm info -a -p /sys/class/scsi_generic/sg7

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:1e.0/0000:01:01.0/host4/target4:0:5/4:0:5:0/scsi_generic/sg7':
KERNEL=="sg7"
SUBSYSTEM=="scsi_generic"
DRIVER==""

looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:01.0/host4/target4:0:5/4:0:5:0':
KERNELS=="4:0:5:0"
SUBSYSTEMS=="scsi"
DRIVERS==""
ATTRS{device_blocked}=="0"
ATTRS{type}=="3"
ATTRS{scsi_level}=="3"
ATTRS{vendor}=="HP "
ATTRS{model}=="C2520A "
ATTRS{rev}=="3644"
ATTRS{state}=="running"
ATTRS{timeout}=="0"
ATTRS{iocounterbits}=="32"
ATTRS{iorequest_cnt}=="0x8"
ATTRS{iodone_cnt}=="0x8"
ATTRS{ioerr_cnt}=="0x1"
ATTRS{modalias}=="scsi:t-0x03"
ATTRS{evt_media_change}=="0"
ATTRS{queue_depth}=="2"
ATTRS{queue_type}=="none"

looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:01.0/host4/target4:0:5':
KERNELS=="target4:0:5"
SUBSYSTEMS=="scsi"
DRIVERS==""

looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:01.0/host4':
KERNELS=="host4"
SUBSYSTEMS=="scsi"
DRIVERS==""

looking at parent device '/devices/pci0000:00/0000:00:1e.0/0000:01:01.0':
KERNELS=="0000:01:01.0"
SUBSYSTEMS=="pci"
DRIVERS=="aic7xxx"
ATTRS{vendor}=="0x9004"
ATTRS{device}=="0x7178"
ATTRS{subsystem_vendor}=="0x0000"
ATTRS{subsystem_device}=="0x0000"
ATTRS{class}=="0x010000"
ATTRS{irq}=="22"
ATTRS{local_cpus}=="ffffffff,ffffffff" ATTRS{local_cpulist}=="0-63"
ATTRS{modalias}=="pci:v00009004d00007178sv00000000sd00000000bc01sc00i00"
ATTRS{enable}=="1"
ATTRS{broken_parity_status}=="0"
ATTRS{msi_bus}==""

looking at parent device '/devices/pci0000:00/0000:00:1e.0':
KERNELS=="0000:00:1e.0"
SUBSYSTEMS=="pci"
DRIVERS==""
ATTRS{vendor}=="0x8086"
ATTRS{device}=="0x244e"
ATTRS{subsystem_vendor}=="0x0000"
ATTRS{subsystem_device}=="0x0000"
ATTRS{class}=="0x060400"
ATTRS{irq}=="0"
ATTRS{local_cpus}=="ffffffff,ffffffff"
ATTRS{local_cpulist}=="0-63"
ATTRS{modalias}=="pci:v00008086d0000244Esv00000000sd00000000bc06sc04i00"
ATTRS{enable}=="1"
ATTRS{broken_parity_status}=="0"
ATTRS{msi_bus}=="1"

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

Note that the DRIVERS=="aic7xxx" indentifies the Adaptec AHA-2940 SCSI card. All of this data can be referenced by a udev rule to identify when and how to manipulate the device. That is what udev does - run everything through a list of rules, matching or excluding attributes as specified by a rule, then performing an operation when the conditions of a rule is met. The manual for udev is at /usr/share/doc/udev/writing_udev_rules/index.html and it gives many good examples of what you can do. In this case the scanner device needs different permissions and group ownership so that users can access it with Xsane. Most of the rules included with packages are in /lib/udev but local rules can be added to /etc/udev/rules.d and they can override existing rules. There is a file name standard for the rule files (see the README in the directory) - they always start with a number (which indicates priority) and end with ".rules". My rule file is "/etc/udev/rules.d/45-scsi-scanner.rules", owned by root and in group root with 0644 (rw-r--r--) permissions. You have to reboot to make it active. This is what it contains:

# permissions for HP ScanJet 6100C SCSI scanner
SUBSYSTEM=="scsi_generic",ATTRS{vendor}=="HP",ATTRS{model}=="C2520A", NAME="%k", SYMLINK="scanner%n", MODE="0660", GROUP="scanner"

So what does this all mean? First the SUBSYSTEM keyword says it only applies to devices in the "scsi_generic" subsystem (as per the first few lines that udevadm reported). The "==" is a comparison operator. Next the ATTRS{vendor} keyword specifies that an attribute named "vendor" in the subsystem (or any parent subsystem) has to have a value of "HP" (which the SCSI module reports via the SCSI card). Then the ATTRS{model} keyword tells udev to look in the same subsystem that matched the vendor for a model attribute that matches "C2520A". If it finds one, and since there are no other comparisons specified, then the rule matches and the rest is processed. NAME is the keyword for setting the device node name (sg7 in this case) and the %k is a string substitution operator that udev will expand to the original name assigned by the kernel (again sg7). The "=" is the assignment operator. So this part of the rule sets the NAME assignment key to the original "sg7" effectively keeping the default device node "/dev/sg7" as is. The SYMLINK keyword creates symlinks to the default device node. The %n operator is expanded by udev to the kernel number of the device (the 7 in sg7). The resulting symlink will be scanner7 in this case and if the default node changes due to a SCSI device being added or removed the symlink will change to match (scanner5 for sg5, etc.) For the scanner rule it is for convenience only as a device named "scanner" is easier to figure out than "sg", especially when trying to do user support over the phone. The MODE just sets the permissions in octal and GROUP assigns a specific group membership of "scanner".

When this rule is activated, /dev/sg7 will be root:scanner with rw-rw---- permissions and a /dev/scanner7 symlink will also be created that points to it. For the user to access the scanner they need to be in the scanner group. If the scanner group doesn't exist (not in /etc/group) then you can add it with:

addgroup --system scanner

This will dynamically create a system group somewhere in the range of 100-999. Any users added to the group need to relogin for it to take effect.

6 comments:

pianoman4Jesus said...

Wow! Exactly what I was looking for!

I was "struggling" connecting a HP 6100C to Ubuntu 9.04. Your suggestion worked perfectly.

This information should have been in the official Ubuntu docs for Sane and scanning. I will encourage the addition of your findings, sending them a link to your blog post.

ossie said...

A more generic way (at least in slackware) would be:

# permissions for SCSI sg HP scanner devices
SUBSYSTEMS=="scsi", KERNEL=="s[g][0-9]*", ATTRS{type}=="3", ATTRS{vendor}=="HP", NAME="%k", GROUP="scanner", MODE="0660"

SCSI scanners should be type 6 devices, but at least HP ones identify themselves as type 3 (processor).

jhansonxi said...

Thanks for the tip ossie.

ossie said...

You're welcome. Please make a correction: the brackets around "[g]" could be safely eliminated, as they're just inadvertently copied from the "s[gt]" SCSI sg/tape example...

Michael said...

Great solution but I came up with something much simpler assuming you don't mind typing in your password whenever you want to access your scanner.

I created scanner.sh which has two lines of code:

#!/bin/sh
sudo chmod 777 /dev/sg6 && xsane

Then I created a launcher for scanner.sh

When run a terminal window appears, requests mu su credentials, sets the device permissions accordingly (you'll need to edit that line to point to the location of your device,) and then launches xsane. When I close csane, the terminal window also closes.

It maybe isn't all that elegant but it's simple.

jhansonxi said...

You could also just use my Scanner Access Enabler. The posting also explains how to bypass the password request with sudoers.

About Me

Omnifarious Implementer = I do just about everything. With my usual occupations this means anything an electrical engineer does not feel like doing including PCB design, electronic troubleshooting and repair, part sourcing, inventory control, enclosure machining, label design, PC support, network administration, plant maintenance, janitorial, etc. Non-occupational includes residential plumbing, heating, electrical, farming, automotive and small engine repair. There is plenty more but you get the idea.