Re: How to use /dev/lirc0 in VDR?

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

 



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

[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