vdr  1.7.27
rcu.c
Go to the documentation of this file.
00001 /*
00002  * rcu.c: A plugin for the Video Disk Recorder
00003  *
00004  * See the README file for copyright information and how to reach the author.
00005  *
00006  * $Id: rcu.c 1.2 2012/03/07 14:22:44 kls Exp $
00007  */
00008 
00009 #include <getopt.h>
00010 #include <netinet/in.h>
00011 #include <termios.h>
00012 #include <unistd.h>
00013 #include <vdr/plugin.h>
00014 #include <vdr/remote.h>
00015 #include <vdr/status.h>
00016 #include <vdr/thread.h>
00017 #include <vdr/tools.h>
00018 
00019 static const char *VERSION        = "0.0.2";
00020 static const char *DESCRIPTION    = "Remote Control Unit";
00021 
00022 #define REPEATLIMIT      150 // ms
00023 #define REPEATDELAY      350 // ms
00024 #define HANDSHAKETIMEOUT  20 // ms
00025 #define DEFAULTDEVICE    "/dev/ttyS1"
00026 
00027 class cRcuRemote : public cRemote, private cThread, private cStatus {
00028 private:
00029   enum { modeH = 'h', modeB = 'b', modeS = 's' };
00030   int f;
00031   unsigned char dp, code, mode;
00032   int number;
00033   unsigned int data;
00034   bool receivedCommand;
00035   bool SendCommand(unsigned char Cmd);
00036   int ReceiveByte(int TimeoutMs = 0);
00037   bool SendByteHandshake(unsigned char c);
00038   bool SendByte(unsigned char c);
00039   bool SendData(unsigned int n);
00040   void SetCode(unsigned char Code);
00041   void SetMode(unsigned char Mode);
00042   void SetNumber(int n, bool Hex = false);
00043   void SetPoints(unsigned char Dp, bool On);
00044   void SetString(const char *s);
00045   bool DetectCode(unsigned char *Code);
00046   virtual void Action(void);
00047   virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView);
00048   virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
00049 public:
00050   cRcuRemote(const char *DeviceName);
00051   virtual ~cRcuRemote();
00052   virtual bool Ready(void);
00053   virtual bool Initialize(void);
00054   };
00055 
00056 cRcuRemote::cRcuRemote(const char *DeviceName)
00057 :cRemote("RCU")
00058 ,cThread("RCU remote control")
00059 {
00060   dp = 0;
00061   mode = modeB;
00062   code = 0;
00063   number = 0;
00064   data = 0;
00065   receivedCommand = false;
00066   if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
00067      struct termios t;
00068      if (tcgetattr(f, &t) == 0) {
00069         cfsetspeed(&t, B9600);
00070         cfmakeraw(&t);
00071         if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
00072            SetNumber(8888);
00073            const char *Setup = GetSetup();
00074            if (Setup) {
00075               code = *Setup;
00076               SetCode(code);
00077               isyslog("connecting to %s remote control using code %c", Name(), code);
00078               }
00079            Start();
00080            return;
00081            }
00082         }
00083      LOG_ERROR_STR(DeviceName);
00084      close(f);
00085      }
00086   else
00087      LOG_ERROR_STR(DeviceName);
00088   f = -1;
00089 }
00090 
00091 cRcuRemote::~cRcuRemote()
00092 {
00093   Cancel();
00094 }
00095 
00096 bool cRcuRemote::Ready(void)
00097 {
00098   return f >= 0;
00099 }
00100 
00101 bool cRcuRemote::Initialize(void)
00102 {
00103   if (f >= 0) {
00104      unsigned char Code = '0';
00105      isyslog("trying codes for %s remote control...", Name());
00106      for (;;) {
00107          if (DetectCode(&Code)) {
00108             code = Code;
00109             break;
00110             }
00111          }
00112      isyslog("established connection to %s remote control using code %c", Name(), code);
00113      char buffer[16];
00114      snprintf(buffer, sizeof(buffer), "%c", code);
00115      PutSetup(buffer);
00116      return true;
00117      }
00118   return false;
00119 }
00120 
00121 void cRcuRemote::Action(void)
00122 {
00123 #pragma pack(1)
00124   union {
00125     struct {
00126       unsigned short address;
00127       unsigned int command;
00128       } data;
00129     unsigned char raw[6];
00130     } buffer;
00131 #pragma pack()
00132 
00133   time_t LastCodeRefresh = 0;
00134   cTimeMs FirstTime;
00135   unsigned char LastCode = 0, LastMode = 0;
00136   uint64_t LastCommand = ~0; // 0x00 might be a valid command
00137   unsigned int LastData = 0;
00138   bool repeat = false;
00139 
00140   while (Running() && f >= 0) {
00141         if (ReceiveByte(REPEATLIMIT) == 'X') {
00142            for (int i = 0; i < 6; i++) {
00143                int b = ReceiveByte();
00144                if (b >= 0) {
00145                   buffer.raw[i] = b;
00146                   if (i == 5) {
00147                      unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
00148                      uint64_t       Command = ntohl(buffer.data.command);
00149                      if (code == 'B' && Address == 0x0000 && Command == 0x00004000)
00150                         // Well, well, if it isn't the "d-box"...
00151                         // This remote control sends the above command before and after
00152                         // each keypress - let's just drop this:
00153                         break;
00154                      Command |= uint64_t(Address) << 32;
00155                      if (Command != LastCommand) {
00156                         LastCommand = Command;
00157                         repeat = false;
00158                         FirstTime.Set();
00159                         }
00160                      else {
00161                         if (FirstTime.Elapsed() < REPEATDELAY)
00162                            break; // repeat function kicks in after a short delay
00163                         repeat = true;
00164                         }
00165                      Put(Command, repeat);
00166                      receivedCommand = true;
00167                      }
00168                   }
00169                else
00170                   break;
00171                }
00172            }
00173         else if (repeat) { // the last one was a repeat, so let's generate a release
00174            Put(LastCommand, false, true);
00175            repeat = false;
00176            LastCommand = ~0;
00177            }
00178         else {
00179            unsigned int d = data;
00180            if (d != LastData) {
00181               SendData(d);
00182               LastData = d;
00183               }
00184            unsigned char c = code;
00185            if (c != LastCode) {
00186               SendCommand(c);
00187               LastCode = c;
00188               }
00189            unsigned char m = mode;
00190            if (m != LastMode) {
00191               SendCommand(m);
00192               LastMode = m;
00193               }
00194            LastCommand = ~0;
00195            }
00196         if (!repeat && code && time(NULL) - LastCodeRefresh > 60) {
00197            SendCommand(code); // in case the PIC listens to the wrong code
00198            LastCodeRefresh = time(NULL);
00199            }
00200         }
00201 }
00202 
00203 int cRcuRemote::ReceiveByte(int TimeoutMs)
00204 {
00205   // Returns the byte if one was received within a timeout, -1 otherwise
00206   if (cFile::FileReady(f, TimeoutMs)) {
00207      unsigned char b;
00208      if (safe_read(f, &b, 1) == 1)
00209         return b;
00210      else
00211         LOG_ERROR;
00212      }
00213   return -1;
00214 }
00215 
00216 bool cRcuRemote::SendByteHandshake(unsigned char c)
00217 {
00218   if (f >= 0) {
00219      int w = write(f, &c, 1);
00220      if (w == 1) {
00221         for (int reply = ReceiveByte(HANDSHAKETIMEOUT); reply >= 0;) {
00222             if (reply == c)
00223                return true;
00224             else if (reply == 'X') {
00225                // skip any incoming RC code - it will come again
00226                for (int i = 6; i--;) {
00227                    if (ReceiveByte() < 0)
00228                       return false;
00229                    }
00230                }
00231             else
00232                return false;
00233             }
00234         }
00235      LOG_ERROR;
00236      }
00237   return false;
00238 }
00239 
00240 bool cRcuRemote::SendByte(unsigned char c)
00241 {
00242   for (int retry = 5; retry--;) {
00243       if (SendByteHandshake(c))
00244          return true;
00245       }
00246   return false;
00247 }
00248 
00249 bool cRcuRemote::SendData(unsigned int n)
00250 {
00251   for (int i = 0; i < 4; i++) {
00252       if (!SendByte(n & 0x7F))
00253          return false;
00254       n >>= 8;
00255       }
00256   return SendCommand(mode);
00257 }
00258 
00259 void cRcuRemote::SetCode(unsigned char Code)
00260 {
00261   code = Code;
00262 }
00263 
00264 void cRcuRemote::SetMode(unsigned char Mode)
00265 {
00266   mode = Mode;
00267 }
00268 
00269 bool cRcuRemote::SendCommand(unsigned char Cmd)
00270 {
00271   return SendByte(Cmd | 0x80);
00272 }
00273 
00274 void cRcuRemote::SetNumber(int n, bool Hex)
00275 {
00276   number = n;
00277   if (!Hex) {
00278      char buf[8];
00279      sprintf(buf, "%4d", n & 0xFFFF);
00280      n = 0;
00281      for (char *d = buf; *d; d++) {
00282          if (*d == ' ')
00283             *d = 0xF;
00284          n = (n << 4) | ((*d - '0') & 0x0F);
00285          }
00286      }
00287   unsigned int m = 0;
00288   for (int i = 0; i < 4; i++) {
00289       m <<= 8;
00290       m |= ((i & 0x03) << 5) | (n & 0x0F) | (((dp >> i) & 0x01) << 4);
00291       n >>= 4;
00292       }
00293   data = m;
00294 }
00295 
00296 void cRcuRemote::SetString(const char *s)
00297 {
00298   const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
00299   int n = 0;
00300 
00301   for (int i = 0; *s && i < 4; s++, i++) {
00302       n <<= 4;
00303       for (const char *c = chars; *c; c++) {
00304           if (*c == *s) {
00305              n |= c - chars;
00306              break;
00307              }
00308           }
00309       }
00310   SetNumber(n, true);
00311 }
00312 
00313 void cRcuRemote::SetPoints(unsigned char Dp, bool On)
00314 {
00315   if (On)
00316      dp |= Dp;
00317   else
00318      dp &= ~Dp;
00319   SetNumber(number);
00320 }
00321 
00322 bool cRcuRemote::DetectCode(unsigned char *Code)
00323 {
00324   // Caller should initialize 'Code' to 0 and call DetectCode()
00325   // until it returns true. Whenever DetectCode() returns false
00326   // and 'Code' is not 0, the caller can use 'Code' to display
00327   // a message like "Trying code '%c'". If false is returned and
00328   // 'Code' is 0, all possible codes have been tried and the caller
00329   // can either stop calling DetectCode() (and give some error
00330   // message), or start all over again.
00331   if (*Code < 'A' || *Code > 'D') {
00332      *Code = 'A';
00333      return false;
00334      }
00335   if (*Code <= 'D') {
00336      SetMode(modeH);
00337      char buf[5];
00338      sprintf(buf, "C0D%c", *Code);
00339      SetString(buf);
00340      SetCode(*Code);
00341      cCondWait::SleepMs(2 * REPEATDELAY);
00342      if (receivedCommand) {
00343         SetMode(modeB);
00344         SetString("----");
00345         return true;
00346         }
00347      if (*Code < 'D') {
00348         (*Code)++;
00349         return false;
00350         }
00351      }
00352   *Code = 0;
00353   return false;
00354 }
00355 
00356 void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber, bool LiveView)
00357 {
00358   if (ChannelNumber && LiveView)
00359      SetNumber(cDevice::CurrentChannel());
00360 }
00361 
00362 void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
00363 {
00364   SetPoints(1 << Device->DeviceNumber(), Device->Receiving());
00365 }
00366 
00367 class cPluginRcu : public cPlugin {
00368 private:
00369   // Add any member variables or functions you may need here.
00370   const char *device;
00371 public:
00372   cPluginRcu(void);
00373   virtual const char *Version(void) { return VERSION; }
00374   virtual const char *Description(void) { return DESCRIPTION; }
00375   virtual const char *CommandLineHelp(void);
00376   virtual bool ProcessArgs(int argc, char *argv[]);
00377   virtual bool Start(void);
00378   };
00379 
00380 cPluginRcu::cPluginRcu(void)
00381 {
00382   // Initialize any member variables here.
00383   // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
00384   // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
00385   device = DEFAULTDEVICE;
00386 }
00387 
00388 const char *cPluginRcu::CommandLineHelp(void)
00389 {
00390   // Return a string that describes all known command line options.
00391   return "  -d DEV,   --device=DEV   set the device to use (default is " DEFAULTDEVICE ")\n";
00392 }
00393 
00394 bool cPluginRcu::ProcessArgs(int argc, char *argv[])
00395 {
00396   // Implement command line argument processing here if applicable.
00397   static struct option long_options[] = {
00398        { "dev",      required_argument, NULL, 'd' },
00399        { NULL,       no_argument,       NULL,  0  }
00400      };
00401 
00402   int c;
00403   while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
00404         switch (c) {
00405           case 'd': device = optarg;
00406                     break;
00407           default:  return false;
00408           }
00409         }
00410   return true;
00411 }
00412 
00413 bool cPluginRcu::Start(void)
00414 {
00415   // Start any background activities the plugin shall perform.
00416   new cRcuRemote(device);
00417   return true;
00418 }
00419 
00420 VDRPLUGINCREATOR(cPluginRcu); // Don't touch this!