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
diff --git a/lirc.c b/lirc.c
index 9cc287a6..d7797009 100644
--- a/lirc.c
+++ b/lirc.c
@@ -10,19 +10,13 @@
*/
#include "lirc.h"
-#include <netinet/in.h>
-#include <sys/socket.h>
-
-#define RECONNECTDELAY 3000 // ms
+#include <sys/ioctl.h>
cLircRemote::cLircRemote(const char *DeviceName)
:cRemote("LIRC")
,cThread("LIRC remote control")
{
- addr.sun_family = AF_UNIX;
- strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
- if (!Connect())
- f = -1;
+ Connect(DeviceName);
Start();
}
@@ -35,18 +29,17 @@ cLircRemote::~cLircRemote()
close(fh);
}
-bool cLircRemote::Connect(void)
+inline void cLircRemote::Connect(const char *DeviceName)
{
- if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
- if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
- return true;
- LOG_ERROR_STR(addr.sun_path);
+ 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;
}
- else
- LOG_ERROR_STR(addr.sun_path);
- return false;
}
bool cLircRemote::Ready(void)
@@ -56,50 +49,30 @@ bool cLircRemote::Ready(void)
void cLircRemote::Action(void)
{
+ if (f < 0)
+ return;
cTimeMs FirstTime;
cTimeMs LastTime;
cTimeMs ThisTime;
- char buf[LIRC_BUFFER_SIZE];
- char LastKeyName[LIRC_KEY_BUF] = "";
+ uint32_t LastKeyCode = 0;
bool pressed = false;
bool repeat = false;
int timeout = -1;
while (Running()) {
-
- bool ready = f >= 0 && cFile::FileReady(f, timeout);
- int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
-
- if (f < 0 || ready && ret <= 0) {
- esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
- if (f >= 0)
- close(f);
- f = -1;
- while (Running() && f < 0) {
- cCondWait::SleepMs(RECONNECTDELAY);
- if (Connect()) {
- isyslog("reconnected to lircd");
- break;
- }
- }
- }
+ lirc_scancode sc;
+ bool ready = cFile::FileReady(f, timeout);
+ int ret = ready ? safe_read(f, &sc, sizeof sc) : -1;
if (ready && ret > 0) {
- buf[ret - 1] = 0;
- int count;
- char KeyName[LIRC_KEY_BUF];
- if (sscanf(buf, "%*x %x %29s", &count, KeyName) != 2) { // '29' in '%29s' is LIRC_KEY_BUF-1!
- esyslog("ERROR: unparsable lirc command: %s", buf);
- continue;
- }
int Delta = ThisTime.Elapsed(); // the time between two subsequent LIRC events
ThisTime.Set();
- if (count == 0) { // new key pressed
- if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
+ if (!(sc.flags & LIRC_SCANCODE_FLAG_REPEAT)) { // new key pressed
+ if (sc.keycode == LastKeyCode && FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
continue; // skip keys coming in too fast
if (repeat)
- Put(LastKeyName, false, true); // generated release for previous repeated key
- strn0cpy(LastKeyName, KeyName, sizeof(LastKeyName));
+ Put(LastKeyCode, false, true); // generated release for previous repeated key
+ LastKeyCode = sc.keycode;
pressed = true;
repeat = false;
FirstTime.Set();
@@ -116,15 +89,15 @@ void cLircRemote::Action(void)
}
if (pressed) {
LastTime.Set();
- Put(KeyName, repeat);
+ Put(sc.keycode, repeat);
}
}
else {
if (pressed && repeat) // the last one was a repeat, so let's generate a release
- Put(LastKeyName, false, true);
+ Put(LastKeyCode, false, true);
pressed = false;
repeat = false;
- *LastKeyName = 0;
+ LastKeyCode = 0;
timeout = -1;
}
}
diff --git a/lirc.h b/lirc.h
index 3c4735a8..ff6b01c9 100644
--- a/lirc.h
+++ b/lirc.h
@@ -10,17 +10,15 @@
#ifndef __LIRC_H
#define __LIRC_H
-#include <sys/un.h>
+#include <linux/lirc.h>
#include "remote.h"
#include "thread.h"
class cLircRemote : public cRemote, private cThread {
private:
- enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
int f;
- struct sockaddr_un addr;
virtual void Action(void);
- bool Connect(void);
+ inline void Connect(const char *DeviceName);
public:
cLircRemote(const char *DeviceName);
virtual ~cLircRemote();
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
https://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr