vdr 2.6.3
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.1 2022/11/26 13:37:06 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
28private:
29 enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
30 struct sockaddr_un addr;
31 bool Connect(void);
32 virtual void Action(void);
33public:
34 cLircUsrRemote(const char *DeviceName);
35 };
36
37#if HAVE_KERNEL_LIRC
39private:
40 void Connect(const char *DeviceName);
41 virtual void Action(void);
42public:
43 cLircDevRemote(const char *DeviceName);
44 };
45#endif
46
47// --- cLircRemote -----------------------------------------------------------
48
49cLircRemote::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
64void cLircRemote::NewLircRemote(const char *Name)
65{
66#if HAVE_KERNEL_LIRC
67 if (startswith(Name, "/dev/"))
69 else
70#endif
72}
73// --- cLircUsrRemote --------------------------------------------------------
74
75cLircUsrRemote::cLircUsrRemote(const char *DeviceName)
76: cLircRemote("LIRC")
77{
78 addr.sun_family = AF_UNIX;
79 strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
80 Connect();
81 Start();
82}
83
85{
86 if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
87 if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
88 return true;
89 LOG_ERROR_STR(addr.sun_path);
90 close(f);
91 f = -1;
92 }
93 else
94 LOG_ERROR_STR(addr.sun_path);
95 return false;
96}
97
99{
100 return f >= 0;
101}
102
104{
105 cTimeMs FirstTime;
106 cTimeMs LastTime;
107 cTimeMs ThisTime;
108 char buf[LIRC_BUFFER_SIZE];
109 char LastKeyName[LIRC_KEY_BUF] = "";
110 bool pressed = false;
111 bool repeat = false;
112 int timeout = -1;
113
114 while (Running()) {
115
116 bool ready = f >= 0 && cFile::FileReady(f, timeout);
117 int ret = ready ? safe_read(f, buf, sizeof(buf)) : -1;
118
119 if (f < 0 || ready && ret <= 0) {
120 esyslog("ERROR: lircd connection broken, trying to reconnect every %.1f seconds", float(RECONNECTDELAY) / 1000);
121 if (f >= 0)
122 close(f);
123 f = -1;
124 while (Running() && f < 0) {
126 if (Connect()) {
127 isyslog("reconnected to lircd");
128 break;
129 }
130 }
131 }
132
133 if (ready && ret > 0) {
134 buf[ret - 1] = 0;
135 int count;
136 char KeyName[LIRC_KEY_BUF];
137 if (sscanf(buf, "%*x %x %29s", &count, KeyName) != 2) { // '29' in '%29s' is LIRC_KEY_BUF-1!
138 esyslog("ERROR: unparsable lirc command: %s", buf);
139 continue;
140 }
141 int Delta = ThisTime.Elapsed(); // the time between two subsequent LIRC events
142 ThisTime.Set();
143 if (count == 0) { // new key pressed
144 if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
145 continue; // skip keys coming in too fast
146 if (repeat)
147 Put(LastKeyName, false, true); // generated release for previous repeated key
148 strn0cpy(LastKeyName, KeyName, sizeof(LastKeyName));
149 pressed = true;
150 repeat = false;
151 FirstTime.Set();
152 timeout = -1;
153 }
154 else if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
155 continue; // repeat function kicks in after a short delay
156 else if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
157 continue; // skip same keys coming in too fast
158 else {
159 pressed = true;
160 repeat = true;
161 timeout = Delta * 3 / 2;
162 }
163 if (pressed) {
164 LastTime.Set();
165 Put(KeyName, repeat);
166 }
167 }
168 else {
169 if (pressed && repeat) // the last one was a repeat, so let's generate a release
170 Put(LastKeyName, false, true);
171 pressed = false;
172 repeat = false;
173 *LastKeyName = 0;
174 timeout = -1;
175 }
176 }
177}
178
179// --- cLircDevRemote --------------------------------------------------------
180
181#if HAVE_KERNEL_LIRC
182inline void cLircDevRemote::Connect(const char *DeviceName)
183{
184 unsigned mode = LIRC_MODE_SCANCODE;
185 f = open(DeviceName, O_RDONLY, 0);
186 if (f < 0)
187 LOG_ERROR_STR(DeviceName);
188 else if (ioctl(f, LIRC_SET_REC_MODE, &mode)) {
189 LOG_ERROR_STR(DeviceName);
190 close(f);
191 f = -1;
192 }
193}
194
195cLircDevRemote::cLircDevRemote(const char *DeviceName)
196: cLircRemote("DEV_LIRC")
197{
198 Connect(DeviceName);
199 Start();
200}
201
203{
204 if (f < 0)
205 return;
206 uint64_t FirstTime = 0, LastTime = 0;
207 uint32_t LastKeyCode = 0;
208 uint16_t LastFlags = false;
209 bool SeenRepeat = false;
210 bool repeat = false;
211
212 while (Running()) {
213 lirc_scancode sc;
214 ssize_t ret = read(f, &sc, sizeof sc);
215
216 if (ret == sizeof sc) {
217 const bool SameKey = sc.keycode == LastKeyCode && !((sc.flags ^ LastFlags) & LIRC_SCANCODE_FLAG_TOGGLE);
218
219 if (sc.flags & LIRC_SCANCODE_FLAG_REPEAT != 0)
220 // Before Linux 6.0, this flag is never set for some devices.
221 SeenRepeat = true;
222
223 if (SameKey && uint((sc.timestamp - FirstTime) / 1000000) < uint(Setup.RcRepeatDelay))
224 continue; // skip keys coming in too fast
225
226 if (!SameKey || (SeenRepeat && !(sc.flags & LIRC_SCANCODE_FLAG_REPEAT))) {
227 // This is a key-press event, not key-repeat.
228 if (repeat)
229 Put(LastKeyCode, false, true); // generated release for previous key
230 repeat = false;
231 FirstTime = sc.timestamp;
232 LastKeyCode = sc.keycode;
233 LastFlags = sc.flags;
234 }
235 else if (uint((sc.timestamp - LastTime) / 1000000) < uint(Setup.RcRepeatDelta))
236 continue; // filter out too frequent key-repeat events
237 else
238 repeat = true;
239
240 LastTime = sc.timestamp;
241 Put(sc.keycode, repeat);
242 }
243 else {
244 if (repeat) // the last one was a repeat, so let's generate a release
245 Put(LastKeyCode, false, true);
246 repeat = false;
247 LastKeyCode = 0;
248 }
249 }
250}
251#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:72
static bool FileReady(int FileDes, int TimeoutMs=1000)
Definition: tools.c:1722
cLircDevRemote(const char *DeviceName)
Definition: lirc.c:195
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: lirc.c:202
void Connect(const char *DeviceName)
Definition: lirc.c:182
static void NewLircRemote(const char *Name)
Definition: lirc.c:64
int f
Definition: lirc.h:18
virtual ~cLircRemote()
Definition: lirc.c:55
cLircRemote(const char *Name)
Definition: lirc.c:49
virtual bool Ready(void)
Definition: lirc.c:98
@ LIRC_BUFFER_SIZE
Definition: lirc.c:29
@ LIRC_KEY_BUF
Definition: lirc.c:29
cLircUsrRemote(const char *DeviceName)
Definition: lirc.c:75
struct sockaddr_un addr
Definition: lirc.c:30
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: lirc.c:103
bool Connect(void)
Definition: lirc.c:84
Definition: remote.h:20
const char * Name(void)
Definition: remote.h:46
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:124
int RcRepeatDelay
Definition: config.h:307
int RcRepeatDelta
Definition: config.h:308
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:304
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:354
Definition: tools.h:401
uint64_t Elapsed(void) const
Definition: tools.c:802
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:792
cSetup Setup
Definition: config.c:372
#define RECONNECTDELAY
Definition: lirc.c:25
bool startswith(const char *s, const char *p)
Definition: tools.c:329
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