Valid HTML 4.01 Transitional
Prev: Debugging XDMCP X-Windows Service Next: Home Network Dies on Cable Due To Small MTU
(Index)
Jim Carter's Bugfixes

Infrared Remote Control as Keyboard

James F. Carter
2010-01-01
Symptom:

Starting with kernel 2.6.32 or thereabout, your infrared remote control stops working. Some keys still work, like arrow keys, VolumeUp or VolumeDown, and Enter, but many others produce no effect, such as Play and numeric keys.

What's happening:

There's a new paradigm in the world of remote controls: they have turned into keyboards, and feed their keycodes into the event subsystem where the keycodes are interpreted just as if they had been typed on the real keyboard. In particular, in the very likely case that the X Window System is in use, any software that has the keyboard focus can receive the keysyms, without special configuration.

In particular, the LIRC daemon (Linux InfraRed Control) is now deprecated, although it can still be used if its configuration is adjusted.

There is a very small fly in the ointment though: there are two stages where the various drivers have to know which key means what, and if a key is missing or wrong it will not work, producing the symptom complained about.

This whole situation results in a lot of frustrated users posting in forums, with not a whole lot of documentation about what happened and what to do about it. Here is what I did to recover, as the WAF (Wife Acceptance Factor) of my home theater PC dwindled daily.

Naturally this is rather complicated, since the problem is nontrivial. My remote control is a Philips eHome Media Center Edition (MCE) Infrared Remote Control (and receiver), and I will be giving examples for this device, but the same principles apply to all remote control families.

The kernel drivers I have loaded are:

The source code for these drivers is found in /usr/src/linux/drivers/media/rc for kernel 3.2.10, or /usr/src/linux/drivers/media/IR for 2.6.37; I'm not sure when the containing directory was renamed.

First, you need a driver that interacts with USB to obtain infrared data from the receiver. For me this is mceusb. The most common arrangement, including for MCE, is for the driver to deliver a sequence of pulse widths, i.e. how long the signal LED was on, then how long off, and repeating for all the pulses in the signal. Some IR equipment can also transmit, for controlling a closed-source set top box, and it will be told a similar sequence of pulse widths.

Next, the sequence of pulse widths is passed to ir_rc6_decoder and turned bitwise into an integer called the scancode. This is analogous to the scancodes delivered by a normal keyboard. There are several protocols for interpreting the serial IR data, and there are similar decoder modules for rc5 (two variants), jvc, nec, and sony. In addition, LIRC has a codec for capturing raw data. You simplify your life if you blacklist the decoders that you aren't using; some decoders decode promiscuously and produce double keycodes per button press.

Here is how to blacklist a driver. Create a file in /etc/modprobe.d with the form number-basename.conf, e.g. in my case /etc/modprobe.d/49-lirc.J.conf . The content would normally be similar to blacklist ir_sony_decoder, but this only suppresses autoloading upon hardware detection, and we need to really suppress the driver in all contexts. Here is what I use:


install ir_sony_decoder /bin/true
install ir_jvc_decoder /bin/true
install ir_rc5_decoder /bin/true
install ir_nec_decoder /bin/true

I'm leaving alone the ir_lirc_codec and ir_mce_kbd_decoder although I am not using LIRC any more, and ir_mce_kbd_decoder appears to be for a complete infrared keyboard that I don't have.

Next the scancode is interpreted using the default keymap in rc_rc6_mce (source is in the keymaps subdirectory). This translates scancodes to keycodes. All devices using the event subsystem are supposed to translate their idiosyncratic scancodes into the same set of keycodes, which are defined in /usr/src/linux/include/linux/input.h (which is similar but not identical to /usr/include/linux/input.h). Both the scancodes and keycodes come out on /dev/input/event${N} for some N. Look at /proc/bus/input/devices to find out the event device: under N: find the name of the device, then under H: find out its handlers which include the event device's basename.

/lib/udev/rules.d/51-lirc.rules causes any device with IR in its name to receive a symlink /dev/input/ir , such as in my case the Microsoft Wireless Laser Mouse 8000, which is not very useful. This rule file could be overridden (you add a file in /etc/udev/rules.d) to make sure the real IR device gets the symlink.

If you install the v4l-utils package it gives you copies of the official keymaps that are hardwired in rc_rc6_mce and its numerous friends. These files end up in /etc/rc_keymaps . This is for OpenSuSE 11.4, and other distros (I think Ubuntu is one) may put them in another directory, and you have to copy the actually used keymap into /etc/rc_keymaps .

You also get (among others) the ir-keytable command. If you execute it without command line arguments it will report the number (here rc3) of the remote control, the /dev/input/event${N} device, and data about the device. In my case:

Found /sys/class/rc/rc3/ (/dev/input/event8) with:
        Driver mceusb, table rc-rc6-mce
        Supported protocols: RC-6 LIRC other 
        Enabled protocols: RC-6 
        Repeat delay = 500 ms, repeat period = 125 ms

In a modern UNIX distro, the HAL daemon is aware when an input device is hotplugged, and it signals the X Window System to add any such devices as keyboards or mice. Unless overridden, the X-server uses the evdev driver with the standard keycodes from /usr/share/X11/xkb/keycodes/evdev . This file maps the standard event system keycodes into code names for the keys. Drivers other than evdev have their idiosyncratic mappings, generally from raw scancodes to keycode names.

Then the keycode names are interpreted according to /usr/share/X11/xkb/symbols/us and /usr/share/X11/xkb/symbols/inet . This maps keycode names to keysyms, which are semantic interpretations of the keys such as parenright or XF86AudioRewind. The keysyms are defined in /usr/include/X11/keysymdef.h (for standard keysyms) or /usr/include/X11/XF86keysym.h (for multimedia keysyms). These keysyms are what goes out to the applications.

Assuming of course that your key generates an event keycode that maps to a keycode name, and the keycode name is in the symbol table and maps to a useable keysym, which is not the case for quite a number of keys on the remote control.

How to fix:

First install the v4l-utils package, execute ir-keytable (no arguments), and identify the keytable that you are using. Make sure the protocol is appropriate for the keytable, e.g. in my case, protocol RC-6 for keytable rc6_mce. Find the keytable in /etc/rc_keymaps. For me it is /etc/rc_keymaps/rc6_mce. View this file. If there is a question which keytable is being used, or whether the keytable has been trashed, do ir-keytable -r and compare what it prints with the contents of the keytable file. The file has lines such as:

0x800f0414 KEY_FASTFORWARD
0x800f0415 KEY_REWIND
0x800f0416 KEY_PLAY
0x800f0417 KEY_RECORD
0x800f0418 KEY_PAUSE

To test the remote control, execute ir-keytable -t. If you have hotplugged your infrared receiver several times, ir-keytable may report that the sysfs device is something other than /sys/class/rc/rc0, and ir-keytable -t will fail to find this device since the default is rc0. In my case it's rc3, so I would do ir-keytable -s rc3 -t.

Press each button on your remote control. ir-keytable will print out the scancode in hex, and then the event keycode by name and number. If any button produces a scancode but no keycode, verify that the scancode is missing from the keytable. Later when you edit the keytable file, you can add this scancode and create a keycode to go with it. In my case, /etc/rc_keymaps/rc6_mce had a keycode with plausible semantics for every actually produced scancode, plus more scancode-keycode combinations for what appears to be a complete keyboard, which I don't have. By plausible I mean for example that the keycode for the record button is called KEY_RECORD and not something irrelevant.

Now go through /usr/share/X11/xkb/keycodes/evdev and find the key name that goes with the event keycode. This has lines like:

	<AE10> = 19;					   (zero key)
        <I150> = 150;   // #define KEY_SLEEP               142
	<I175> = 175;   // #define KEY_RECORD              167
        <I171> = 171;   // #define KEY_NEXTSONG            163
        <I173> = 173;   // #define KEY_PREVIOUSSONG        165

Generally the numeric value that goes with the key name is 8+event keycode. If any keycode lacks a key name, you will have to pick a different keycode that has a key name. For example, rc6_mce assigns KEY_NEXT to scancode 0x800f041a, but KEY_NEXT has no key name. Therefore I changed the assignment to KEY_NEXTSONG, which does have an assigned key.

Now that you have the key names, check in /usr/share/X11/xkb/symbols/us (or whichever one your X-server is using) and /usr/share/X11/xkb/symbols/inet. If a key name does not appear in the symbol tables, you will have to pick a key that does appear, and assign its event keycode to the remote control's scancode. Or you can create your own symbol table fragment and arrange for it to be loaded.

Changes in rc6_mce:

These are the changes I had to make in /etc/rc_keymaps/rc6_mce so that every button produces a keysym.

Label Scancode Keycode Formerly Key Name Keysym
0 0x800f0400 KEY_0 KEY_NUMERIC_0 <AE10> ASCII zero
--And similarly for all the numeric keys
(i) 0x800f040f KEY_MENU KEY_INFO <I147> XF86MenuKB
ch+ 0x800f0412 KEY_SCROLLUP KEY_CHANNELUP <I185> XF86ScrollUp
ch- 0x800f0412 KEY_SCROLLDOWN KEY_CHANNELDOWN <I185> XF86ScrollDown
>| 0x800f041a KEY_NEXTSONG KEY_NEXT <I171> XF86AudioNext
|< 0x800f041b KEY_PREVIOUSSONG KEY_PREVIOUS <I172> XF86AudioPrev
# 0x800f041c KEY_KPCOMMA KEY_NUMERIC_POUND <I126> plusminus
--It's not clear why it comes out as key I126 rather than the nonexistent I129, but it does.
* 0x800f041d KEY_KPPLUSMINUS KEY_NUMERIC_STAR <KPDL> KP_Decimal
--It's not clear why it comes out as key KPDL rather than I126, but it does.
OK 0x800f0422 KEY_SEND KEY_OK <I239> XF86Send
O 0x800f0424 KEY_MSDOS KEY_DVD <I159> XF86DOS
-> TV0x800f0425 KEY_CALC KEY_TUNER <I148> XF86Calculator
--Label looks like a TV with a play button, so it's the PVR.
Guide 0x800f0426 KEY_FILE KEY_EPG <I152> XF86Explorer
Red TV 0x800f0448 KEY_CAMERA KEY_PVR <I220> XF86WebCam
--Label looks like on-air capture, so it's the live TV button.

As a next step, not covered here, you will need to train the application(s) to recognize the keysyms that they will be given.

Download links for fixed files:

Another nasty detail:

At least in kernel 3.2.10, if you plug my IR receiver into a USB-3.0 (xhci) port, or a hub connected to USB-3.0, it will deliver no data. Workaround: use a USB-2.0 port. This is on a Zotac ZBOX AD03BR.


Prev: Debugging XDMCP X-Windows Service Next: Home Network Dies on Cable Due To Small MTU
(Index)