summaryrefslogtreecommitdiff
path: root/PLUGINS/src/rcu/rcu.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <Klaus (dot) Schmidinger (at) tvdr (dot) de>2012-03-03 13:16:00 +0100
committerDieter Hametner <dh (plus) vdr (at) gekrumbel (dot) de>2012-03-10 23:52:09 +0100
commit3df1b6d1397379a7ccdde4b559d46baf3f515837 (patch)
tree687ffe934420abacb19693a1809e1336c923f61d /PLUGINS/src/rcu/rcu.c
parentc2d9577b3dd7557b7a9c702d5c1fd3b1bbe0e5e7 (diff)
downloadvdr-patches-3df1b6d1397379a7ccdde4b559d46baf3f515837.tar.gz
vdr-patches-3df1b6d1397379a7ccdde4b559d46baf3f515837.tar.bz2
Version 1.7.25
Original announce message: VDR developer version 1.7.25 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.25.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.24-1.7.25.diff MD5 checksums: a3fd482a3dd8027706d4f32a88c6bd13 vdr-1.7.25.tar.bz2 f34adcdc0cdab378431d8946872d6b59 vdr-1.7.24-1.7.25.diff WARNING: ======== This is a developer version. Even though I use it in my productive environment. I strongly recommend that you only use it under controlled conditions and for testing and debugging. From the HISTORY file: - The fps value for channels where it differs from the default is now set correctly when pausing live video. - Increased the average character estimate for calculating tab positions in skins, to better suit wide fonts (reported by Rudi Hofer). - Fixed getting the subsystem ids of DVB devices in case they have been rearranged via udev rules. - Added several cTimer::Set...() functions (suggested by Alexander Rieger). - Changed the return value of cTimer::SetFile() to 'void'. - Revoked "Fixed a possible deadlock in time shift mode" because it caused trouble with output on vdr-xine and dxr3, and also short glitches when replaying on any output device. - Updated the Estonian OSD texts (thanks to Arthur Konovalov). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Fixed handling subtitle color palettes on channels where subtitles appear "word by word" (thanks to Rolf Ahrenberg). - Fixed upscaling cBitmaps with anti-aliasing (thanks to Rolf Ahrenberg for reporting a problem with color palettes in subtitles). - Fixed getting the video aspect ratio for scaling subtitles. - Removed the "PrimaryLimit". Old "full featured" DVB cards can be run with the --outputonly option to avoid problems with recording high bandwidth channels. Besides, with HDTV becoming ever more popular those cards are pretty much obsolete by now (the TT S2-6400 has no problems recording and replaying high bandwidth channels simultaneously). And, last but not least, people using software players won't notice this change, anyway. - Since cReceivers can have priorities between -99 and 99, the priority for an unused device has been changed from -1 to -100. - If the first event in a schedule has a table id of 0x00, any incoming EIT data for that schedule from the DVB stream will be completely ignored. This way an external EPG source can fill the schedules with EPG data that will not be messed up with that from the DVB data stream. Note, though, that this means VDR can not do VPS controlled recordings with such events! - Added some typecasts to silence gcc compiler warnings (thanks to Rolf Ahrenberg). - Fixed handling overlapping timers in case a VPS timer with higher priority needs to interrupt a timer with lower priority. - The code for the RCU remote control unit has been moved into a separate plugin named "rcu". The REMOTE=RCU option in the 'make' call for VDR is now obsolete. The command line option --rcu is now obsolete. Use -Prcu instead. If you have used --rcu with a device path, use -P"rcu -d<device>". - Added support for automatically selecting subtitles when playing old PES recordings made with the subtitles plugin (thanks to Anssi Hannula). - Revised priority handling to allow receivers with a priority that is lower than that of live viewing (with suggestions from Frank Schmirler): + An idle device (one that is not used for live viewing and has no receiver attached to it) now has priority IDLEPRIORITY (-100). + An unused CAM slot now has priority IDLEPRIORITY. + The default priority of a cReceiver is now MINPRIORITY (-99). + A device that is used only for live viewing (no matter whether it's in Transfer Mode or real live mode) now has priority TRANSFERPRIORITY (-1). + The function cDevice::Receiving() now returns true if there is any receiver attached to the device. Its boolean parameter has no meaning any more. + The default value for the Priority parameter of the function cDevice::ProvidesChannel() has been changed to IDLEPRIORITY. - Added a Query parameter to cDevice::GetDevice(), so that devices can be queried without side effects when zapping. - Replaced min(max()) calls with the new function constrain(). - Fixed handling OSD color button texts in case a menu item has texts of its own (reported by Rolf Ahrenberg). If a plugin creates derived cMenuEditItems that set color button texts, these should not set the texts directly by calling cSkinDisplay::Current()->SetButtons(), but rather call the new member function cMenuEditItem::SetHelp(). - Moved the call to cStatus::MsgChannelSwitch(this, 0) to the beginning of cDevice::SetChannel(), so that any receivers that have been attached to the device by plugins may be detached before the final call to GetDevice(). This actually reverts "Only calling cStatus::MsgChannelSwitch() if a channel is actually going to be switched or has actually been switched successfully" which was made in version 1.1.10, so please report if this has any unwanted side effects.
Diffstat (limited to 'PLUGINS/src/rcu/rcu.c')
-rw-r--r--PLUGINS/src/rcu/rcu.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/PLUGINS/src/rcu/rcu.c b/PLUGINS/src/rcu/rcu.c
new file mode 100644
index 0000000..f0abedd
--- /dev/null
+++ b/PLUGINS/src/rcu/rcu.c
@@ -0,0 +1,420 @@
+/*
+ * rcu.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: rcu.c 1.1 2012/02/27 11:49:51 kls Exp $
+ */
+
+#include <getopt.h>
+#include <netinet/in.h>
+#include <termios.h>
+#include <unistd.h>
+#include <vdr/plugin.h>
+#include <vdr/remote.h>
+#include <vdr/status.h>
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Remote Control Unit";
+
+#define REPEATLIMIT 150 // ms
+#define REPEATDELAY 350 // ms
+#define HANDSHAKETIMEOUT 20 // ms
+#define DEFAULTDEVICE "/dev/ttyS1"
+
+class cRcuRemote : public cRemote, private cThread, private cStatus {
+private:
+ enum { modeH = 'h', modeB = 'b', modeS = 's' };
+ int f;
+ unsigned char dp, code, mode;
+ int number;
+ unsigned int data;
+ bool receivedCommand;
+ bool SendCommand(unsigned char Cmd);
+ int ReceiveByte(int TimeoutMs = 0);
+ bool SendByteHandshake(unsigned char c);
+ bool SendByte(unsigned char c);
+ bool SendData(unsigned int n);
+ void SetCode(unsigned char Code);
+ void SetMode(unsigned char Mode);
+ void SetNumber(int n, bool Hex = false);
+ void SetPoints(unsigned char Dp, bool On);
+ void SetString(const char *s);
+ bool DetectCode(unsigned char *Code);
+ virtual void Action(void);
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+ virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+public:
+ cRcuRemote(const char *DeviceName);
+ virtual ~cRcuRemote();
+ virtual bool Ready(void);
+ virtual bool Initialize(void);
+ };
+
+cRcuRemote::cRcuRemote(const char *DeviceName)
+:cRemote("RCU")
+,cThread("RCU remote control")
+{
+ dp = 0;
+ mode = modeB;
+ code = 0;
+ number = 0;
+ data = 0;
+ receivedCommand = false;
+ if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
+ struct termios t;
+ if (tcgetattr(f, &t) == 0) {
+ cfsetspeed(&t, B9600);
+ cfmakeraw(&t);
+ if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
+ SetNumber(8888);
+ const char *Setup = GetSetup();
+ if (Setup) {
+ code = *Setup;
+ SetCode(code);
+ isyslog("connecting to %s remote control using code %c", Name(), code);
+ }
+ Start();
+ return;
+ }
+ }
+ LOG_ERROR_STR(DeviceName);
+ close(f);
+ }
+ else
+ LOG_ERROR_STR(DeviceName);
+ f = -1;
+}
+
+cRcuRemote::~cRcuRemote()
+{
+ Cancel();
+}
+
+bool cRcuRemote::Ready(void)
+{
+ return f >= 0;
+}
+
+bool cRcuRemote::Initialize(void)
+{
+ if (f >= 0) {
+ unsigned char Code = '0';
+ isyslog("trying codes for %s remote control...", Name());
+ for (;;) {
+ if (DetectCode(&Code)) {
+ code = Code;
+ break;
+ }
+ }
+ isyslog("established connection to %s remote control using code %c", Name(), code);
+ char buffer[16];
+ snprintf(buffer, sizeof(buffer), "%c", code);
+ PutSetup(buffer);
+ return true;
+ }
+ return false;
+}
+
+void cRcuRemote::Action(void)
+{
+#pragma pack(1)
+ union {
+ struct {
+ unsigned short address;
+ unsigned int command;
+ } data;
+ unsigned char raw[6];
+ } buffer;
+#pragma pack()
+
+ time_t LastCodeRefresh = 0;
+ cTimeMs FirstTime;
+ unsigned char LastCode = 0, LastMode = 0;
+ uint64_t LastCommand = ~0; // 0x00 might be a valid command
+ unsigned int LastData = 0;
+ bool repeat = false;
+
+ while (Running() && f >= 0) {
+ if (ReceiveByte(REPEATLIMIT) == 'X') {
+ for (int i = 0; i < 6; i++) {
+ int b = ReceiveByte();
+ if (b >= 0) {
+ buffer.raw[i] = b;
+ if (i == 5) {
+ unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
+ uint64_t Command = ntohl(buffer.data.command);
+ if (code == 'B' && Address == 0x0000 && Command == 0x00004000)
+ // Well, well, if it isn't the "d-box"...
+ // This remote control sends the above command before and after
+ // each keypress - let's just drop this:
+ break;
+ Command |= uint64_t(Address) << 32;
+ if (Command != LastCommand) {
+ LastCommand = Command;
+ repeat = false;
+ FirstTime.Set();
+ }
+ else {
+ if (FirstTime.Elapsed() < REPEATDELAY)
+ break; // repeat function kicks in after a short delay
+ repeat = true;
+ }
+ Put(Command, repeat);
+ receivedCommand = true;
+ }
+ }
+ else
+ break;
+ }
+ }
+ else if (repeat) { // the last one was a repeat, so let's generate a release
+ Put(LastCommand, false, true);
+ repeat = false;
+ LastCommand = ~0;
+ }
+ else {
+ unsigned int d = data;
+ if (d != LastData) {
+ SendData(d);
+ LastData = d;
+ }
+ unsigned char c = code;
+ if (c != LastCode) {
+ SendCommand(c);
+ LastCode = c;
+ }
+ unsigned char m = mode;
+ if (m != LastMode) {
+ SendCommand(m);
+ LastMode = m;
+ }
+ LastCommand = ~0;
+ }
+ if (!repeat && code && time(NULL) - LastCodeRefresh > 60) {
+ SendCommand(code); // in case the PIC listens to the wrong code
+ LastCodeRefresh = time(NULL);
+ }
+ }
+}
+
+int cRcuRemote::ReceiveByte(int TimeoutMs)
+{
+ // Returns the byte if one was received within a timeout, -1 otherwise
+ if (cFile::FileReady(f, TimeoutMs)) {
+ unsigned char b;
+ if (safe_read(f, &b, 1) == 1)
+ return b;
+ else
+ LOG_ERROR;
+ }
+ return -1;
+}
+
+bool cRcuRemote::SendByteHandshake(unsigned char c)
+{
+ if (f >= 0) {
+ int w = write(f, &c, 1);
+ if (w == 1) {
+ for (int reply = ReceiveByte(HANDSHAKETIMEOUT); reply >= 0;) {
+ if (reply == c)
+ return true;
+ else if (reply == 'X') {
+ // skip any incoming RC code - it will come again
+ for (int i = 6; i--;) {
+ if (ReceiveByte() < 0)
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+ }
+ LOG_ERROR;
+ }
+ return false;
+}
+
+bool cRcuRemote::SendByte(unsigned char c)
+{
+ for (int retry = 5; retry--;) {
+ if (SendByteHandshake(c))
+ return true;
+ }
+ return false;
+}
+
+bool cRcuRemote::SendData(unsigned int n)
+{
+ for (int i = 0; i < 4; i++) {
+ if (!SendByte(n & 0x7F))
+ return false;
+ n >>= 8;
+ }
+ return SendCommand(mode);
+}
+
+void cRcuRemote::SetCode(unsigned char Code)
+{
+ code = Code;
+}
+
+void cRcuRemote::SetMode(unsigned char Mode)
+{
+ mode = Mode;
+}
+
+bool cRcuRemote::SendCommand(unsigned char Cmd)
+{
+ return SendByte(Cmd | 0x80);
+}
+
+void cRcuRemote::SetNumber(int n, bool Hex)
+{
+ number = n;
+ if (!Hex) {
+ char buf[8];
+ sprintf(buf, "%4d", n & 0xFFFF);
+ n = 0;
+ for (char *d = buf; *d; d++) {
+ if (*d == ' ')
+ *d = 0xF;
+ n = (n << 4) | ((*d - '0') & 0x0F);
+ }
+ }
+ unsigned int m = 0;
+ for (int i = 0; i < 4; i++) {
+ m <<= 8;
+ m |= ((i & 0x03) << 5) | (n & 0x0F) | (((dp >> i) & 0x01) << 4);
+ n >>= 4;
+ }
+ data = m;
+}
+
+void cRcuRemote::SetString(const char *s)
+{
+ const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
+ int n = 0;
+
+ for (int i = 0; *s && i < 4; s++, i++) {
+ n <<= 4;
+ for (const char *c = chars; *c; c++) {
+ if (*c == *s) {
+ n |= c - chars;
+ break;
+ }
+ }
+ }
+ SetNumber(n, true);
+}
+
+void cRcuRemote::SetPoints(unsigned char Dp, bool On)
+{
+ if (On)
+ dp |= Dp;
+ else
+ dp &= ~Dp;
+ SetNumber(number);
+}
+
+bool cRcuRemote::DetectCode(unsigned char *Code)
+{
+ // Caller should initialize 'Code' to 0 and call DetectCode()
+ // until it returns true. Whenever DetectCode() returns false
+ // and 'Code' is not 0, the caller can use 'Code' to display
+ // a message like "Trying code '%c'". If false is returned and
+ // 'Code' is 0, all possible codes have been tried and the caller
+ // can either stop calling DetectCode() (and give some error
+ // message), or start all over again.
+ if (*Code < 'A' || *Code > 'D') {
+ *Code = 'A';
+ return false;
+ }
+ if (*Code <= 'D') {
+ SetMode(modeH);
+ char buf[5];
+ sprintf(buf, "C0D%c", *Code);
+ SetString(buf);
+ SetCode(*Code);
+ cCondWait::SleepMs(2 * REPEATDELAY);
+ if (receivedCommand) {
+ SetMode(modeB);
+ SetString("----");
+ return true;
+ }
+ if (*Code < 'D') {
+ (*Code)++;
+ return false;
+ }
+ }
+ *Code = 0;
+ return false;
+}
+
+void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ if (ChannelNumber && Device->IsPrimaryDevice())
+ SetNumber(cDevice::CurrentChannel());
+}
+
+void cRcuRemote::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
+{
+ SetPoints(1 << Device->DeviceNumber(), Device->Receiving());
+}
+
+class cPluginRcu : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+ const char *device;
+public:
+ cPluginRcu(void);
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ };
+
+cPluginRcu::cPluginRcu(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ device = DEFAULTDEVICE;
+}
+
+const char *cPluginRcu::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return " -d DEV, --device=DEV set the device to use (default is " DEFAULTDEVICE ")\n";
+}
+
+bool cPluginRcu::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ static struct option long_options[] = {
+ { "dev", required_argument, NULL, 'd' },
+ { NULL, no_argument, NULL, 0 }
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "d:", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'd': device = optarg;
+ break;
+ default: return false;
+ }
+ }
+ return true;
+}
+
+bool cPluginRcu::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ new cRcuRemote(device);
+ return true;
+}
+
+VDRPLUGINCREATOR(cPluginRcu); // Don't touch this!