vdr  2.7.6
lirc.c
Go to the documentation of this file.
1 /*
2  * lirc.c: LIRC remote control
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * LIRC support added by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
8  *
9  * $Id: lirc.c 5.3 2025/03/02 11:03:35 kls Exp $
10  */
11 
12 #include "lirc.h"
13 #include <linux/version.h>
14 #define HAVE_KERNEL_LIRC (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
15 // cLircUsrRemote
16 #include <netinet/in.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 // cLircDevRemote
20 #if HAVE_KERNEL_LIRC
21 #include <linux/lirc.h>
22 #include <sys/ioctl.h>
23 #endif
24 
25 #define RECONNECTDELAY 3000 // ms
26 
27 class cLircUsrRemote : public cLircRemote {
28 private:
29  enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
30  struct sockaddr_un addr;
31  bool Connect(void);
32  virtual void Action(void) override;
33 public:
34  cLircUsrRemote(const char *DeviceName);
35  };
36 
37 #if HAVE_KERNEL_LIRC
38 class cLircDevRemote : public cLircRemote {
39 private:
40  virtual void Action(void) override;
41 public:
42  cLircDevRemote(void);
43  bool Connect(const char *DeviceName);
44  };
45 #endif
46 
47 // --- cLircRemote -----------------------------------------------------------
48 
49 cLircRemote::cLircRemote(const char *Name)
50 :cRemote(Name)
51 ,cThread("LIRC remote control")
52 {
53 }
54 
56 {
57  int fh = f;
58  f = -1;
59  Cancel();
60  if (fh >= 0)
61  close(fh);
62 }
63 
64 void cLircRemote::NewLircRemote(const char *Name)
65 {
66 #if HAVE_KERNEL_LIRC
67  cLircDevRemote *r = new cLircDevRemote();
68  if (r->Connect(Name))
69  return;
70  delete r;
71 #endif
72  new cLircUsrRemote(Name);
73 }
74 // --- cLircUsrRemote --------------------------------------------------------
75 
76 cLircUsrRemote::cLircUsrRemote(const char *DeviceName)
77 : cLircRemote("LIRC")
78 {
79  addr.sun_family = AF_UNIX;
80  strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
81  Connect();
82  Start();
83 }
84 
86 {
87  if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
88  if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
89  return true;
90  LOG_ERROR_STR(addr.sun_path);
91  close(f);
92  f = -1;
93  }
94  else
95  LOG_ERROR_STR(addr.sun_path);
96  return false;
97 }
98 
100 {
101  return f >= 0;
102 }
103 
105 {
106  cTimeMs FirstTime;
107  cTimeMs LastTime;
108  cTimeMs ThisTime;
109  char buf[LIRC_BUFFER_SIZE];
110  char LastKeyName[LIRC_KEY_BUF] = "";
111  bool pressed = false;
112  bool repeat = false;
113  int timeout = -1;
114 
115  while (Running()) {
116 
117  bool ready = f >= 0 && cFile::FileReady(f, timeout);
118  int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
119 
120  if (f < 0 || ready && ret <= 0) {
121  esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
122  if (f >= 0)
123  close(f);
124  f = -1;
125  while (Running() && f < 0) {
127  if (Connect()) {
128  isyslog("reconnected to lircd");
129  break;
130  }
131  }
132  }
133 
134  if (ready && ret > 0) {
135  buf[ret - 1] = 0;
136  int count;
137  char KeyName[LIRC_KEY_BUF];
138  if (sscanf(buf, "%*x %x %29s", &count, KeyName) != 2) { // '29' in '%29s' is LIRC_KEY_BUF-1!
139  esyslog("ERROR: unparsable lirc command: %s", buf);
140  continue;
141  }
142  int Delta = ThisTime.Elapsed(); // the time between two subsequent LIRC events
143  ThisTime.Set();
144  if (count == 0) { // new key pressed
145  if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
146  continue; // skip keys coming in too fast
147  if (repeat)
148  Put(LastKeyName, false, true); // generated release for previous repeated key
149  strn0cpy(LastKeyName, KeyName, sizeof(LastKeyName));
150  pressed = true;
151  repeat = false;
152  FirstTime.Set();
153  timeout = -1;
154  }
155  else if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
156  continue; // repeat function kicks in after a short delay
157  else if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
158  continue; // skip same keys coming in too fast
159  else {
160  pressed = true;
161  repeat = true;
162  timeout = Delta * 3 / 2;
163  }
164  if (pressed) {
165  LastTime.Set();
166  Put(KeyName, repeat);
167  }
168  }
169  else {
170  if (pressed && repeat) // the last one was a repeat, so let's generate a release
171  Put(LastKeyName, false, true);
172  pressed = false;
173  repeat = false;
174  *LastKeyName = 0;
175  timeout = -1;
176  }
177  }
178 }
179 
180 // --- cLircDevRemote --------------------------------------------------------
181 
182 #if HAVE_KERNEL_LIRC
183 bool cLircDevRemote::Connect(const char *DeviceName)
184 {
185  unsigned mode = LIRC_MODE_SCANCODE;
186  f = open(DeviceName, O_RDONLY, 0);
187  if (f < 0) {
188  switch (errno) {
189  case ENXIO:
190  case ENODEV:
191  // Do not complain about an attempt to open a lircd socket file.
192  break;
193  default:
194  LOG_ERROR_STR(DeviceName);
195  }
196  }
197  else if (ioctl(f, LIRC_SET_REC_MODE, &mode)) {
198  LOG_ERROR_STR(DeviceName);
199  close(f);
200  f = -1;
201  }
202  if (f >= 0)
203  Start();
204  return f >= 0;
205 }
206 
208 :cLircRemote("DEV_LIRC")
209 {
210 }
211 
213 {
214  if (f < 0)
215  return;
216  uint64_t FirstTime = 0, LastTime = 0;
217  uint32_t LastKeyCode = 0;
218  uint16_t LastFlags = false;
219  bool SeenRepeat = false;
220  bool repeat = false;
221 
222  while (Running()) {
223  lirc_scancode sc;
224  ssize_t ret = read(f, &sc, sizeof sc);
225 
226  if (ret == sizeof sc) {
227  const bool SameKey = sc.keycode == LastKeyCode && !((sc.flags ^ LastFlags) & LIRC_SCANCODE_FLAG_TOGGLE);
228 
229  if (sc.flags & LIRC_SCANCODE_FLAG_REPEAT != 0)
230  // Before Linux 6.0, this flag is never set for some devices.
231  SeenRepeat = true;
232 
233  if (SameKey && uint((sc.timestamp - FirstTime) / 1000000) < uint(Setup.RcRepeatDelay))
234  continue; // skip keys coming in too fast
235 
236  if (!SameKey || (SeenRepeat && !(sc.flags & LIRC_SCANCODE_FLAG_REPEAT))) {
237  // This is a key-press event, not key-repeat.
238  if (repeat)
239  Put(LastKeyCode, false, true); // generated release for previous key
240  repeat = false;
241  FirstTime = sc.timestamp;
242  LastKeyCode = sc.keycode;
243  LastFlags = sc.flags;
244  }
245  else if (uint((sc.timestamp - LastTime) / 1000000) < uint(Setup.RcRepeatDelta))
246  continue; // filter out too frequent key-repeat events
247  else
248  repeat = true;
249 
250  LastTime = sc.timestamp;
251  Put(sc.keycode, repeat);
252  }
253  else {
254  if (repeat) // the last one was a repeat, so let's generate a release
255  Put(LastKeyCode, false, true);
256  repeat = false;
257  LastKeyCode = 0;
258  }
259  }
260 }
261 #endif
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:73
static bool FileReady(int FileDes, int TimeoutMs=1000)
Definition: tools.c:1726
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: lirc.c:212
bool Connect(const char *DeviceName)
Definition: lirc.c:183
cLircDevRemote(void)
Definition: lirc.c:207
virtual ~cLircRemote() override
Definition: lirc.c:55
virtual bool Ready(void) override
Definition: lirc.c:99
static void NewLircRemote(const char *Name)
Definition: lirc.c:64
int f
Definition: lirc.h:18
cLircRemote(const char *Name)
Definition: lirc.c:49
@ LIRC_BUFFER_SIZE
Definition: lirc.c:29
@ LIRC_KEY_BUF
Definition: lirc.c:29
cLircUsrRemote(const char *DeviceName)
Definition: lirc.c:76
struct sockaddr_un addr
Definition: lirc.c:30
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: lirc.c:104
bool Connect(void)
Definition: lirc.c:85
Definition: remote.h:20
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:124
const char * Name(void)
Definition: remote.h:46
int RcRepeatDelay
Definition: config.h:317
int RcRepeatDelta
Definition: config.h:318
Definition: thread.h:79
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:305
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:355
Definition: tools.h:404
uint64_t Elapsed(void) const
Definition: tools.c:818
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:808
cSetup Setup
Definition: config.c:372
#define RECONNECTDELAY
Definition: lirc.c:25
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: tools.c:53
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
#define LOG_ERROR_STR(s)
Definition: tools.h:40
#define esyslog(a...)
Definition: tools.h:35
#define isyslog(a...)
Definition: tools.h:36