Hi all,
TL;DR: How could I connect VDR to the kernel-provided /dev/lirc0 device? Is there a dummy lircd implementation that would simply open /dev/lirc0 in LIRC_MODE_SCANCODE and relay its contents over a socket?
As far as I understand, LIRC was first implemented as a user-space daemon that would decode a bitstream (via various bit-banging interfaces) into scancodes that would then be relayed over a socket to the final application.
At some point (before I started to use VDR in 2005 or 2006), support for remote control units was expanded in the Linux kernel, and also a translation into input events was implemented. In VDR, the input event interface can be used via the "remote" plugin (vdr -Premote). I think that this is what I always used with VDR, first using the cx88 driver (Hauppauge Nova-T PCI 90002) and now trying to use a USB stick (rtl82xxu, Astrometa DVB-T2) on a Raspberry Pi.
The input event interface is mapping the stream of IR messages to "key-down", "key-up" and "key-repeat" events, which adds some inaccuracy. For the rtl82xxu, the repeat logic is broken: key-repeat or key-down events for long key presses are only being sent intermittently. The fix in https://patchwork.linuxtv.org/project/linux-media/list/?series=7322 improves it a lot. The USB interface for delivering IR events in a 128-byte buffer is prone to race conditions, and that cannot be fixed without changing the device firmware.
There is also a kernel-based interface (such as /dev/lirc0) that can provide scan codes to end applications. Both the input event interface and this one would be polled by "ir-keytable -t".
I would like to use the /dev/lirc0 interface with VDR, so that VDR has a chance to react to each and every IR message that is sent by the remote control unit. I think that this would minimize the disturbance caused by the broken USB protocol of the rtl82xxu. It is OK if one LIRC scancode of a long keypress (say, browsing a list of recordings) would be lost; another one would be sent in 113ms by the RC5 protocol. With the input event driver, a lost IR message would result in a bogus key-up event, a bogus key-down event, and a long delay before key-repeat events are generated again.
My initial attempt at using /dev/lirc0 did not work:
vdr -v /var/lib/vdr/video --no-kbd --lirc=/dev/lirc0 -Prpihddevice
That is, pressing any buttons on the remote control unit did not have any effect, and VDR did not start in the "learning mode" either, even after I renamed the remote.conf that it is opening at startup. It appeared that a read() on "/dev/lirc0" would block indefinitely, but I did not check that.
I noticed that VDR's lirc.c is explicitly opening a Unix domain socket to the LIRC device, while "ir-keytable -t" would invoke open(2) followed by ioctl(lircfd, LIRC_SET_REC_MODE, &mode): https://git.linuxtv.org/v4l-utils.git/tree/utils/keytable/keytable.c
My goal would be to use the Hauppauge remote control with the Astrometa DVB-T2 stick and for it to be as responsive as it was back in 2006 with my patched cx88 driver. My implementation back then directly mapped IR messages to input events:
(1) produced a key-down event for the first IR message (2) discard the first repeated IR message (to have an initial delay) (3) produced key-repeat events for subsequent IR messages (every 113ms) (4) if any key-up events were produced, that would be after a timeout that would be reset in (1),(2),(3).
I think that it should be possible to implement this behaviour in a dummy lircd that translates /dev/lirc0 into a socket. Before I implement that from the scratch, I would like to know if something similar already exists.
Related to this, I submitted a fix to the kernel to set the "repeat" flag in the LIRC scan codes when appropriate: https://patchwork.linuxtv.org/project/linux-media/list/?series=8338 Without this fix, it should still be possible to detect repeat key events (long key presses) by comparing successive scancodes. For protocols that do not include a toggle flag (like RC5 does), it might be possible to compare timestamps to distinguish long button presses from multiple short presses.
Best regards,
Marko
Mon, Jul 18, 2022 at 09:36:55AM +0300, Marko Mäkelä wrote:
TL;DR: How could I connect VDR to the kernel-provided /dev/lirc0 device? Is there a dummy lircd implementation that would simply open /dev/lirc0 in LIRC_MODE_SCANCODE and relay its contents over a socket?
I wrote a simple converter (attached) that is compatible with vdr --lirc=/dev/shm/lirc. Alas, as far as I can tell, the socket interface does not currently allow any "repeat" flag to be passed explicitly. Instead, lirc.c will detect long keypresses by itself based on some time stamps or timeouts, and then pass the parameter to cRemote::Put().
The Linux kernel documentation gave me the impression that the /dev/input/event interface is the modern way to handle any input.
I wonder if a /dev/input/event interface could be implemented natively in the VDR core. It would remove the need for both lircd and vdr-plugin-remote in many typical installations.
It seems that in any case, I'd better write a converter from /dev/lirc0 to /dev/uinput, to have the key events generated in my way, slightly differently from the kernel's built-in /dev/input/event driver.
Marko
Wed, Jul 20, 2022 at 12:34:19PM +0300, Marko Mäkelä wrote:
Mon, Jul 18, 2022 at 09:36:55AM +0300, Marko Mäkelä wrote:
TL;DR: How could I connect VDR to the kernel-provided /dev/lirc0 device? Is there a dummy lircd implementation that would simply open /dev/lirc0 in LIRC_MODE_SCANCODE and relay its contents over a socket?
I wrote a simple converter (attached) that is compatible with vdr --lirc=/dev/shm/lirc. Alas, as far as I can tell, the socket interface does not currently allow any "repeat" flag to be passed explicitly. Instead, lirc.c will detect long keypresses by itself based on some time stamps or timeouts, and then pass the parameter to cRemote::Put().
I took a further step along this path and wrote an experimental patch that makes --lirc=/dev/lirc0 work (while completely breaking compatibility with lircd). The attached patch is only a proof of concept; a final version of this would have to be tied to a dedicated command line option.
I did not clean up the timer logic yet. The lirc_scancode events already contain monotonic timestamps; those could be used in order to reduce the number of system calls.
This patch assumes that the /dev/lirc0 interface is setting the LIRC_SCANCODE_FLAG_REPEAT for subsequent messages sent for a long key press, as in https://patchwork.linuxtv.org/project/linux-media/list/?series=8338 (and before that, only for some RCUs that send special "repeat" messages). The flag could be simulated in userspace by keeping track of the timestamps.
The Linux kernel documentation gave me the impression that the /dev/input/event interface is the modern way to handle any input.
I wonder if a /dev/input/event interface could be implemented natively in the VDR core. It would remove the need for both lircd and vdr-plugin-remote in many typical installations.
It seems that in any case, I'd better write a converter from /dev/lirc0 to /dev/uinput, to have the key events generated in my way, slightly differently from the kernel's built-in /dev/input/event driver.
I experimented with a program that would convert /dev/lirc0 to /dev/uinput, but it quickly got too complex for my taste, starting from the fact that all potential keycodes have to be declared upfront. While searching for information, I learned that lircd could already generate input events: https://www.lirc.org/html/lircd-uinput.html
The /dev/input/event driver in the kernel rc-core has (in my opinion) a design problem that the key-repeat events are triggered by an independent timer and not directly by the reception of IR messages from the RCU. When multiple clock sources are involved, timers will easily get out of sync, and in this case cause input lag: For example, a perceivable time (such as 0.1s) after a button was already released, the independent timer might fire one more key-repeat event.
VDR's existing LIRC interface avoids such input lag by always triggering Put() calls by the reception of IR messages. It merely "filters out" some IR messages by enforcing RcRepeatDelay and RcRepeatDelta.
Best regards,
Marko
I thought that it would be a good idea to make use of the built-in LIRC driver of the Linux kernel. Currently, there is a --lirc option for interfacing to a user-space driver (lircd), but nothing for using the kernel driver. The "remote" plugin can interface with /dev/input/event* but not with /dev/lirc* (except when REMOTE_FEATURE_LIRCOLD is enabled, to use an older protocol).
The kernel LIRC driver exposes a raw interface to the actual received IR messages, allowing applications to implement key-repeat more accurately than the input event driver.
The attached patch uses LIRC_MODE_SCANCODE, which reports key codes, scan codes and monotonic timestamps. This initial patch is based on cLircRemote and the timing logic was not simplified yet. The reported keycodes can be configured with ir-keytable.
I have tested this patch on Raspberry Pi 2 B, on a 5.10 kernel as well as on a 6.0.6 kernel that was built following the instructions at https://www.raspberrypi.com/documentation/computers/linux_kernel.html and choosing the rpi-6.0.y branch.
Best regards,
Marko