diff -Nur vdr-1.7.16.orig//diseqc.c vdr-1.7.16/diseqc.c --- vdr-1.7.16.orig//diseqc.c 2010-02-06 16:43:31.000000000 +0100 +++ vdr-1.7.16/diseqc.c 2011-03-23 20:50:25.085758720 +0100 @@ -24,6 +24,7 @@ commands = NULL; parsing = false; numCodes = 0; + unicable = -1; } cDiseqc::~cDiseqc() @@ -93,15 +94,17 @@ { char *e = strchr(s, ']'); if (e) { - numCodes = 0; + int num = 0; char *t = s; char *p = s; while (t < e) { - if (numCodes < MaxDiseqcCodes) { + if (num < MaxDiseqcCodes) { errno = 0; int n = strtol(t, &p, 16); if (!errno && p != t && 0 <= n && n <= 255) { - codes[numCodes++] = uchar(n); + if (parsing) + codes[num] = uchar(n); + ++num; t = skipspace(p); } else { @@ -114,6 +117,8 @@ return NULL; } } + if (parsing) + numCodes = num; return e + 1; } else @@ -121,6 +126,46 @@ return NULL; } +char *cDiseqc::Unicable(char *s) +{ + char *p = NULL; + errno = 0; + int n = strtol(s, &p, 10); + if (!errno && p != s && n >= 0 && n < 8) { + if (parsing) + unicable = n; + return p; + } + esyslog("ERROR: invalid unicable sat in '%s'", s - 1); + return NULL; +} + +unsigned int cDiseqc::UnicableFreq(unsigned int frequency, int satcr, unsigned int bpf) +{ + unsigned int t = frequency == 0 ? 0 : (frequency + bpf + 2) / 4 - 350; + if (t < 1024 && satcr >= 0 && satcr < 8) + { + codes[3] = t >> 8 | (t == 0 ? 0 : unicable << 2) | satcr << 5; + codes[4] = t; + return (t + 350) * 4 - frequency; + } + + return 0; +} + +void cDiseqc::UnicablePin(int pin) +{ + if (pin >= 0 && pin <= 255) { + numCodes = 6; + codes[2] = 0x5c; + codes[5] = pin; + } + else { + numCodes = 5; + codes[2] = 0x5a; + } +} + cDiseqc::eDiseqcActions cDiseqc::Execute(char **CurrentAction) { if (!*CurrentAction) @@ -136,6 +181,7 @@ case 'B': return daMiniB; case 'W': *CurrentAction = Wait(*CurrentAction); break; case '[': *CurrentAction = Codes(*CurrentAction); return *CurrentAction ? daCodes : daNone; + case 'U': *CurrentAction = Unicable(*CurrentAction); return *CurrentAction ? daUnicable : daNone; default: return daNone; } } @@ -161,3 +207,46 @@ } return NULL; } + +// --- cUnicable -------------------------------------------------------------- + +cUnicable::cUnicable() +{ + satcr = -1; + bpf = 0; + pin = -1; + unused = true; +} + +bool cUnicable::Parse(const char *s) +{ + bool result = false; + int fields = sscanf(s, "%d %u %d", &satcr, &bpf, &pin); + if (fields >= 2) { + if (satcr >= 0 && satcr < 8) + result = true; + else + esyslog("Error: invalid unicable channel '%d'", satcr); + if (result && fields == 3 && (pin < 0 || pin > 255)) { + esyslog("Error: invalid unicable pin '%d'", pin); + result = false; + } + } + return result; +} + + +// --- cUnicables -------------------------------------------------------------- + +cUnicables Unicables; + +cUnicable *cUnicables::GetUnused() +{ + for (cUnicable *p = First(); p; p = Next(p)) { + if (p->Unused()) { + p->Use(); + return p; + } + } + return NULL; +} diff -Nur vdr-1.7.16.orig//diseqc.conf vdr-1.7.16/diseqc.conf --- vdr-1.7.16.orig//diseqc.conf 2010-02-06 16:54:04.000000000 +0100 +++ vdr-1.7.16/diseqc.conf 2011-03-23 20:50:25.085758720 +0100 @@ -18,6 +18,7 @@ # V voltage high (18V) # A mini A # B mini B +# Un unicable code sequence for bank n follows # Wnn wait nn milliseconds (nn may be any positive integer number) # [xx ...] hex code sequence (max. 6) # @@ -75,3 +76,16 @@ # S19.2E 99999 H 10560 t v # S19.2E 12110 V 11080 t v # S19.2E 99999 V 10720 t v +# +# Unicable +# +# S19.2E 11700 V 9750 t V U0 W10 [E0 10 5A 00 00] W10 v +# S19.2E 99999 V 10600 t V U1 W10 [E0 10 5A 00 00] W10 v +# S19.2E 11700 H 9750 t V U2 W10 [E0 10 5A 00 00] W10 v +# S19.2E 99999 H 10600 t V U3 W10 [E0 10 5A 00 00] W10 v +# +# S13.0E 11700 V 9750 t V U4 W10 [E0 10 5A 00 00] W10 v +# S13.0E 99999 V 10600 t V U5 W10 [E0 10 5A 00 00] W10 v +# S13.0E 11700 H 9750 t V U6 W10 [E0 10 5A 00 00] W10 v +# S13.0E 99999 H 10600 t V U7 W10 [E0 10 5A 00 00] W10 v + diff -Nur vdr-1.7.16.orig//diseqc.h vdr-1.7.16/diseqc.h --- vdr-1.7.16.orig//diseqc.h 2010-02-06 16:14:42.000000000 +0100 +++ vdr-1.7.16/diseqc.h 2011-03-23 20:50:25.085758720 +0100 @@ -23,6 +23,7 @@ daMiniA, daMiniB, daCodes, + daUnicable, }; enum { MaxDiseqcCodes = 6 }; private: @@ -32,11 +33,13 @@ char polarization; int lof; char *commands; + int unicable; bool parsing; uchar codes[MaxDiseqcCodes]; int numCodes; char *Wait(char *s); char *Codes(char *s); + char *Unicable(char *s); public: cDiseqc(void); ~cDiseqc(); @@ -56,6 +59,9 @@ int Lof(void) const { return lof; } const char *Commands(void) const { return commands; } uchar *Codes(int &NumCodes) { NumCodes = numCodes; return numCodes ? codes : NULL; } + bool Unicable() const { return unicable >= 0; } + unsigned int UnicableFreq(unsigned int frequency, int satcr, unsigned int bpf); + void UnicablePin(int pin); }; class cDiseqcs : public cConfig { @@ -65,4 +71,27 @@ extern cDiseqcs Diseqcs; +class cUnicable : public cListObject { +private: + int satcr; + unsigned int bpf; + int pin; + bool unused; +public: + cUnicable(); + bool Parse(const char *s); + int Satcr() const { return satcr; } + unsigned int Bpf() const { return bpf; } + int Pin() const { return pin; } + bool Unused() const { return unused; } + void Use() { unused = false; } + }; + +class cUnicables : public cConfig { +public: + cUnicable *GetUnused(); + }; + +extern cUnicables Unicables; + #endif //__DISEQC_H diff -Nur vdr-1.7.16.orig//dvbdevice.c vdr-1.7.16/dvbdevice.c --- vdr-1.7.16.orig//dvbdevice.c 2011-03-11 16:12:12.000000000 +0100 +++ vdr-1.7.16/dvbdevice.c 2011-03-23 20:54:59.822855382 +0100 @@ -267,7 +267,8 @@ time_t lastTimeoutReport; fe_delivery_system frontendType; cChannel channel; - const char *diseqcCommands; + cUnicable *unicable; + cDiseqc *diseqcLast; eTunerStatus tunerStatus; cMutex mutex; cCondVar locked; @@ -276,6 +277,7 @@ dvb_diseqc_master_cmd diseqc_cmd; #endif /* ROTOR */ bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0); + void ExecuteDiseqc(cDiseqc *diseqc, unsigned int &frequency); bool SetFrontend(void); virtual void Action(void); public: @@ -288,6 +290,9 @@ bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); #endif /* ROTOR */ bool Locked(int TimeoutMs = 0); + int UnicableSatcr() const { return unicable ? unicable->Satcr() : -1; } + int UnicableBpf() const { return unicable ? unicable->Bpf() : 0; } + int UnicablePin() const { return unicable ? unicable->Pin() : -1; } }; cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType) @@ -303,10 +308,13 @@ tuneTimeout = 0; lockTimeout = 0; lastTimeoutReport = 0; - diseqcCommands = NULL; + unicable = NULL; + diseqcLast = NULL; tunerStatus = tsIdle; - if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) + if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) { CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power + unicable = Unicables.GetUnused(); + } SetDescription("tuner on frontend %d/%d", adapter, frontend); Start(); } @@ -317,6 +325,10 @@ newSet.Broadcast(); locked.Broadcast(); Cancel(3); + if (diseqcLast && diseqcLast->Unicable()) { + unsigned int frequency = 0; + ExecuteDiseqc(diseqcLast, frequency); + } } bool cDvbTuner::IsTunedTo(const cChannel *Channel) const @@ -390,6 +402,43 @@ return f; } +void cDvbTuner::ExecuteDiseqc(cDiseqc *diseqc, unsigned int &frequency) +{ + cDiseqc::eDiseqcActions da; + for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) { + switch (da) { + case cDiseqc::daNone: break; + case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break; + case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break; + case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break; + case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break; + case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break; + case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break; + case cDiseqc::daCodes: { + int n = 0; + uchar *codes = diseqc->Codes(n); + if (codes) { + struct dvb_diseqc_master_cmd cmd; + memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg)))); + cmd.msg_len = n; + CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); + } + } + break; + case cDiseqc::daUnicable: { + frequency = diseqc->UnicableFreq(frequency, UnicableSatcr(), UnicableBpf()); + diseqc->UnicablePin(UnicablePin()); + if (frequency == 0) { + CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); + esyslog("ERROR: unable to setup unicable frequency for channel %d (f=%u, s=%d, b=%d)", channel.Number(), frequency, UnicableSatcr(), UnicableBpf()); + } + } + break; + default: esyslog("ERROR: unknown diseqc command %d", da); + } + } +} + bool cDvbTuner::SetFrontend(void) { #define MAXFRONTENDCMDS 16 @@ -419,34 +468,14 @@ if (Setup.DiSEqC) { cDiseqc *diseqc = Diseqcs.Get(device, channel.Source(), channel.Frequency(), dtp.Polarization()); if (diseqc) { - if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) { - cDiseqc::eDiseqcActions da; - for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) { - switch (da) { - case cDiseqc::daNone: break; - case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break; - case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break; - case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break; - case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break; - case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break; - case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break; - case cDiseqc::daCodes: { - int n = 0; - uchar *codes = diseqc->Codes(n); - if (codes) { - struct dvb_diseqc_master_cmd cmd; - cmd.msg_len = min(n, int(sizeof(cmd.msg))); - memcpy(cmd.msg, codes, cmd.msg_len); - CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); - } - } - break; - default: esyslog("ERROR: unknown diseqc command %d", da); - } - } - diseqcCommands = diseqc->Commands(); - } frequency -= diseqc->Lof(); + if (diseqc->Commands() && (!diseqcLast || strcmp(diseqcLast->Commands(), diseqc->Commands()) != 0 || diseqc->Unicable())) { + ExecuteDiseqc(diseqc, frequency); + if (frequency == 0) { + return false; + } + diseqcLast = diseqc; + } } else { esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number()); @@ -571,7 +600,7 @@ case tsTuned: if (Timer.TimedOut()) { tunerStatus = tsSet; - diseqcCommands = NULL; + diseqcLast = NULL; if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder()); lastTimeoutReport = time(NULL); @@ -581,7 +610,7 @@ case tsLocked: if (Status & FE_REINIT) { tunerStatus = tsSet; - diseqcCommands = NULL; + diseqcLast = NULL; isyslog("frontend %d/%d was reinitialized", adapter, frontend); lastTimeoutReport = 0; continue; diff -Nur vdr-1.7.16.orig//unicable.conf vdr-1.7.16/unicable.conf --- vdr-1.7.16.orig//unicable.conf 1970-01-01 01:00:00.000000000 +0100 +++ vdr-1.7.16/unicable.conf 2011-03-23 20:50:25.085758720 +0100 @@ -0,0 +1,15 @@ +# Unicable configuration for VDR +# +# Format: +# +# channel frequency [pin] +# +# channel: unicable channel (0-7) +# frequency: frequency of the unicable channel +# pin: optional pin of the unicable channel (0-255) +# +# Examples: + +0 1400 +1 1516 + diff -Nur vdr-1.7.16.orig//vdr.c vdr-1.7.16/vdr.c --- vdr-1.7.16.orig//vdr.c 2011-03-11 16:12:12.000000000 +0100 +++ vdr-1.7.16/vdr.c 2011-03-23 20:50:25.089758737 +0100 @@ -601,6 +601,7 @@ Setup.Load(AddDirectory(ConfigDirectory, "setup.conf")); Sources.Load(AddDirectory(ConfigDirectory, "sources.conf"), true, true); Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC); + Unicables.Load(AddDirectory(ConfigDirectory, "unicable.conf"), true); Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true); Timers.Load(AddDirectory(ConfigDirectory, "timers.conf")); Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"));