2014-09-18

Getting USB printer to work under OpenBSD

I am writing this post mainly for myself, although it may be helpful to other OpenBSD users as well.

Preparation

The first and most important step is to find a driver for your printer. Binary Linux–only support packages most likely won't help, particularily in case of amd64 system, so your only hope is open–source driver.

I was lucky to find that my printer is supported by Gutenprint project, and thus a driver is available in gutenprint package (print/gutenprint port).

So, the preparation stage concludes into following command:

$ sudo pkg_add a2ps gutenprint

Now, that packages are installed, we are ready to continue with configuration.

Device

By default OpenBSD recognizes USB printers and assigns ulpt(4) driver to them. Unfortunately, this printer can't work via ulpt(4), so we have to disable this driver in kernel:

$ sudo config -fe /bsd
OpenBSD 5.6-current (GENERIC.MP) #372: Wed Sep 10 18:03:54 MDT 2014
todd@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Enter 'help' for information
ukc> disable ulpt
282 ulpt* disabled
ukc> quit
Saving modified kernel.

Running the same command without -f switch will result in modification to the current kernel, which is needed to avoid rebooting.

Now that printer will be identified as generic usb device (ugen(4), the appropriate file permissions for corresponding devices should be set. Once printer is connected to OpenBSD PC, it will show up in dmesg and usbdevs as ulptN device:

$ usbdevs -d
addr 1: EHCI root hub, ATI
uhub0
addr 2: MP210 series, Canon
ugen1
addr 1: EHCI root hub, ATI
uhub1
addr 2: Integrated Camera, Ricoh Company Ltd.
uvideo0
addr 1: OHCI root hub, ATI
uhub2
addr 2: Broadcom Bluetooth Device, Broadcom Corp
ugen0
addr 1: OHCI root hub, ATI
uhub3

In this case it appears as ugen1 (its identifier, "MP210 series, Canon") will also be needed later.

As lpt operates with "daemon" permissions, and sane operates with group "_saned", both daemon will be able to use device after following change in ownership:

$ chown daemon:_saned /dev/ugen1.*

Getting PPD

According to /usr/local/share/doc/pkg-readmes/cups-filters, there are two ways of getting PPDs:

  1. Get readily available PPD file from /usr/local/share/foomatic/db/source/PPD or
  2. Generating one from source.

Unfortunately, there is no pre-build PPD for Canon PIXMA MP10, so PPD should be generated manually following instruction from /usr/local/share/doc/pkg-readmes/foomatic-db-engine:

$ sudo mkdir /etc/foomatic/direct
$ foomatic-ppdfile -P MP210
Canon MP210-series Id='Canon-MP210-series' Driver='No Default Driver' CompatibleDrivers='gutenprint-ijs-simplified.5.2 gutenprint-ijs.5.2 '
Canon MULTIPASS-MP210 Id='Canon-MULTIPASS-MP210' Driver='No Default Driver' CompatibleDrivers='gutenprint-ijs-simplified.5.2 gutenprint-ijs.5.2 '
$ foomatic-ppdfile -p 'Canon-MP210-series' -d 'gutenprint-ijs.5.2' | sudo tee /etc/foomatic/direct/canon.ppd >/dev/null

(Of course "MP210" and "Canon-MP210-series" "gutenprint-ijs.5.2" and "canon" in PPD path should be replaced with parameters corresponding to actual printer. Choice of particular driver among several matching is, of course, individual. If in doubt, choose randomly and try; you'll always have a chance to make different choice any time later.)

Configuration

Readme file /usr/local/share/doc/pkg-readmes/cups-filters gently suggests continuing with creating a converter script (filename may be chosen by user; I chose /etc/converter.sh):

#!/bin/sh

/usr/local/bin/a2ps -BRq --columns=1 -o - | \
 /usr/local/bin/foomatic-rip -P canon

Next, /etc/printcap entry pointing to actual printer device and converter script should be created:

lp|canon:\
 :lp=/dev/ugen1.01:\
 :if=/etc/converter.sh:\
 :sd=/var/spool/output:\
 :lf=/var/log/lpd-errs:\
 :sh:

(In MP210 the actual device turned out to be /dev/ugen1.01. Actual number of sub–device may be different on other devices.)

Ultimately lpd service should be enabled and started:

$ sudo rcctl enable lpd
$ sudo /etc/rc.d/lpd start

At this stage printing test document should succeed:

$ lpr test.pdf

Scanner

Scanner component of the device should already work since changing ownership of /dev/ugen1.*:

$ scanimage > 1.pnm
scanimage: open of device pixma:04A91721 failed: Access to resource has been denied
$ sudo scanimage > test.pnm

(Scanning should probably work without "sudo". The last command, although it produces the output image, exists with non-zero status and "Bus error (core dumped)" message. I'll investigate these issues and update this post "later".)

Hotplugging

Although contrary to /usr/local/share/doc/pkg-readmes/cups-filters ownership of /dev/ugen1.* devices persists over reboots, there is no guarantee that every single time the printer will attach as ugen1 each and every time.

The process of changing and restoring permissions may be automated using hotplugd(8) daemon. To make hotplugd do its job the following files should be created:

  • /etc/hotplugs/attach:
    #!/bin/ksh
    
    DEVCLASS=$1
    DEVNAME=$2
    
    case $DEVCLASS in
      0) # Generic devices
        if [ "${DEVNAME%%+([0-9])}" == "ugen" ]; then
          DEVDESCR=$(usbdevs -d | grep -B1 ugen1 | sed -Ee 's/ addr [0-9]+: (.+)$/\1/' -e 1q)
          if [ "${DEVDESCR}" == "MP210 series, Canon" ]; then
            chown daemon:_saned /dev/${DEVNAME}.*
            echo "#!/bin/ksh\nchown root:wheel /dev/${DEVNAME}.*" > /etc/hotplug/detach.$DEVNAME
            chmod +x /etc/hotplug/detach.$DEVNAME
            echo "/canon/\n/:lp=/c\n\t:lp=/dev/${DEVNAME}.01:\\n.\nwq" | ed /etc/printcap
          fi
        fi
        ;;
    esac
    
    (Upon detecting "MP210 series, Canon" device the script changes permissions, fixes device name in /etc/printcap and creates a script for cleaning up permissions after device is disconnected.
  • /etc/hotplug/detach:
    #!/bin/ksh
    
    DEVCLASS=$1
    DEVNAME=$2
    
    if [ -x /etc/hotplug/detach.${DEVNAME} ]; then
      /etc/hotplug/detach.${DEVNAME} && rm /etc/hotplug/detach.${DEVNAME}
    fi
    
    (Basically this file just calls cleanup scripts and removes them after completion.

With this setup the printer may be connected and disconnected on demand with no additional actions required.

Further directions

By default a2ps and foomatic-rip understand several formats including PostScript, PDF and DVI documents, as well as popular image types and several other formats. Support for additional formats may be added by modifying /etc/a2ps.cfg and /etc/a2ps-site.cfg files. Reading manuals may also help.

2012-05-01

Proper Unicode Serbo-Croatian digraphs

Here in ex-Yugoslavia we have three letters that are nearly never properly spelled out: digraphs "DŽ", "LJ" and "NJ" (Cyrillic corresponding letters are "Џ", "Љ" and "Њ". In fact, the standard Yugoslav keyboard doesn't even have corresponding keys:

Though in most fonts "NJ" (digraph, single letter) is visually [nearly] indistinguishable from "NJ" (letters "N" and "J"), some practical concerns still apply. Eg., the length of word "injekcija" in any case will be correctly counted, as it has two letters "n" and "j" (Cyr. "инјекција"), but the length of the word "njen" (Cyr. "њен") will depend on the way it is entered, and if it is spelled incorrectly, will be reported to be one letter longer and will get improperly sorted.

With the introduction of Unicode the digraphs received their own range: 0x01C4–0x01CC; so in theory we got a chance to fix the problem. Still, until yet to be released Windows 8 Microsoft's operating systems simply didn't render the single-letter Unicode versions digraphs, so using them in a wild was problematic.

The situation was quite a bit better in X11 environments: the "Unicode" family of XKB keymaps for Serbo-Croatian language1 maps these digraphs to the keys where Cyrillic counterparts can be found in standard layout. Still this approach isn't very useful, as it dismisses the one of the design goals of the Yugoslav keyboard layout standard: the ability to enter text in virtually any language – the digraphs replace letters "Q", "W" and "X", that are used quite extensively in English and many other languages.

As my needs demand being able to write also in English, French, German and Russian, I had three options:

  1. use two separate Latin layouts (one for digraphs, one for "Q", "W" and "X");
  2. create custom XKBlayout with some kind of magic;
  3. use Compose X11 extention to create the key mappings.

The first option was rejected from the very beginning: as i already have Cyrillic layout for typing in Russian (no native Latin alphabet to date, unfortunately), switching between three layouts would certainly create too much confusion to overcome the benefit of making less moves to get things done. The second option, while generally appealing (I already had to create a layout for typing Russian on Yugoslav keyboard without pain), had significant flow: unlike the rest of Latin letter the digraphs have 3 (yes, three) cases: lower, upper and title ("njen", "NJEN" and "Njen" respectively), so the letters can't be simply mapped to third level "L", "N" and "D".

So I was left with the only option to define my custom ~/.XCompose list:

<Multi_key> <D> <Zcaron> : U01c4
<Multi_key> <D> <zcaron> : U01c5
<Multi_key> <d> <zcaron> : U01c6
<Multi_key> <L> <J> : U01c7
<Multi_key> <L> <j> : U01c8
<Multi_key> <l> <j> : U01c9
<Multi_key> <N> <J> : U01ca
<Multi_key> <N> <j> : U01cb
<Multi_key> <n> <j> : U01cc

Given the fact that this solution allowed me to avoid relearning my Serbo-Croiatian typing skills (I just have to press a Compose2 key once before entering digraph), I would say that this solution is nearly perfect. The only caveat is the necessity of amending ~/.xsession script with the export GTK_IM_MODULE=xim (similar variable exists for Qt which I don't have).

Similarly the support for other weird Latin letters can be added if needed simply by amending this file.

P.S.: I'm still somehow uncomfortable with two features of standard Yugoslav keyboard: pressing 3 keys to emit "^" character drives me nuts. Though I don't use the "Ł" and "ł" letters, having them on different keys ("K" and "L" respectively) also seems insane. Probably it's time to make a dvorak-like layout for Serbo-Croatian...


1 The Special Olympics naming conventions for Serbo-Croatian language include "Bosnian", "Croatian", "Montenegrin" or "Serbian".

2 On my ThinkPad Edge E325 there is a PrtSc key next to AltGr; as I don't take screenshots regularly, using "compose:pscr" XKB option was a natural choice.