vdr
1.7.27
|
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!