From patchwork Sun Nov 6 14:39:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?TWFya28gTcOka2Vsw6Q=?= X-Patchwork-Id: 87177 Received: from [127.0.0.1] by www.linuxtv.org with esmtp (Exim 4.92) (envelope-from ) id 1orgoY-00F5HV-Ty; Sun, 06 Nov 2022 14:39:58 +0000 Received: from lahtoruutu.iki.fi ([185.185.170.37]) by www.linuxtv.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1orgoU-00F5Gw-HS for vdr@linuxtv.org; Sun, 06 Nov 2022 14:39:57 +0000 Received: from jyty (dsl-hkibng31-54fae6-199.dhcp.inet.fi [84.250.230.199]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: msmakela) by lahtoruutu.iki.fi (Postfix) with ESMTPSA id E8B7B1B0001D for ; Sun, 6 Nov 2022 16:39:48 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=lahtoruutu; t=1667745589; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=vH3fLSyFNFFYpv8ZXeH0Wk0UB4PXUcF+F1+0VgRtEE4=; b=Eu57mCmTSYB4RgzeWy1e8TphlCFUrmKSxSpVRpUBaEaxrN9SUlMgq56SE6yAU1ptL9BJha fjI6YUDsJIdGx3kw2WssJU/pPvMYZvRtbuQhSkGD7iVz7C/Hm9mSbZtvJQLXt7/xBDQi/8 MuleWN3mip2HLCiMdporXfWVH8W+9ZMqpXOcBihzfOnrMuIJjtxr5fYqjhqVclQ/CUsYJ4 D64wwEdyUcx29nBqiXJWVfUUnKYctO4MOwtFU4r09FYdCIeULXJpFPwtJPtO9I/ZXBH3gM wRCPp/IUenNShHBcOEdKQj9bKSV30lxCpQcm3FZB9YUfAi77pvTr50o2vkIB3w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=lahtoruutu; t=1667745589; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=vH3fLSyFNFFYpv8ZXeH0Wk0UB4PXUcF+F1+0VgRtEE4=; b=iU6oR/ozF5S1OCwdihjr4/vokw1l0QpTyinUY1URPpCrW6UiFB4MEOPsiJM98MUc46fOla Zb+0sAyzYSbOQMMnNHRpdONgaVTog9WBU/tD1SHHN9nZdd6JefGrWgyDxFLZ8UXiv5z1Nx KpwBlQpBrE4rBxO1so27aNjAgbwyD36EvQpfCG8+/ebTzXxo6UfcNPuKPgoVYeW0jQiRnx oLv9H8L217c9cVv3dMfxhuJhSgPpq5V90siP+iGUq+uufI3VMm+8f1P6XnO/HkOMFHfoH0 CRGlpkF5JPrzg4u7ffYc+rKMLNWjNYAvVcoUpjSJsRMBKoIlIYtDZ+TWIPgN7Q== ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=msmakela smtp.mailfrom=marko.makela@iki.fi ARC-Seal: i=1; s=lahtoruutu; d=iki.fi; t=1667745589; a=rsa-sha256; cv=none; b=MIKQqA3aIHJcLmjw+IAuEexpjWtwLVCqlVeGDfHMEmBVryM5pQ76n0xo3mT0oaKZMzvbqe kFin7ie/kckyQfpR0XC5doizQ3odVC8PvFKE0XWbeCX4JcApEW1ZvTl7QU0njbWxZ0K9Er JdSKiBNAS8ewm39k0Xkxl1V0Zu4kCdD4sBB11HkheJ7ioqrZKmgdqlu0Kc6GlI2VVitO+h zNYk3Yu969bNrnUAWh//NR69OMTOK3tVQYnp3m7kziuiJaR//h/+o9GbbsTpCinPMhjASm QjLETPvYVzJ3QypflPaEjnVlyxQyRYCDfXk4X1FNnoNWshLe1su03NmrDMZZzw== Date: Sun, 6 Nov 2022 16:39:47 +0200 From: Marko =?iso-8859-1?q?M=E4kel=E4?= To: VDR Mailing List Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-LSpam-Score: -1.8 (-) X-LSpam-Report: No, score=-1.8 required=5.0 tests=BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FROM_EXCESS_BASE64=0.979, RCVD_IN_DNSWL_LOW=-0.7 autolearn=ham autolearn_force=no Subject: [vdr] [PATCH] Support kernel-based LIRC X-BeenThere: vdr@linuxtv.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: VDR Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: VDR Mailing List Errors-To: vdr-bounces@linuxtv.org Sender: "vdr" 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?= 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 + +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 +#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