[PATCH] Support kernel-based LIRC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
>From 1aeb51dea6f432cb4dd9769d9cd9e94ba30c00ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@xxxxxx>
Date: Sun, 6 Nov 2022 15:44:31 +0200
Subject: [PATCH] Support kernel-based LIRC

The Linux Infrared Remote Control (LIRC) started as a user-space driver
that offered a Unix Domain Socket based interface to other processes.

Nowadays, there is a LIRC driver in the Linux kernel, which supports
LIRC_MODE_SCANCODE for reporting key codes in addition to raw scan codes.
The key codes and the low-level interface can be configured with
ir-keytable.

We introduce the option -k or --klirc (default: /dev/lirc0) for
interfacing to the kernel-based driver.
---
 Make.config.template |   1 +
 Makefile             |   3 ++
 klirc.c              | 105 +++++++++++++++++++++++++++++++++++++++++++
 klirc.h              |  27 +++++++++++
 vdr.c                |  11 +++++
 5 files changed, 147 insertions(+)
 create mode 100644 klirc.c
 create mode 100644 klirc.h

diff --git a/Make.config.template b/Make.config.template
index 82d55617..957f398e 100644
--- a/Make.config.template
+++ b/Make.config.template
@@ -73,6 +73,7 @@ endif
 #PLGCFG = $(CONFDIR)/plugins.mk
 
 ### The remote control:
+KLIRC_DEVICE = /dev/lirc0
 LIRC_DEVICE = /var/run/lirc/lircd
 
 ### Define if you always want to use LIRC, independent of the --lirc option:
diff --git a/Makefile b/Makefile
index a251f903..f4df3ad2 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,7 @@ SILIB    = $(LSIDIR)/libsi.a
 
 OBJS = args.o audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o\
        dvbplayer.o dvbspu.o dvbsubtitle.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
+       klirc.o \
        lirc.o menu.o menuitems.o mtd.o nit.o osdbase.o osd.o pat.o player.o plugin.o positioner.o\
        receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
        skinclassic.o skinlcars.o skins.o skinsttng.o sourceparams.o sources.o spu.o status.o svdrp.o themes.o thread.o\
@@ -120,8 +121,10 @@ DEFINES += -DSDNOTIFY
 LIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libsystemd-daemon || $(PKG_CONFIG) --libs libsystemd)
 endif
 
+KLIRC_DEVICE ?= /dev/lirc0
 LIRC_DEVICE ?= /var/run/lirc/lircd
 
+DEFINES += -DKLIRC_DEVICE=\"$(KLIRC_DEVICE)\"
 DEFINES += -DLIRC_DEVICE=\"$(LIRC_DEVICE)\"
 DEFINES += -DVIDEODIR=\"$(VIDEODIR)\"
 DEFINES += -DCONFDIR=\"$(CONFDIR)\"
diff --git a/klirc.c b/klirc.c
new file mode 100644
index 00000000..5ccbd047
--- /dev/null
+++ b/klirc.c
@@ -0,0 +1,105 @@
+/*
+ * klirc.c: LIRC remote control
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#include "klirc.h"
+#include <sys/ioctl.h>
+
+cKLircRemote::cKLircRemote(const char *DeviceName)
+:cRemote("KLIRC")
+,cThread("KLIRC remote control")
+{
+  Connect(DeviceName);
+  Start();
+}
+
+cKLircRemote::~cKLircRemote()
+{
+  int fh = f;
+  f = -1;
+  Cancel();
+  if (fh >= 0)
+     close(fh);
+}
+
+inline void cKLircRemote::Connect(const char *DeviceName)
+{
+  unsigned mode = LIRC_MODE_SCANCODE;
+  f = open(DeviceName, O_RDONLY, 0);
+  if (f < 0)
+     LOG_ERROR_STR(DeviceName);
+  else if (ioctl(f, LIRC_SET_REC_MODE, &mode)) {
+     LOG_ERROR_STR(DeviceName);
+     close(f);
+     f = -1;
+     }
+}
+
+bool cKLircRemote::Ready(void)
+{
+  return f >= 0;
+}
+
+void cKLircRemote::Action(void)
+{
+  if (f < 0)
+     return;
+  cTimeMs FirstTime;
+  cTimeMs LastTime;
+  cTimeMs ThisTime;
+  uint32_t LastKeyCode = 0;
+  uint16_t LastFlags = false;
+  bool pressed = false;
+  bool repeat = false;
+  int timeout = -1;
+
+  while (Running()) {
+        lirc_scancode sc;
+        bool ready = cFile::FileReady(f, timeout);
+        int ret = ready ? safe_read(f, &sc, sizeof sc) : -1;
+
+        if (ready && ret > 0) {
+           int Delta = ThisTime.Elapsed(); // the time between two subsequent LIRC events
+           ThisTime.Set();
+           if (!(sc.flags & LIRC_SCANCODE_FLAG_REPEAT)) { // new key pressed
+              if (sc.keycode == LastKeyCode &&
+                  (sc.flags ^ LastFlags) & LIRC_SCANCODE_FLAG_TOGGLE &&
+                  FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
+                 continue; // skip keys coming in too fast
+              if (repeat)
+                 Put(LastKeyCode, false, true); // generated release for previous repeated key
+              LastKeyCode = sc.keycode;
+              LastFlags = sc.flags;
+              pressed = true;
+              repeat = false;
+              FirstTime.Set();
+              timeout = -1;
+              }
+           else if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
+              continue; // repeat function kicks in after a short delay
+           else if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
+              continue; // skip same keys coming in too fast
+           else {
+              pressed = true;
+              repeat = true;
+              timeout = Delta * 3 / 2;
+              }
+           if (pressed) {
+              LastTime.Set();
+              Put(sc.keycode, repeat);
+              }
+           }
+        else {
+           if (pressed && repeat) // the last one was a repeat, so let's generate a release
+              Put(LastKeyCode, false, true);
+           pressed = false;
+           repeat = false;
+           LastKeyCode = 0;
+           timeout = -1;
+           }
+        }
+}
diff --git a/klirc.h b/klirc.h
new file mode 100644
index 00000000..c3dca52e
--- /dev/null
+++ b/klirc.h
@@ -0,0 +1,27 @@
+/*
+ * klirc.h: Kernel Linux Infrared Remote Control
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ */
+
+#ifndef __KLIRC_H
+#define __KLIRC_H
+
+#include <linux/lirc.h>
+#include "remote.h"
+#include "thread.h"
+
+class cKLircRemote : public cRemote, private cThread {
+private:
+  int f;
+  virtual void Action(void);
+  inline void Connect(const char *DeviceName);
+public:
+  cKLircRemote(const char *DeviceName);
+  virtual ~cKLircRemote();
+  virtual bool Ready(void);
+  };
+
+#endif //__KLIRC_H
diff --git a/vdr.c b/vdr.c
index 06c0c9a9..8136c98a 100644
--- a/vdr.c
+++ b/vdr.c
@@ -53,6 +53,7 @@
 #include "interface.h"
 #include "keys.h"
 #include "libsi/si.h"
+#include "klirc.h"
 #include "lirc.h"
 #include "menu.h"
 #include "osdbase.h"
@@ -240,6 +241,7 @@ int main(int argc, char *argv[])
 
   bool UseKbd = true;
   const char *LircDevice = NULL;
+  const char *KLircDevice = NULL;
 #if !defined(REMOTE_KBD)
   UseKbd = false;
 #endif
@@ -281,6 +283,7 @@ int main(int argc, char *argv[])
       { "grab",     required_argument, NULL, 'g' },
       { "help",     no_argument,       NULL, 'h' },
       { "instance", required_argument, NULL, 'i' },
+      { "klirc",    optional_argument, NULL, 'k' },
       { "lib",      required_argument, NULL, 'L' },
       { "lirc",     optional_argument, NULL, 'l' | 0x100 },
       { "localedir",required_argument, NULL, 'l' | 0x200 },
@@ -405,6 +408,9 @@ int main(int argc, char *argv[])
                        }
                     fprintf(stderr, "vdr: invalid instance id: %s\n", optarg);
                     return 2;
+          case 'k':
+                    KLircDevice = optarg ? optarg : KLIRC_DEVICE;
+                    break;
           case 'l': {
                     char *p = strchr(optarg, '.');
                     if (p)
@@ -593,6 +599,8 @@ int main(int argc, char *argv[])
                "                           if logging should be done to LOG_LOCALn instead of\n"
                "                           LOG_USER, add '.n' to LEVEL, as in 3.7 (n=0..7)\n"
                "  -L DIR,   --lib=DIR      search for plugins in DIR (default is %s)\n"
+               "            --klirc[=PATH] use a Linux kernel LIRC remote control device, attached to PATH\n"
+               "                           (default: %s)\n"
                "            --lirc[=PATH]  use a LIRC remote control device, attached to PATH\n"
                "                           (default: %s)\n"
                "            --localedir=DIR search for locale files in DIR (default is\n"
@@ -629,6 +637,7 @@ int main(int argc, char *argv[])
                DEFAULTEPGDATAFILENAME,
                MAXVIDEOFILESIZEDEFAULT,
                DEFAULTPLUGINDIR,
+               KLIRC_DEVICE,
                LIRC_DEVICE,
                DEFAULTLOCDIR,
                DEFAULTSVDRPPORT,
@@ -874,6 +883,8 @@ int main(int argc, char *argv[])
      }
 
   // Remote Controls:
+  if (KLircDevice)
+     new cKLircRemote(KLircDevice);
   if (LircDevice)
      new cLircRemote(LircDevice);
   if (!DaemonMode && HasStdin && UseKbd)
-- 
2.38.1

_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
https://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux