vdr 2.7.9
rcu.c
Go to the documentation of this file.
1/*
2 * rcu.c: A plugin for the Video Disk Recorder
3 *
4 * See the README file for copyright information and how to reach the author.
5 *
6 * $Id: rcu.c 5.1 2018/12/17 10:22:54 kls Exp $
7 */
8
9#include <getopt.h>
10#include <netinet/in.h>
11#include <termios.h>
12#include <unistd.h>
13#include <vdr/plugin.h>
14#include <vdr/remote.h>
15#include <vdr/status.h>
16#include <vdr/thread.h>
17#include <vdr/tools.h>
18
19static const char *VERSION = "2.2.1";
20static const char *DESCRIPTION = "Remote Control Unit";
21
22#define REPEATLIMIT 150 // ms
23#define REPEATDELAY 350 // ms
24#define HANDSHAKETIMEOUT 20 // ms
25#define DEFAULTDEVICE "/dev/ttyS1"
26#define MAXPOINTS 4
27
28class cRcuRemote : public cRemote, private cThread, private cStatus {
29private:
30 enum { modeH = 'h', modeB = 'b', modeS = 's' };
31 int f;
32 unsigned char dp, code, mode;
33 unsigned char counter[MAXPOINTS];
34 int number;
35 unsigned int data;
37 bool SendCommand(unsigned char Cmd);
38 int ReceiveByte(int TimeoutMs = 0);
39 bool SendByteHandshake(unsigned char c);
40 bool SendByte(unsigned char c);
41 bool SendData(unsigned int n);
42 void SetCode(unsigned char Code);
43 void SetMode(unsigned char Mode);
44 void SetNumber(int n, bool Hex = false);
45 void SetPoints(unsigned char Dp, bool On);
46 void SetString(const char *s);
47 bool DetectCode(unsigned char *Code);
48 virtual void Action(void);
49 virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
50 virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
51public:
52 cRcuRemote(const char *DeviceName);
53 virtual ~cRcuRemote();
54 virtual bool Ready(void);
55 virtual bool Initialize(void);
56 };
57
58cRcuRemote::cRcuRemote(const char *DeviceName)
59:cRemote("RCU")
60,cThread("RCU remote control")
61{
62 dp = 0;
63 mode = modeB;
64 code = 0;
65 memset(counter, 0x00, sizeof(counter));
66 number = 0;
67 data = 0;
68 receivedCommand = false;
69 if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
70 struct termios t;
71 if (tcgetattr(f, &t) == 0) {
72 cfsetspeed(&t, B9600);
73 cfmakeraw(&t);
74 if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
75 SetNumber(8888);
76 const char *Setup = GetSetup();
77 if (Setup) {
78 code = *Setup;
80 isyslog("connecting to %s remote control using code %c", Name(), code);
81 }
82 Start();
83 return;
84 }
85 }
86 LOG_ERROR_STR(DeviceName);
87 close(f);
88 }
89 else
90 LOG_ERROR_STR(DeviceName);
91 f = -1;
92}
93
98
100{
101 return f >= 0;
102}
103
105{
106 if (f >= 0) {
107 unsigned char Code = '0';
108 isyslog("trying codes for %s remote control...", Name());
109 for (;;) {
110 if (DetectCode(&Code)) {
111 code = Code;
112 break;
113 }
114 }
115 isyslog("established connection to %s remote control using code %c", Name(), code);
116 char buffer[16];
117 snprintf(buffer, sizeof(buffer), "%c", code);
118 PutSetup(buffer);
119 return true;
120 }
121 return false;
122}
123
125{
126#pragma pack(1)
127 union {
128 struct {
129 unsigned short address;
130 unsigned int command;
131 } data;
132 unsigned char raw[6];
133 } buffer;
134#pragma pack()
135
136 time_t LastCodeRefresh = 0;
137 cTimeMs FirstTime;
138 unsigned char LastCode = 0, LastMode = 0;
139 uint64_t LastCommand = ~0; // 0x00 might be a valid command
140 unsigned int LastData = 0;
141 bool repeat = false;
142
143 while (Running() && f >= 0) {
144 if (ReceiveByte(REPEATLIMIT) == 'X') {
145 for (int i = 0; i < 6; i++) {
146 int b = ReceiveByte();
147 if (b >= 0) {
148 buffer.raw[i] = b;
149 if (i == 5) {
150 unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
151 uint64_t Command = ntohl(buffer.data.command);
152 if (code == 'B' && Address == 0x0000 && Command == 0x00004000)
153 // Well, well, if it isn't the "d-box"...
154 // This remote control sends the above command before and after
155 // each keypress - let's just drop this:
156 break;
157 Command |= uint64_t(Address) << 32;
158 if (Command != LastCommand) {
159 LastCommand = Command;
160 repeat = false;
161 FirstTime.Set();
162 }
163 else {
164 if (FirstTime.Elapsed() < REPEATDELAY)
165 break; // repeat function kicks in after a short delay
166 repeat = true;
167 }
168 Put(Command, repeat);
169 receivedCommand = true;
170 }
171 }
172 else
173 break;
174 }
175 }
176 else if (repeat) { // the last one was a repeat, so let's generate a release
177 Put(LastCommand, false, true);
178 repeat = false;
179 LastCommand = ~0;
180 }
181 else {
182 unsigned int d = data;
183 if (d != LastData) {
184 SendData(d);
185 LastData = d;
186 }
187 unsigned char c = code;
188 if (c != LastCode) {
189 SendCommand(c);
190 LastCode = c;
191 }
192 unsigned char m = mode;
193 if (m != LastMode) {
194 SendCommand(m);
195 LastMode = m;
196 }
197 LastCommand = ~0;
198 }
199 if (!repeat && code && time(NULL) - LastCodeRefresh > 60) {
200 SendCommand(code); // in case the PIC listens to the wrong code
201 LastCodeRefresh = time(NULL);
202 }
203 }
204}
205
206int cRcuRemote::ReceiveByte(int TimeoutMs)
207{
208 // Returns the byte if one was received within a timeout, -1 otherwise
209 if (cFile::FileReady(f, TimeoutMs)) {
210 unsigned char b;
211 if (safe_read(f, &b, 1) == 1)
212 return b;
213 else
214 LOG_ERROR;
215 }
216 return -1;
217}
218
219bool cRcuRemote::SendByteHandshake(unsigned char c)
220{
221 if (f >= 0) {
222 int w = write(f, &c, 1);
223 if (w == 1) {
224 for (int reply = ReceiveByte(HANDSHAKETIMEOUT); reply >= 0;) {
225 if (reply == c)
226 return true;
227 else if (reply == 'X') {
228 // skip any incoming RC code - it will come again
229 for (int i = 6; i--;) {
230 if (ReceiveByte() < 0)
231 return false;
232 }
233 }
234 else
235 return false;
236 }
237 }
238 LOG_ERROR;
239 }
240 return false;
241}
242
243bool cRcuRemote::SendByte(unsigned char c)
244{
245 for (int retry = 5; retry--;) {
246 if (SendByteHandshake(c))
247 return true;
248 }
249 return false;
250}
251
252bool cRcuRemote::SendData(unsigned int n)
253{
254 for (int i = 0; i < 4; i++) {
255 if (!SendByte(n & 0x7F))
256 return false;
257 n >>= 8;
258 }
259 return SendCommand(mode);
260}
261
262void cRcuRemote::SetCode(unsigned char Code)
263{
264 code = Code;
265}
266
267void cRcuRemote::SetMode(unsigned char Mode)
268{
269 mode = Mode;
270}
271
272bool cRcuRemote::SendCommand(unsigned char Cmd)
273{
274 return SendByte(Cmd | 0x80);
275}
276
277void cRcuRemote::SetNumber(int n, bool Hex)
278{
279 number = n;
280 if (!Hex) {
281 char buf[8];
282 sprintf(buf, "%4d", n & 0xFFFF);
283 n = 0;
284 for (char *d = buf; *d; d++) {
285 if (*d == ' ')
286 *d = 0xF;
287 n = (n << 4) | ((*d - '0') & 0x0F);
288 }
289 }
290 unsigned int m = 0;
291 for (int i = 0; i < 4; i++) {
292 m <<= 8;
293 m |= ((i & 0x03) << 5) | (n & 0x0F) | (((dp >> i) & 0x01) << 4);
294 n >>= 4;
295 }
296 data = m;
297}
298
299void cRcuRemote::SetString(const char *s)
300{
301 const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
302 int n = 0;
303
304 for (int i = 0; *s && i < 4; s++, i++) {
305 n <<= 4;
306 for (const char *c = chars; *c; c++) {
307 if (*c == *s) {
308 n |= c - chars;
309 break;
310 }
311 }
312 }
313 SetNumber(n, true);
314}
315
316void cRcuRemote::SetPoints(unsigned char Dp, bool On)
317{
318 if (On)
319 dp |= Dp;
320 else
321 dp &= ~Dp;
323}
324
325bool cRcuRemote::DetectCode(unsigned char *Code)
326{
327 // Caller should initialize 'Code' to 0 and call DetectCode()
328 // until it returns true. Whenever DetectCode() returns false
329 // and 'Code' is not 0, the caller can use 'Code' to display
330 // a message like "Trying code '%c'". If false is returned and
331 // 'Code' is 0, all possible codes have been tried and the caller
332 // can either stop calling DetectCode() (and give some error
333 // message), or start all over again.
334 if (*Code < 'A' || *Code > 'D') {
335 *Code = 'A';
336 return false;
337 }
338 if (*Code <= 'D') {
339 SetMode(modeH);
340 char buf[5];
341 sprintf(buf, "C0D%c", *Code);
342 SetString(buf);
343 SetCode(*Code);
345 if (receivedCommand) {
346 SetMode(modeB);
347 SetString("----");
348 return true;
349 }
350 if (*Code < 'D') {
351 (*Code)++;
352 return false;
353 }
354 }
355 *Code = 0;
356 return false;
357}
358
359void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
360{
361 if (ChannelNumber && LiveView)
363}
364
365void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
366{
367 int n = Device->DeviceNumber();
368 if (n < MAXPOINTS) {
369 counter[n] += On ? 1 : -1;
370 SetPoints(1 << Device->DeviceNumber(), counter[n]);
371 }
372}
373
374class cPluginRcu : public cPlugin {
375private:
376 // Add any member variables or functions you may need here.
377 const char *device;
378public:
379 cPluginRcu(void);
380 virtual const char *Version(void) { return VERSION; }
381 virtual const char *Description(void) { return DESCRIPTION; }
382 virtual const char *CommandLineHelp(void);
383 virtual bool ProcessArgs(int argc, char *argv[]);
384 virtual bool Start(void);
385 };
386
388{
389 // Initialize any member variables here.
390 // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
391 // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
393}
394
396{
397 // Return a string that describes all known command line options.
398 return " -d DEV, --device=DEV set the device to use (default is " DEFAULTDEVICE ")\n";
399}
400
401bool cPluginRcu::ProcessArgs(int argc, char *argv[])
402{
403 // Implement command line argument processing here if applicable.
404 static struct option long_options[] = {
405 { "dev", required_argument, NULL, 'd' },
406 { NULL, no_argument, NULL, 0 }
407 };
408
409 int c;
410 while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
411 switch (c) {
412 case 'd': device = optarg;
413 break;
414 default: return false;
415 }
416 }
417 return true;
418}
419
421{
422 // Start any background activities the plugin shall perform.
423 new cRcuRemote(device);
424 return true;
425}
426
427VDRPLUGINCREATOR(cPluginRcu); // Don't touch this!
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
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition device.c:167
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition device.h:371
static bool FileReady(int FileDes, int TimeoutMs=1000)
Definition tools.c:1743
cPluginRcu(void)
Definition rcu.c:387
virtual bool ProcessArgs(int argc, char *argv[])
Definition rcu.c:401
virtual const char * Description(void)
Definition rcu.c:381
virtual const char * Version(void)
Definition rcu.c:380
const char * device
Definition rcu.c:377
virtual bool Start(void)
Definition rcu.c:420
virtual const char * CommandLineHelp(void)
Definition rcu.c:395
cPlugin(void)
Definition plugin.c:33
int ReceiveByte(int TimeoutMs=0)
Definition rcu.c:206
@ modeH
Definition rcu.c:30
@ modeS
Definition rcu.c:30
@ modeB
Definition rcu.c:30
bool SendCommand(unsigned char Cmd)
Definition rcu.c:272
bool SendByteHandshake(unsigned char c)
Definition rcu.c:219
int f
Definition rcu.c:31
virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
Definition rcu.c:365
bool DetectCode(unsigned char *Code)
Definition rcu.c:325
bool receivedCommand
Definition rcu.c:36
unsigned char counter[MAXPOINTS]
Definition rcu.c:33
void SetNumber(int n, bool Hex=false)
Definition rcu.c:277
virtual ~cRcuRemote()
Definition rcu.c:94
int number
Definition rcu.c:34
bool SendData(unsigned int n)
Definition rcu.c:252
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition rcu.c:124
unsigned int data
Definition rcu.c:35
void SetPoints(unsigned char Dp, bool On)
Definition rcu.c:316
virtual bool Ready(void)
Definition rcu.c:99
unsigned char mode
Definition rcu.c:32
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
Definition rcu.c:359
void SetCode(unsigned char Code)
Definition rcu.c:262
void SetMode(unsigned char Mode)
Definition rcu.c:267
unsigned char code
Definition rcu.c:32
bool SendByte(unsigned char c)
Definition rcu.c:243
cRcuRemote(const char *DeviceName)
Definition rcu.c:58
virtual bool Initialize(void)
Definition rcu.c:104
unsigned char dp
Definition rcu.c:32
void SetString(const char *s)
Definition rcu.c:299
const char * Name(void)
Definition remote.h:46
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition remote.c:124
const char * GetSetup(void)
Definition remote.c:51
cRemote(const char *Name)
Definition remote.c:39
void PutSetup(const char *Setup)
Definition remote.c:56
cStatus(void)
Definition status.c:19
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
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:239
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
uint64_t Elapsed(void) const
Returns the number of milliseconds that have elapsed since the last call to Set().
Definition tools.c:825
void Set(int Ms=0)
Sets the timer.
Definition tools.c:812
cSetup Setup
Definition config.c:372
static const char * VERSION
Definition dvbhddevice.c:14
static const char * DESCRIPTION
Definition dvbhddevice.c:15
#define VDRPLUGINCREATOR(PluginClass)
Definition plugin.h:18
static const char * VERSION
Definition rcu.c:19
#define MAXPOINTS
Definition rcu.c:26
static const char * DESCRIPTION
Definition rcu.c:20
#define DEFAULTDEVICE
Definition rcu.c:25
#define HANDSHAKETIMEOUT
Definition rcu.c:24
#define REPEATDELAY
Definition rcu.c:23
#define REPEATLIMIT
Definition rcu.c:22
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition tools.c:53
#define LOG_ERROR_STR(s)
Definition tools.h:40
#define LOG_ERROR
Definition tools.h:39
#define isyslog(a...)
Definition tools.h:36