From 1aeb51dea6f432cb4dd9769d9cd9e94ba30c00ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@iki.fi>
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
@@ -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:
@@ -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)\"
new file mode 100644
@@ -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;
+ }
+ }
+}
new file mode 100644
@@ -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
@@ -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