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