vdr  2.2.0
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 3.2 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 
19 static const char *VERSION = "2.2.0";
20 static 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 
27 class cRcuRemote : public cRemote, private cThread, private cStatus {
28 private:
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);
49 public:
50  cRcuRemote(const char *DeviceName);
51  virtual ~cRcuRemote();
52  virtual bool Ready(void);
53  virtual bool Initialize(void);
54  };
55 
56 cRcuRemote::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;
76  SetCode(code);
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 
92 {
93  Cancel();
94 }
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 
203 int 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 
216 bool 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 
240 bool 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 
249 bool 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 
259 void cRcuRemote::SetCode(unsigned char Code)
260 {
261  code = Code;
262 }
263 
264 void cRcuRemote::SetMode(unsigned char Mode)
265 {
266  mode = Mode;
267 }
268 
269 bool cRcuRemote::SendCommand(unsigned char Cmd)
270 {
271  return SendByte(Cmd | 0x80);
272 }
273 
274 void 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 
296 void 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 
313 void cRcuRemote::SetPoints(unsigned char Dp, bool On)
314 {
315  if (On)
316  dp |= Dp;
317  else
318  dp &= ~Dp;
319  SetNumber(number);
320 }
321 
322 bool 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 
356 void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
357 {
358  if (ChannelNumber && LiveView)
360 }
361 
362 void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
363 {
364  SetPoints(1 << Device->DeviceNumber(), Device->Receiving());
365 }
366 
367 class cPluginRcu : public cPlugin {
368 private:
369  // Add any member variables or functions you may need here.
370  const char *device;
371 public:
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!
385  device = DEFAULTDEVICE;
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 
394 bool 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 
420 VDRPLUGINCREATOR(cPluginRcu); // Don't touch this!
virtual bool Start(void)
Definition: rcu.c:413
void SetString(const char *s)
Definition: rcu.c:296
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition: device.c:161
Definition: rcu.c:27
virtual const char * CommandLineHelp(void)
Definition: rcu.c:388
bool Receiving(bool Dummy=false) const
Returns true if we are currently receiving. The parameter has no meaning (for backwards compatibility...
Definition: device.c:1582
void Set(int Ms=0)
Definition: tools.c:738
const char * GetSetup(void)
Definition: remote.c:51
#define LOG_ERROR
Definition: tools.h:38
#define HANDSHAKETIMEOUT
Definition: rcu.c:24
Definition: status.h:22
unsigned char code
Definition: rcu.c:31
static bool FileReady(int FileDes, int TimeoutMs=1000)
Definition: tools.c:1614
Definition: plugin.h:20
virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
Definition: rcu.c:362
bool SendData(unsigned int n)
Definition: rcu.c:249
#define LOG_ERROR_STR(s)
Definition: tools.h:39
static const char * DESCRIPTION
Definition: rcu.c:20
#define REPEATDELAY
Definition: rcu.c:23
int ReceiveByte(int TimeoutMs=0)
Definition: rcu.c:203
unsigned int data
Definition: rcu.c:33
virtual const char * Description(void)
Definition: rcu.c:374
int f
Definition: rcu.c:30
virtual ~cRcuRemote()
Definition: rcu.c:91
virtual const char * Version(void)
Definition: rcu.c:373
void SetMode(unsigned char Mode)
Definition: rcu.c:264
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
virtual bool ProcessArgs(int argc, char *argv[])
Definition: rcu.c:394
void PutSetup(const char *Setup)
Definition: remote.c:56
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition: device.h:323
const char * device
Definition: rcu.c:370
#define REPEATLIMIT
Definition: rcu.c:22
virtual bool Initialize(void)
Definition: rcu.c:101
bool SendByteHandshake(unsigned char c)
Definition: rcu.c:216
void SetCode(unsigned char Code)
Definition: rcu.c:259
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:57
cPluginRcu(void)
Definition: rcu.c:380
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:273
unsigned char dp
Definition: rcu.c:31
cSetup Setup
Definition: config.c:373
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:124
unsigned char mode
Definition: rcu.c:31
cRcuRemote(const char *DeviceName)
Definition: rcu.c:56
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:99
bool SendByte(unsigned char c)
Definition: rcu.c:240
bool receivedCommand
Definition: rcu.c:34
Definition: remote.h:20
bool DetectCode(unsigned char *Code)
Definition: rcu.c:322
#define DEFAULTDEVICE
Definition: rcu.c:25
#define isyslog(a...)
Definition: tools.h:35
const char * Name(void)
Definition: remote.h:46
Definition: thread.h:77
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: tools.c:53
Definition: tools.h:333
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
Definition: rcu.c:356
VDRPLUGINCREATOR(cPluginRcu)
void SetNumber(int n, bool Hex=false)
Definition: rcu.c:274
void SetPoints(unsigned char Dp, bool On)
Definition: rcu.c:313
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
static const char * VERSION
Definition: rcu.c:19
bool SendCommand(unsigned char Cmd)
Definition: rcu.c:269
virtual bool Ready(void)
Definition: rcu.c:96
The cDevice class is the base from which actual devices can be derived.
Definition: device.h:109
uint64_t Elapsed(void) const
Definition: tools.c:748
int number
Definition: rcu.c:32