vdr 2.8.4
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.6 2026/10/25 20:49:18 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#define LIRC_REPEAT_TIMEOUT 250 // ms (sufficient to compensate 1 lost transmission)
27
29private:
30 enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
31 struct sockaddr_un addr;
32 bool Connect(void);
33 virtual void Action(void) override;
34public:
35 cLircUsrRemote(const char *DeviceName);
36 virtual ~cLircUsrRemote() override;
37 };
38
39#if HAVE_KERNEL_LIRC
41private:
42 virtual void Action(void) override;
43public:
44 cLircDevRemote(void);
45 virtual ~cLircDevRemote() override;
46 bool Connect(const char *DeviceName);
47 };
48#endif
49
50// --- cLircRemote -----------------------------------------------------------
51
54,cThread("LIRC remote control")
55{
56}
57
59{
60 int fh = f;
61 f = -1;
62 Cancel();
63 if (fh >= 0)
64 close(fh);
65}
66
68{
69#if HAVE_KERNEL_LIRC
71 if (r->Connect(Name))
72 return;
73 delete r;
74#endif
76}
77// --- cLircUsrRemote --------------------------------------------------------
78
79cLircUsrRemote::cLircUsrRemote(const char *DeviceName)
80: cLircRemote("LIRC")
81{
82 addr.sun_family = AF_UNIX;
83 strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
84 Connect();
85 Start();
86}
87
89{
90 if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
91 if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
92 return true;
93 LOG_ERROR_STR(addr.sun_path);
94 close(f);
95 f = -1;
96 }
97 else
98 LOG_ERROR_STR(addr.sun_path);
99 return false;
100}
101
103{
104 return f >= 0;
105}
106
108{
109 Cancel(3); // Action() uses this object, so stop it before the object goes away
110}
111
113{
114 cTimeMs FirstTime;
115 cTimeMs LastTime;
116 char buf[LIRC_BUFFER_SIZE];
117 char LastKeyName[LIRC_KEY_BUF] = "";
118 bool repeat = false;
119 int timeout = -1;
120
121 while (Running()) {
122
123 bool ready = f >= 0 && cFile::FileReady(f, timeout >= 0 ? timeout : 100); // check Running() at least every 100 ms
124 int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
125
126 if (f < 0 || ready && ret <= 0) {
127 esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
128 if (f >= 0)
129 close(f);
130 f = -1;
131 while (Running() && f < 0) {
133 if (Connect()) {
134 isyslog("reconnected to lircd");
135 break;
136 }
137 }
138 }
139
140 else if (ready) {
141 buf[ret - 1] = 0;
142 int count;
143 char KeyName[LIRC_KEY_BUF];
144 if (sscanf(buf, "%*x %x %29s", &count, KeyName) != 2) { // '29' in '%29s' is LIRC_KEY_BUF-1!
145 esyslog("ERROR: unparsable lirc command: %s", buf);
146 continue;
147 }
148 if (count == 0) { // new key pressed
149 if (repeat)
150 Put(LastKeyName, false, true); // generated release for previous repeated key
151 strn0cpy(LastKeyName, KeyName, sizeof(LastKeyName));
152 FirstTime.Set();
153 repeat = false;
154 timeout = -1;
155 }
156 else { // repeat of a continuously pressed key
157 if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
158 continue; // repeat function kicks in after a short delay
159 if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
160 continue; // skip identical keys coming in too fast
161 repeat = true;
162 timeout = LIRC_REPEAT_TIMEOUT;
163 }
164 LastTime.Set();
165 Put(KeyName, repeat);
166 }
167 else { // no key pressed within timeout
168 if (repeat) // the last one was a repeat, so let's generate a release
169 Put(LastKeyName, false, true);
170 repeat = false;
171 timeout = -1;
172 }
173 }
174}
175
176// --- cLircDevRemote --------------------------------------------------------
177
178#if HAVE_KERNEL_LIRC
179bool cLircDevRemote::Connect(const char *DeviceName)
180{
181 unsigned mode = LIRC_MODE_SCANCODE;
182 f = open(DeviceName, O_RDONLY, 0);
183 if (f < 0) {
184 switch (errno) {
185 case ENXIO:
186 case ENODEV:
187 // Do not complain about an attempt to open a lircd socket file.
188 break;
189 default:
190 LOG_ERROR_STR(DeviceName);
191 }
192 }
193 else if (ioctl(f, LIRC_SET_REC_MODE, &mode)) {
194 LOG_ERROR_STR(DeviceName);
195 close(f);
196 f = -1;
197 }
198 if (f >= 0)
199 Start();
200 return f >= 0;
201}
202
204:cLircRemote("DEV_LIRC")
205{
206}
207
209{
210 Cancel(3); // see ~cLircUsrRemote()
211}
212
214{
215 if (f < 0)
216 return;
217 uint64_t FirstTime = 0, LastTime = 0;
218 uint32_t LastKeyCode = 0;
219 uint16_t LastFlags = false;
220 bool SeenRepeat = false;
221 bool repeat = false;
222
223 while (Running()) {
224 if (!cFile::FileReady(f, 100))
225 continue; // check Running() at least every 100 ms
226 lirc_scancode sc;
227 ssize_t ret = read(f, &sc, sizeof sc);
228
229 if (ret == sizeof sc) {
230 const bool SameKey = sc.keycode == LastKeyCode && !((sc.flags ^ LastFlags) & LIRC_SCANCODE_FLAG_TOGGLE);
231
232 if ((sc.flags & LIRC_SCANCODE_FLAG_REPEAT) != 0)
233 // Before Linux 6.0, this flag is never set for some devices.
234 SeenRepeat = true;
235
236 if (SameKey && uint((sc.timestamp - FirstTime) / 1000000) < uint(Setup.RcRepeatDelay))
237 continue; // skip keys coming in too fast
238
239 if (!SameKey || (SeenRepeat && !(sc.flags & LIRC_SCANCODE_FLAG_REPEAT))) {
240 // This is a key-press event, not key-repeat.
241 if (repeat)
242 Put(LastKeyCode, false, true); // generated release for previous key
243 repeat = false;
244 FirstTime = sc.timestamp;
245 LastKeyCode = sc.keycode;
246 LastFlags = sc.flags;
247 }
248 else if (uint((sc.timestamp - LastTime) / 1000000) < uint(Setup.RcRepeatDelta))
249 continue; // filter out too frequent key-repeat events
250 else
251 repeat = true;
252
253 LastTime = sc.timestamp;
254 Put(sc.keycode, repeat);
255 }
256 else {
257 if (repeat) // the last one was a repeat, so let's generate a release
258 Put(LastKeyCode, false, true);
259 repeat = false;
260 LastKeyCode = 0;
261 }
262 }
263}
264#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:1777
virtual ~cLircDevRemote() override
Definition lirc.c:208
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:213
bool Connect(const char *DeviceName)
Definition lirc.c:179
cLircDevRemote(void)
Definition lirc.c:203
virtual ~cLircRemote() override
Definition lirc.c:58
virtual bool Ready(void) override
Definition lirc.c:102
static void NewLircRemote(const char *Name)
Definition lirc.c:67
int f
Definition lirc.h:18
cLircRemote(const char *Name)
Definition lirc.c:52
@ LIRC_BUFFER_SIZE
Definition lirc.c:30
@ LIRC_KEY_BUF
Definition lirc.c:30
cLircUsrRemote(const char *DeviceName)
Definition lirc.c:79
struct sockaddr_un addr
Definition lirc.c:31
virtual ~cLircUsrRemote() override
Definition lirc.c:107
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:112
bool Connect(void)
Definition lirc.c:88
const char * Name(void)
Definition remote.h:47
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition remote.c:130
cRemote(const char *Name)
Definition remote.c:40
int RcRepeatDelay
Definition config.h:314
int RcRepeatDelta
Definition config.h:315
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:330
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition thread.h:110
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:264
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:381
uint64_t Elapsed(void) const
Returns the number of milliseconds that have elapsed since the last call to Set().
Definition tools.c:855
void Set(int Ms=0)
Sets the timer.
Definition tools.c:842
cSetup Setup
Definition config.c:373
#define LIRC_REPEAT_TIMEOUT
Definition lirc.c:26
#define RECONNECTDELAY
Definition lirc.c:25
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition tools.c:50
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:128
#define LOG_ERROR_STR(s)
Definition tools.h:40
#define esyslog(a...)
Definition tools.h:35
#define isyslog(a...)
Definition tools.h:36