summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Hanisch <dvb@flensrocker.de>2011-08-18 01:22:13 +0200
committerLars Hanisch <dvb@flensrocker.de>2011-08-18 01:22:13 +0200
commita3f74f995fa94edd9eca26986e65287314ae8ad5 (patch)
tree485f38c1988f71fa938536c68d33256bc3884ba5
parent3b77d96d6f98d4ac9729edda05cd9c0d4eb2902a (diff)
downloadvdr-plugin-dynamite-a3f74f995fa94edd9eca26986e65287314ae8ad5.tar.gz
vdr-plugin-dynamite-a3f74f995fa94edd9eca26986e65287314ae8ad5.tar.bz2
add new combined patch of dynamite and lnb-sharing
-rw-r--r--patches/vdr-lnb-sharing-dynamite-0.1.4-1.7.20.patch1450
1 files changed, 1450 insertions, 0 deletions
diff --git a/patches/vdr-lnb-sharing-dynamite-0.1.4-1.7.20.patch b/patches/vdr-lnb-sharing-dynamite-0.1.4-1.7.20.patch
new file mode 100644
index 0000000..b5ec1ff
--- /dev/null
+++ b/patches/vdr-lnb-sharing-dynamite-0.1.4-1.7.20.patch
@@ -0,0 +1,1450 @@
+diff --git a/HISTORY.LNBshare b/HISTORY.LNBshare
+new file mode 100644
+index 0000000..d6eeb7f
+--- /dev/null
++++ b/HISTORY.LNBshare
+@@ -0,0 +1,42 @@
++configurableLNBshare Revision History
++
++Version for VDR 1.4.3
++
++- configurableLNBshare-VDR_1.4.3.patch from Matthias Lötzke (http://lötzke.de/vdr.html)
++
++2010-04-10: Version for VDR 1.7.14
++- Created HISTORY.LNBshare
++- Created README.LNBshare
++- Fixed possible buffer overflow in cMenuSetupLNB::Setup(void)
++- Ignore all non-DVB-S devices when checking for possible conflicts.
++- Use the 'actual' device instead of the 'primary' device as device that is used as reciever for live viewing. This adds support for 'other' output devices than a FF card like xine. Based on a patch from CR7 at vdr-portal.de
++- Adept to VDR 1.7.14. Support the new diseqc feature introduced with VDR 1.7.13: 'Added device definitions to the diseqc.conf file format, so that certain satellite positions can be limited to a given list of devices'.
++
++2010-11-13:
++- Fix: Devices sharing same LNB don't switch channel on main channel switch (thanks to hivdr @ vdr-portal.de for reporting the bug and spockele @ vdr-portal.de and hopsi @ vdr-portal.de for testing the correction)
++- Fix: In LOG, device numbering is now as in VDR: Starting from 1, ...
++- Fix: Encoding of german umlaut in "DVB-Empfänger %d nutzt LNB Nr"
++
++2010-11-28:
++- Fix: Device numbering in message 'LNB or DiSEq conflict with device ...'.
++- Fix: Numbering of LNBs not using SAT
++
++2010-12-25: Version 0.1.0 for VDR version 1.7.16
++- Rename the patch to include patch version number (vdr-lnb-sharing-0.1.0-1.7.16.patch)
++- Write patch version number to log file (if logging is switched on).
++- Adept to VDR 1.7.16
++
++2011-02-06 Version 0.1.1 for VDR version 1.7.16
++- Fix: If several DVB devices share one LNB, only the first of these devises will send signals like 22kHz. See README.LNBshare for details.
++ Thanks to Uwe (Licherkette @ vdr-portal.de) for finding and analyzing the error and testing the correction.
++- README.LNBshare: Include warning about 1.3 FF cards with buggy Loop-Through that might result in destruction of LNBP-Chips if the Loop-Through is used
++- Fix: Log file: Numbering of 'LNBs' not connected to a SAT DVB card
++
++2011-02-08 Version 0.1.2 for VDR version 1.7.16
++- Change: Make it easier to apply this patch together with the dynamite-Plugin patch. Note: this is mostly for maintainers of distributions and multi-patches. It will (must probably) not be possible for devices added with dynamite to share LNBs. Some concept work is required for this: How can VDR uniquely identify a device? How can a user identify such a device on OSD?
++
++2011-02-16 Version 0.1.3 for VDR version 1.7.16
++- Fix an error that can occur if devices are explizitly specified (-D or --device) during start of VDR. Thanks to Lars (mini73 @ vdr-portal.de) for reporting this error.
++
++2011-02-18 Version 0.1.4 for VDR version 1.7.16
++- Fix: Short before a recording starts, the system switches between black screen and channel. If VPS is used, the recording will not even start. Thanks to urknall @ vdr-portal.de for reporting this error and Uwe (Licherkette @ vdr-portal.de) for providing a Log
+diff --git a/README.LNBshare b/README.LNBshare
+new file mode 100644
+index 0000000..1d624bf
+--- /dev/null
++++ b/README.LNBshare
+@@ -0,0 +1,32 @@
++Warning
++- At some 1.3 FF-Cards a voltage at the Loop-Through will destroy the LNBP-Chips of these cards. Please check the manual. If you are unsure or your card is affected, don't use the Loop-Through! Even if this patch avoids switching on the voltage in such a case, the patch might be buggy or wrong configured or ...
++
++Required hardware:
++- sat splitter like SVE 2-01 (reichelt.de). Make sure to have a sat splitter with built in diodes preventing any current in case of different voltage on the connected DVB cards.
++
++Settings (OSD)
++In VDR, if you select Settings -> LNB and have two or more devices that can recieve SAT channels, you can set the 'LNB Nr.' for each of these devices. Note:
++- All devices with an identical 'LNB Nr.' share the same LNB
++- The patch assumes that a device has always the same number. If you have more than two devices, you must make sure to load the drivers in a given order to make sure that each of the listed devices belongs always to the same card. If during setup, for example, your first DVB-S card is device 2 and your second DVB-S card is device 4 (device 1 and 3 are, for example, DVB-T cards) you must make sure that after reboot these stay the same: your first DVB-S card is device 2 and your second DVB-S card is device 4.
++- Signals (like DiSEqC, 22kHz, Voltage) are only sent by the first device if several devices share the same LNB. VDR will even switch on the LNB Voltage only on the first device. Note: This is not guarantied: If you use a Loop-Through, please see the warning in the README and make sure that the FF Card is the first card.
++
++DiSEqC Support:
++DiSEqC is supported, including the feature added in VDR 1.7.13: Added device definitions to the diseqc.conf file format, so that certain satellite positions can be limited to a given list of devices
++Note: Please ensure in diseqc.conf that all devices sharing one LNB have exactly the same satellite positions. The system will not check this, but, otherwise, the patch will not work correctly. This is not a restriction: obviously, if two devices share the same LNB, they can also receive the same satellite positions.
++
++Primary limit (Setup.PrimaryLimit):
++See MANUAL for a description: 'This is mainly useful for recordings that should take place only when there is nothing else to do, but should never keep the user from viewing stuff on the primary interface'
++In unpatched VDR, this seems to work with FF cards. For other output devices (for example vdr-xine), it probably won't work.
++Not fully supported by LNB sharing: If the primary device shares an LNB with device2, device2 might start a recording with priority < Setup.PrimaryLimit and force the primary device to an other channel.
++
++TODO / Missing:
++Intelligent assigning of timers to devices.
++Example: If you have 3 DVB-S cards and 2 of them share the same LNB, you can, in principle, record 2 channels in 'high' band and one in 'low' band. But, with this patch, this is not guaranteed. The DVB-S card with an own LNB might record one of the channels in high band. So, one of the other channels will not be recorded.
++Note:
++- Such intelligent scheduling is generally missing in VDR and would be also useful for 'mixed' systems with DVB-S and DVB-T cards as well as systems in which not all DVB-S cards can receive the same satellites.
++- I don't plan to implement this feature. Any volunteers?
++
++Hompage: http://projects.vdr-developer.org/projects/patch-lnbsharing . Please use this for bug reports. You can also create a new thread in http://www.vdr-portal.de , Developer->Patches for any questions.
++Git: The patch is available in http://projects.vdr-developer.org/git/?p=vdr-patch-lnbsharing.git
++
++License: The same license as for VDR applies. See file COPYING (in VDR sources) for details.
+diff --git a/ci.h b/ci.h
+index c31dccf..daa18f6 100644
+--- a/ci.h
++++ b/ci.h
+@@ -115,6 +115,8 @@ public:
+ ///< The derived class must call Cancel(3) in its destructor.
+ virtual bool Ready(void);
+ ///< Returns 'true' if all present CAMs in this adapter are ready.
++ virtual bool SetIdle(bool Idle, bool TestOnly) { return false; }
++ virtual bool IsIdle(void) const { return false; }
+ };
+
+ class cTPDU;
+diff --git a/config.c b/config.c
+index 6767b5c..59f544b 100644
+--- a/config.c
++++ b/config.c
+@@ -399,6 +399,10 @@ cSetup::cSetup(void)
+ InitialVolume = -1;
+ ChannelsWrap = 0;
+ EmergencyExit = 1;
++//ML
++ VerboseLNBlog = 0;
++ for (int i = 0; i < MAXDEVICES; i++) CardUsesLnbNr[i] = i + 1;
++//ML-Ende
+ }
+
+ cSetup& cSetup::operator= (const cSetup &s)
+@@ -592,7 +596,23 @@ bool cSetup::Parse(const char *Name, const char *Value)
+ else if (!strcasecmp(Name, "ChannelsWrap")) ChannelsWrap = atoi(Value);
+ else if (!strcasecmp(Name, "EmergencyExit")) EmergencyExit = atoi(Value);
+ else
+- return false;
++
++//ML
++ if (!strcasecmp(Name, "VerboseLNBlog")) VerboseLNBlog = atoi(Value);
++ else {
++ char tmp[20];
++ bool result = false;
++ for (int i = 1; i <= MAXDEVICES; i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ if (!strcasecmp(Name, tmp)) {
++ CardUsesLnbNr[i - 1] = atoi(Value);
++ result = true;
++ }
++ }
++ return result;
++ }
++//ML-Ende
++
+ return true;
+ }
+
+@@ -688,6 +708,17 @@ bool cSetup::Save(void)
+ Store("ChannelsWrap", ChannelsWrap);
+ Store("EmergencyExit", EmergencyExit);
+
++//ML
++ Store("VerboseLNBlog", VerboseLNBlog);
++ char tmp[20];
++ if (cDevice::NumDevices() > 1) {
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ sprintf(tmp, "Card%dusesLNBnr", i);
++ Store(tmp, CardUsesLnbNr[i - 1]);
++ }
++ }
++//ML-Ende
++
+ Sort();
+
+ if (cConfig<cSetupLine>::Save()) {
+diff --git a/config.h b/config.h
+index 19f8768..ac53ac3 100644
+--- a/config.h
++++ b/config.h
+@@ -290,6 +290,16 @@ public:
+ int InitialVolume;
+ int ChannelsWrap;
+ int EmergencyExit;
++
++//ML
++ #define LNB_SHARING_VERSION "0.1.4"
++ int VerboseLNBlog;
++ #define MAXDEVICES 16 // Since VDR 1.3.32 we can not #include "device.h" for MAXDEVICES anymore.
++ // With this workaround a warning will be shown during compilation if
++ // MAXDEVICES changes in device.h.
++ int CardUsesLnbNr[MAXDEVICES];
++//ML-Ende
++
+ int __EndData__;
+ cString InitialChannel;
+ cSetup(void);
+diff --git a/device.c b/device.c
+index 0759993..16811d2 100644
+--- a/device.c
++++ b/device.c
+@@ -72,12 +72,22 @@ cDevice *cDevice::device[MAXDEVICES] = { NULL };
+ cDevice *cDevice::primaryDevice = NULL;
+ cDevice *cDevice::avoidDevice = NULL;
+ cList<cDeviceHook> cDevice::deviceHooks;
++cDevice *cDevice::nextParentDevice = NULL;
+
+-cDevice::cDevice(void)
++cDevice::cDevice(cDevice *ParentDevice)
+ :patPmtParser(true)
+-{
+- cardIndex = nextCardIndex++;
+- dsyslog("new device number %d", CardIndex() + 1);
++,isIdle(false)
++,parentDevice(ParentDevice)
++,subDevice(NULL)
++{
++ if (!ParentDevice)
++ parentDevice = nextParentDevice;
++ cDevice::nextParentDevice = NULL;
++ if (parentDevice)
++ cardIndex = parentDevice->cardIndex;
++ else
++ cardIndex = nextCardIndex++;
++ dsyslog("new %sdevice number %d", parentDevice ? "sub-" : "", CardIndex() + 1);
+
+ SetDescription("receiver on device %d", CardIndex() + 1);
+
+@@ -108,10 +118,14 @@ cDevice::cDevice(void)
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ receiver[i] = NULL;
+
+- if (numDevices < MAXDEVICES)
+- device[numDevices++] = this;
++ if (!parentDevice) {
++ if (numDevices < MAXDEVICES)
++ device[numDevices++] = this;
++ else
++ esyslog("ERROR: too many devices or \"dynamite\"-unpatched device creator!");
++ }
+ else
+- esyslog("ERROR: too many devices!");
++ parentDevice->subDevice = this;
+ }
+
+ cDevice::~cDevice()
+@@ -120,6 +134,29 @@ cDevice::~cDevice()
+ DetachAllReceivers();
+ delete liveSubtitle;
+ delete dvbSubtitleConverter;
++ if (parentDevice && (parentDevice->subDevice == this))
++ parentDevice->subDevice = NULL;
++}
++
++bool cDevice::SetIdle(bool Idle)
++{
++ if (parentDevice)
++ return parentDevice->SetIdle(Idle);
++ if (isIdle == Idle)
++ return true;
++ if (Receiving(false))
++ return false;
++ if (Idle) {
++ Detach(player);
++ DetachAllReceivers();
++ }
++ if (!SetIdleDevice(Idle, true))
++ return false;
++ isIdle = Idle;
++ if (SetIdleDevice(Idle, false))
++ return true;
++ isIdle = !Idle;
++ return false;
+ }
+
+ bool cDevice::WaitForAllDevicesReady(int Timeout)
+@@ -144,6 +181,31 @@ void cDevice::SetUseDevice(int n)
+ useDevice |= (1 << n);
+ }
+
++//ML
++void cDevice::SetLnbNr(void)
++{
++ for (int i = 0; i < numDevices; i++) {
++ device[i]->SetLnbNrFromSetup();
++ }
++}
++
++
++bool cDevice::IsLnbSendSignals(void)
++{
++ if (parentDevice)
++ return parentDevice->IsLnbSendSignals();
++ for (int i = 0; device[i] != this && i < numDevices; i++) {
++ if (device[i]->IsShareLnb(this) ) {
++ isyslog("Device %d: will not send any signal (like 22kHz) to LNB as device %d will do this", cardIndex+1, device[i]->cardIndex + 1);
++ return false;
++ }
++ }
++ isyslog("Device %d: will send signals (like 22kHz) to LNB nr. = %d ", cardIndex+1, LnbNr() );
++ return true;
++}
++
++//ML-Ende
++
+ int cDevice::NextCardIndex(int n)
+ {
+ if (n > 0) {
+@@ -158,6 +220,8 @@ int cDevice::NextCardIndex(int n)
+
+ int cDevice::DeviceNumber(void) const
+ {
++ if (parentDevice)
++ return parentDevice->DeviceNumber();
+ for (int i = 0; i < numDevices; i++) {
+ if (device[i] == this)
+ return i;
+@@ -263,6 +327,10 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
+ for (int i = 0; i < numDevices; i++) {
+ if (device[i] == AvoidDevice)
+ continue; // this device shall be temporarily avoided
++ // LNB - Sharing
++ if (AvoidDevice && device[i]->IsShareAvoidDevice(Channel, AvoidDevice) )
++ continue; // this device shall be temporarily avoided
++ // LNB - Sharing END
+ if (Channel->Ca() && Channel->Ca() <= CA_DVB_MAX && Channel->Ca() != device[i]->CardIndex() + 1)
+ continue; // a specific card was requested, but not this one
+ if (NumUsableSlots && !CamSlots.Get(j)->Assign(device[i], true))
+@@ -283,9 +351,20 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
+ imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
+ imp <<= 4; imp |= GetClippedNumProvidedSystems(4, device[i]) - 1; // avoid cards which support multiple delivery systems
+ imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
+- imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++ // LNB - Sharing
++ int badPriority = device[i]->GetMaxBadPriority(Channel);
++ if (badPriority < 0 ) // a device receiving with lower priority would need to be stopped
++ {
++ imp <<= 8; imp |= min(max(device[i]->Priority() + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++ } else {
++ imp <<= 8; imp |= min(max(max(device[i]->Priority(), badPriority) + MAXPRIORITY, 0), 0xFF); // use the device with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
++ }
++ // LNB - Sharing End
+ imp <<= 8; imp |= min(max((NumUsableSlots ? SlotPriority[j] : 0) + MAXPRIORITY, 0), 0xFF); // use the CAM slot with the lowest priority (+MAXPRIORITY to assure that values -99..99 can be used)
+ imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
++ // LNB - Sharing
++ imp <<= 1; imp |= (badPriority == -1); // avoid cards where the actual device needs to be switched
++ // LNB - Sharing End
+ imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
+ imp <<= 1; imp |= device[i]->AvoidRecording(); // avoid SD full featured cards
+ imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
+@@ -328,6 +407,8 @@ bool cDevice::HasCi(void)
+
+ void cDevice::SetCamSlot(cCamSlot *CamSlot)
+ {
++ if (parentDevice)
++ return parentDevice->SetCamSlot(CamSlot);
+ camSlot = CamSlot;
+ }
+
+@@ -531,6 +612,10 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
+
+ void cDevice::StartSectionHandler(void)
+ {
++ if (parentDevice) {
++ parentDevice->StartSectionHandler();
++ return;
++ }
+ if (!sectionHandler) {
+ sectionHandler = new cSectionHandler(this);
+ AttachFilter(eitFilter = new cEitFilter);
+@@ -542,6 +627,10 @@ void cDevice::StartSectionHandler(void)
+
+ void cDevice::StopSectionHandler(void)
+ {
++ if (parentDevice) {
++ parentDevice->StopSectionHandler();
++ return;
++ }
+ if (sectionHandler) {
+ delete nitFilter;
+ delete sdtFilter;
+@@ -568,12 +657,17 @@ void cDevice::CloseFilter(int Handle)
+
+ void cDevice::AttachFilter(cFilter *Filter)
+ {
++ if (parentDevice)
++ return parentDevice->AttachFilter(Filter);
++ SetIdle(false);
+ if (sectionHandler)
+ sectionHandler->Attach(Filter);
+ }
+
+ void cDevice::Detach(cFilter *Filter)
+ {
++ if (parentDevice)
++ return parentDevice->Detach(Filter);
+ if (sectionHandler)
+ sectionHandler->Detach(Filter);
+ }
+@@ -602,7 +696,11 @@ bool cDevice::ProvidesTransponder(const cChannel *Channel) const
+ bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
+ {
+ for (int i = 0; i < numDevices; i++) {
+- if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
++
++//ML
++ if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel) && device[i]->IsShareLnb(this) )
++//ML-Ende
++
+ return false;
+ }
+ return true;
+@@ -694,6 +792,71 @@ bool cDevice::SwitchChannel(int Direction)
+ return result;
+ }
+
++// ML
++cDevice *cDevice::GetBadDevice(const cChannel *Channel)
++{
++ if (parentDevice)
++ return parentDevice->GetBadDevice(Channel);
++ if(!cSource::IsSat(Channel->Source())) return NULL; // no conflict if the new channel is not on sat
++ if(!ProvidesSource(cSource::stSat)) return NULL; // no conflict if this device is not on sat
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->IsShareLnb(this) && device[i]->IsLnbConflict(Channel) ) {
++ // there is a conflict between device[i] and 'this' if we tune this to Channel
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for channel %d on device %d. LNB or DiSEq conflict with device %d", LnbNr(), Channel->Number(), this->cardIndex + 1, device[i]->cardIndex + 1);
++ }
++ return device[i];
++ }
++ }
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Device check for channel %d on device %d. OK", LnbNr(), Channel->Number(), this->cardIndex + 1);
++ }
++ return NULL;
++}
++
++int cDevice::GetMaxBadPriority(const cChannel *Channel) const
++{
++ if (parentDevice)
++ return parentDevice->GetMaxBadPriority(Channel);
++ if(!cSource::IsSat(Channel->Source())) return -2; // no conflict if the new channel is not on sat
++ if(!ProvidesSource(cSource::stSat)) return -2; // no conflict if this device is not on sat
++
++ int maxBadPriority = -2;
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->IsShareLnb(this) && device[i]->IsLnbConflict(Channel) ) {
++ // there is a conflict between device[i] and 'this' if we tune this to Channel
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Conflict for device %d, priority of conflicting device: %d", LnbNr(), device[i]->cardIndex + 1, device[i]->Priority());
++ }
++ if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority) maxBadPriority = device[i]->Priority();
++ if (device[i] == ActualDevice() && maxBadPriority < -1 ) maxBadPriority = -1;
++ }
++ }
++
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Request for channel %d on device %d. MaxBadPriority is %d", LnbNr(), Channel->Number(), this->cardIndex + 1, maxBadPriority);
++ }
++ return maxBadPriority;
++}
++
++bool cDevice::IsShareAvoidDevice(const cChannel *Channel, const cDevice *AvoidDevice) const
++{
++ if (parentDevice)
++ return parentDevice->IsShareAvoidDevice(Channel, AvoidDevice);
++ if(!cSource::IsSat(Channel->Source())) return false; // no conflict if the new channel is not on sat
++ if(!ProvidesSource(cSource::stSat)) return false; // no conflict if this device is not on sat
++
++ for (int i = 0; i < numDevices; i++) {
++ if (this != device[i] && device[i]->IsShareLnb(this) && device[i]->IsLnbConflict(Channel) ) {
++ // there is a conflict between device[i] and 'this' if we tune this to Channel
++ if(device[i] == AvoidDevice) return true;
++ }
++ }
++ return false;
++}
++// ML Ende
++
++
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
+ if (LiveView) {
+@@ -708,6 +871,13 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+
+ eSetChannelResult Result = scrOk;
+
++//ML
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: Switching device %d to channel %d", LnbNr(), this->DeviceNumber() + 1, Channel->Number());
++ }
++//ML-Ende
++
++
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+@@ -730,6 +900,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ sectionHandler->SetStatus(false);
+ sectionHandler->SetChannel(NULL);
+ }
++ SetIdle(false);
+ // Tell the camSlot about the channel switch and add all PIDs of this
+ // channel to it, for possible later decryption:
+ if (camSlot)
+@@ -776,8 +947,10 @@ void cDevice::ForceTransferMode(void)
+ {
+ if (!cTransferControl::ReceiverDevice()) {
+ cChannel *Channel = Channels.GetByNumber(CurrentChannel());
+- if (Channel)
++ if (Channel) {
++ SetIdle(false);
+ SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode
++ }
+ }
+ }
+
+@@ -1148,7 +1321,10 @@ bool cDevice::Transferring(void) const
+
+ bool cDevice::AttachPlayer(cPlayer *Player)
+ {
++ if (parentDevice)
++ return parentDevice->AttachPlayer(Player);
+ if (CanReplay()) {
++ SetIdle(false);
+ if (player)
+ Detach(player);
+ DELETENULL(liveSubtitle);
+@@ -1167,6 +1343,8 @@ bool cDevice::AttachPlayer(cPlayer *Player)
+
+ void cDevice::Detach(cPlayer *Player)
+ {
++ if (parentDevice)
++ return parentDevice->Detach(Player);
+ if (Player && player == Player) {
+ cPlayer *p = player;
+ player = NULL; // avoids recursive calls to Detach()
+@@ -1186,6 +1364,8 @@ void cDevice::Detach(cPlayer *Player)
+
+ void cDevice::StopReplay(void)
+ {
++ if (parentDevice)
++ return parentDevice->StopReplay();
+ if (player) {
+ Detach(player);
+ if (IsPrimaryDevice())
+@@ -1468,6 +1648,8 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
+
+ int cDevice::Priority(void) const
+ {
++ if (parentDevice)
++ return parentDevice->Priority();
+ int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i])
+@@ -1483,6 +1665,8 @@ bool cDevice::Ready(void)
+
+ bool cDevice::Receiving(bool CheckAny) const
+ {
++ if (parentDevice)
++ return parentDevice->Receiving(CheckAny);
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i] && (CheckAny || receiver[i]->priority >= 0)) // cReceiver with priority < 0 doesn't count
+ return true;
+@@ -1562,10 +1746,13 @@ bool cDevice::GetTSPacket(uchar *&Data)
+
+ bool cDevice::AttachReceiver(cReceiver *Receiver)
+ {
++ if (parentDevice)
++ return parentDevice->AttachReceiver(Receiver);
+ if (!Receiver)
+ return false;
+ if (Receiver->device == this)
+ return true;
++ SetIdle(false);
+ // activate the following line if you need it - actually the driver should be fixed!
+ //#define WAIT_FOR_TUNER_LOCK
+ #ifdef WAIT_FOR_TUNER_LOCK
+@@ -1604,6 +1791,8 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
+
+ void cDevice::Detach(cReceiver *Receiver)
+ {
++ if (parentDevice)
++ return parentDevice->Detach(Receiver);
+ if (!Receiver || Receiver->device != this)
+ return;
+ bool receiversLeft = false;
+@@ -1629,6 +1818,8 @@ void cDevice::Detach(cReceiver *Receiver)
+
+ void cDevice::DetachAll(int Pid)
+ {
++ if (parentDevice)
++ return parentDevice->DetachAll(Pid);
+ if (Pid) {
+ cMutexLock MutexLock(&mutexReceiver);
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+@@ -1641,6 +1832,8 @@ void cDevice::DetachAll(int Pid)
+
+ void cDevice::DetachAllReceivers(void)
+ {
++ if (parentDevice)
++ return parentDevice->DetachAllReceivers();
+ cMutexLock MutexLock(&mutexReceiver);
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ Detach(receiver[i]);
+@@ -1712,3 +1905,25 @@ uchar *cTSBuffer::Get(void)
+ }
+ return NULL;
+ }
++
++// --- cDynamicDeviceProbe -------------------------------------------------------
++
++cList<cDynamicDeviceProbe> DynamicDeviceProbes;
++
++cList<cDynamicDeviceProbe::cDynamicDeviceProbeItem> cDynamicDeviceProbe::commandQueue;
++
++void cDynamicDeviceProbe::QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath)
++{
++ if (DevPath)
++ commandQueue.Add(new cDynamicDeviceProbeItem(Cmd, new cString(DevPath)));
++}
++
++cDynamicDeviceProbe::cDynamicDeviceProbe(void)
++{
++ DynamicDeviceProbes.Add(this);
++}
++
++cDynamicDeviceProbe::~cDynamicDeviceProbe()
++{
++ DynamicDeviceProbes.Del(this, false);
++}
+diff --git a/device.h b/device.h
+index d937e5f..9c19e10 100644
+--- a/device.h
++++ b/device.h
+@@ -159,11 +159,50 @@ public:
+ static void Shutdown(void);
+ ///< Closes down all devices.
+ ///< Must be called at the end of the program.
++//ML
++public:
++ static void SetLnbNr(void);
++ ///< Called after changes in setup
++ ///< call SetLNBNrFromSetup for each device
++ virtual void SetLnbNrFromSetup(void) {};
++ ///< Called after changes in setup
++ ///< Read Setup.CardUsesLNBnr, write value to member variable of this class
++ ///< Only implemented in dvbdevice, other devices don't use LNBs
++ bool IsLnbSendSignals(void);
++ ///< If several devices share the same LNB, only the first of these
++ ///< must send signals (like 22 kHz) to the LNB
++ ///< check, whepher this device must send these signals
++ virtual int LnbNr(void) const { return ( cardIndex + 1 ) * -1; };
++ ///< Number of LNB. This is -cardIndex for all non-DVB devices.
++ ///< So, there will be no LNB conflicts for non-DVB devices.
++ virtual bool IsShareLnb(const cDevice *Device) { return false; };
++ ///< True if both devices differ (this != Device) and share the same LNB
++ virtual bool IsLnbConflict(const cChannel *Channel) { return false; };
++ ///< false if 'Channel' can be recieved with the same
++ ///< LNB as 'this' device is using
++ ///< Otherwise, true
++ cDevice *GetBadDevice(const cChannel *Channel) ;
++ ///< Returns NULL if there is no device which uses the same LNB or if
++ ///< all of those devices are tuned to the same frequency band and
++ ///< polarization as of the requested channel.
++ ///< Otherwise returns the first device found.
++ int GetMaxBadPriority(const cChannel *Channel) const;
++ ///< Returns the highest priority of all receiving devices which use
++ ///< the same LNB and are tuned to a different frequency band or
++ ///< polarization as of the requested channel.
++ ///< Returns -1 if there are no such devices, but the 'actual' device
++ ///< (device recieving live view) would be affected by switching to the requested channel.
++ ///< Returns -2 if there are no such devices and the actual device
++ ///< would not be affected by switching to the requested channel.
++ bool IsShareAvoidDevice(const cChannel *Channel, const cDevice *AvoidDevice) const;
++ ///< Returns true if switching this device to Channel will result in a switch of AvoidDevice
++//ML-Ende
++
++
+ private:
+ static int nextCardIndex;
+ int cardIndex;
+ protected:
+- cDevice(void);
+ virtual ~cDevice();
+ virtual bool Ready(void);
+ ///< Returns true if this device is ready. Devices with conditional
+@@ -190,9 +229,6 @@ protected:
+ ///< A derived class must call the MakePrimaryDevice() function of its
+ ///< base class.
+ public:
+- bool IsPrimaryDevice(void) const { return this == primaryDevice; }
+- int CardIndex(void) const { return cardIndex; }
+- ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
+ int DeviceNumber(void) const;
+ ///< Returns the number of this device (0 ... numDevices).
+ virtual bool HasDecoder(void) const;
+@@ -378,9 +414,6 @@ public:
+ ///< Returns true if this device has a Common Interface.
+ void SetCamSlot(cCamSlot *CamSlot);
+ ///< Sets the given CamSlot to be used with this device.
+- cCamSlot *CamSlot(void) const { return camSlot; }
+- ///< Returns the CAM slot that is currently used with this device,
+- ///< or NULL if no CAM slot is in use.
+
+ // Image Grab facilities
+
+@@ -536,9 +569,6 @@ private:
+ cTsToPes tsToPesSubtitle;
+ bool isPlayingVideo;
+ protected:
+- const cPatPmtParser *PatPmtParser(void) const { return &patPmtParser; }
+- ///< Returns a pointer to the patPmtParser, so that a derived device
+- ///< can use the stream information from it.
+ virtual bool CanReplay(void) const;
+ ///< Returns true if this device can currently start a replay session.
+ virtual bool SetPlayMode(ePlayMode PlayMode);
+@@ -724,6 +754,39 @@ public:
+ ///< Detaches all receivers from this device for this pid.
+ void DetachAllReceivers(void);
+ ///< Detaches all receivers from this device.
++
++// --- dynamite subdevice patch start ---
++ friend class cDynamicDevice;
++private:
++ static cDevice *nextParentDevice;
++ ///< Holds the parent device for the next subdevice
++ ///< so the dynamite-plugin can work with unpatched plugins
++ bool isIdle;
++protected:
++ cDevice *parentDevice;
++ cDevice *subDevice;
++ cDevice(cDevice *ParentDevice = NULL);
++ const cPatPmtParser *PatPmtParser(void) const { if (parentDevice) return parentDevice->PatPmtParser(); return &patPmtParser; }
++ ///< Returns a pointer to the patPmtParser, so that a derived device
++ ///< can use the stream information from it.
++public:
++ bool IsPrimaryDevice(void) const { if (parentDevice) return parentDevice->IsPrimaryDevice(); return this == primaryDevice; }
++ int CardIndex(void) const { if (parentDevice) return parentDevice->cardIndex; return cardIndex; }
++ ///< Returns the card index of this device (0 ... MAXDEVICES - 1).
++ cCamSlot *CamSlot(void) const { if (parentDevice) return parentDevice->CamSlot(); return camSlot; }
++ ///< Returns the CAM slot that is currently used with this device,
++ ///< or NULL if no CAM slot is in use.
++ bool IsSubDevice(void) const { return (parentDevice != NULL); }
++ bool HasSubDevice(void) const { return (subDevice != NULL); }
++ cDevice *SubDevice(void) const { return subDevice; }
++ bool IsIdle(void) const { if (parentDevice) return parentDevice->IsIdle(); return isIdle; }
++ bool SetIdle(bool Idle);
++ virtual bool SetIdleDevice(bool Idle, bool TestOnly) { return false; }
++ ///< Called by SetIdle
++ ///< if TestOnly, don't do anything, just return, if the device
++ ///< can be set to the new idle state
++ virtual bool CanScanForEPG(void) const { return !IsIdle(); }
++ // --- dynamite subdevice patch end ---
+ };
+
+ /// Derived cDevice classes that can receive channels will have to provide
+@@ -747,4 +810,47 @@ public:
+ uchar *Get(void);
+ };
+
++/// A plugin that want to create devices handled by the dynamite-plugin needs to create
++/// a cDynamicDeviceProbe derived object on the heap in order to have its Probe()
++/// function called, where it can actually create the appropriate device.
++/// The cDynamicDeviceProbe object must be created in the plugin's constructor,
++/// and deleted in its destructor.
++/// The "DevPath" hasn't to be a physical device or a path in the filesystem.
++/// It can be any string a plugin may react on.
++
++#define __DYNAMIC_DEVICE_PROBE
++
++enum eDynamicDeviceProbeCommand { ddpcAttach, ddpcDetach, ddpcService };
++
++class cDynamicDeviceProbe : public cListObject {
++ friend class cDynamicDevice;
++private:
++ class cDynamicDeviceProbeItem : public cListObject {
++ public:
++ eDynamicDeviceProbeCommand cmd;
++ cString *devpath;
++ cDynamicDeviceProbeItem(eDynamicDeviceProbeCommand Cmd, cString *DevPath):cmd(Cmd),devpath(DevPath) {}
++ virtual ~cDynamicDeviceProbeItem() { if (devpath) delete devpath; }
++ };
++ static cList<cDynamicDeviceProbeItem> commandQueue;
++ ///< A list where all attach/detach commands are queued
++ ///< so they can be processed in the MainThreadHook of
++ ///< the dynamite plugin.
++public:
++ static void QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath);
++ ///< Plugins which support cDynamicDeviceProbe must use this function
++ ///< to queue the devices they normally create in their Initialize method.
++ ///< These devices are created as subdevices in the Start-method of the dynamite-plugin.
++ cDynamicDeviceProbe(void);
++ virtual ~cDynamicDeviceProbe();
++ virtual cDevice *Attach(cDevice *ParentDevice, const char *DevPath) = 0;
++ ///< Probes for a device at the given device-path like /dev/dvb/adapter0/frontend0
++ ///< or /dev/video0 etc. and creates the appropriate
++ ///< object derived from cDevice if applicable.
++ ///< Returns the device that has been created or NULL if not.
++ ///< The dynamite-plugin will delete the device if it is detached.
++ };
++
++extern cList<cDynamicDeviceProbe> DynamicDeviceProbes;
++
+ #endif //__DEVICE_H
+diff --git a/dvbci.c b/dvbci.c
+index 5289bbd..fea3a83 100644
+--- a/dvbci.c
++++ b/dvbci.c
+@@ -10,15 +10,18 @@
+ #include "dvbci.h"
+ #include <linux/dvb/ca.h>
+ #include <sys/ioctl.h>
+-#include "device.h"
++#include "dvbdevice.h"
+
+ // --- cDvbCiAdapter ---------------------------------------------------------
+
+-cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
++cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend)
+ {
+ device = Device;
+ SetDescription("CI adapter on device %d", device->DeviceNumber());
+ fd = Fd;
++ adapter = Adapter;
++ frontend = Frontend;
++ idle = false;
+ ca_caps_t Caps;
+ if (ioctl(fd, CA_GET_CAP, &Caps) == 0) {
+ if ((Caps.slot_type & CA_CI_LINK) != 0) {
+@@ -41,10 +44,44 @@ cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
+ cDvbCiAdapter::~cDvbCiAdapter()
+ {
+ Cancel(3);
++ if (device->IsSubDevice() || device->HasSubDevice())
++ CloseCa();
++}
++
++bool cDvbCiAdapter::OpenCa(void)
++{
++ if (fd >= 0)
++ return true;
++ fd = cDvbDevice::DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
++ return (fd >= 0);
++}
++
++void cDvbCiAdapter::CloseCa(void)
++{
++ if (fd < 0)
++ return;
++ close(fd);
++ fd = -1;
++}
++
++bool cDvbCiAdapter::SetIdle(bool Idle, bool TestOnly)
++{
++ if ((adapter < 0) || (frontend < 0))
++ return false;
++ if (TestOnly || (idle == Idle))
++ return true;
++ if (Idle)
++ CloseCa();
++ else
++ OpenCa();
++ idle = Idle;
++ return true;
+ }
+
+ int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
+ {
++ if (idle || (fd < 0))
++ return 0;
+ if (Buffer && MaxLength > 0) {
+ struct pollfd pfd[1];
+ pfd[0].fd = fd;
+@@ -61,6 +98,8 @@ int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
+
+ void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
+ {
++ if (idle || (fd < 0))
++ return;
+ if (Buffer && Length > 0) {
+ if (safe_write(fd, Buffer, Length) != Length)
+ esyslog("ERROR: can't write to CI adapter on device %d: %m", device->DeviceNumber());
+@@ -69,6 +108,8 @@ void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
+
+ bool cDvbCiAdapter::Reset(int Slot)
+ {
++ if (idle || (fd < 0))
++ return false;
+ if (ioctl(fd, CA_RESET, 1 << Slot) != -1)
+ return true;
+ else
+@@ -78,6 +119,8 @@ bool cDvbCiAdapter::Reset(int Slot)
+
+ eModuleStatus cDvbCiAdapter::ModuleStatus(int Slot)
+ {
++ if (idle || (fd < 0))
++ return msNone;
+ ca_slot_info_t sinfo;
+ sinfo.num = Slot;
+ if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) {
+@@ -99,10 +142,10 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query)
+ return true;
+ }
+
+-cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd)
++cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend)
+ {
+ // TODO check whether a CI is actually present?
+ if (Device)
+- return new cDvbCiAdapter(Device, Fd);
++ return new cDvbCiAdapter(Device, Fd, Adapter, Frontend);
+ return NULL;
+ }
+diff --git a/dvbci.h b/dvbci.h
+index adbe40d..6d117b2 100644
+--- a/dvbci.h
++++ b/dvbci.h
+@@ -16,16 +16,24 @@ class cDvbCiAdapter : public cCiAdapter {
+ private:
+ cDevice *device;
+ int fd;
++ int adapter;
++ int frontend;
++ bool idle;
++
++ bool OpenCa(void);
++ void CloseCa(void);
+ protected:
+ virtual int Read(uint8_t *Buffer, int MaxLength);
+ virtual void Write(const uint8_t *Buffer, int Length);
+ virtual bool Reset(int Slot);
+ virtual eModuleStatus ModuleStatus(int Slot);
+ virtual bool Assign(cDevice *Device, bool Query = false);
+- cDvbCiAdapter(cDevice *Device, int Fd);
++ cDvbCiAdapter(cDevice *Device, int Fd, int Adapter = -1, int Frontend = -1);
+ public:
+ virtual ~cDvbCiAdapter();
+- static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd);
++ virtual bool SetIdle(bool Idle, bool TestOnly);
++ virtual bool IsIdle(void) const { return idle; }
++ static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd, int Adapter = -1, int Frontend = -1);
+ };
+
+ #endif //__DVBCI_H
+diff --git a/dvbdevice.c b/dvbdevice.c
+index b0750dd..ca2d3bc 100644
+--- a/dvbdevice.c
++++ b/dvbdevice.c
+@@ -264,6 +264,7 @@ private:
+ uint32_t subsystemId;
+ int tuneTimeout;
+ int lockTimeout;
++ bool lnbSendSignals; // LNB Sharing. false if this device must not send signals to the LNB (like 22 kHz, ...).
+ time_t lastTimeoutReport;
+ fe_delivery_system frontendType;
+ cChannel channel;
+@@ -272,24 +273,37 @@ private:
+ cMutex mutex;
+ cCondVar locked;
+ cCondVar newSet;
+- void ClearEventQueue(void) const;
+- bool GetFrontendStatus(fe_status_t &Status) const;
++ void ClearEventQueue(void);
++ bool GetFrontendStatus(fe_status_t &Status);
+ bool SetFrontend(void);
+ virtual void Action(void);
++
++ cDvbDevice *dvbdevice;
++ bool isIdle;
++ bool OpenFrontend(void);
++ bool CloseFrontend(void);
+ public:
+- cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType);
++ cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType, cDvbDevice *Dvbdevice);
+ virtual ~cDvbTuner();
+ const cChannel *GetTransponder(void) const { return &channel; }
+ uint32_t SubsystemId(void) const { return subsystemId; }
+ bool IsTunedTo(const cChannel *Channel) const;
+ void Set(const cChannel *Channel);
+ bool Locked(int TimeoutMs = 0);
+- int GetSignalStrength(void) const;
+- int GetSignalQuality(void) const;
++ int GetSignalStrength(void);
++ int GetSignalQuality(void);
++
++ bool SetIdle(bool Idle);
++ bool IsIdle(void) const { return isIdle; }
+ };
+
+-cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType)
++cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType, cDvbDevice *Dvbdevice)
+ {
++// LNB Sharing
++ if(Dvbdevice) {
++ lnbSendSignals = Dvbdevice->IsLnbSendSignals();
++ } else lnbSendSignals = true;
++// END LNB Sharing
+ device = Device;
+ fd_frontend = Fd_Frontend;
+ adapter = Adapter;
+@@ -301,8 +315,10 @@ cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_
+ lastTimeoutReport = 0;
+ diseqcCommands = NULL;
+ tunerStatus = tsIdle;
++ dvbdevice = Dvbdevice;
++ isIdle = false;
+ if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
+- CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
++ if(lnbSendSignals) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power // LNB Sharing
+ SetDescription("tuner on frontend %d/%d", adapter, frontend);
+ Start();
+ }
+@@ -313,6 +329,8 @@ cDvbTuner::~cDvbTuner()
+ newSet.Broadcast();
+ locked.Broadcast();
+ Cancel(3);
++ if (dvbdevice && dvbdevice->IsSubDevice())
++ CloseFrontend();
+ }
+
+ bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
+@@ -347,8 +365,10 @@ bool cDvbTuner::Locked(int TimeoutMs)
+ return tunerStatus >= tsLocked;
+ }
+
+-void cDvbTuner::ClearEventQueue(void) const
++void cDvbTuner::ClearEventQueue(void)
+ {
++ if (!OpenFrontend())
++ return;
+ cPoller Poller(fd_frontend);
+ if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
+ dvb_frontend_event Event;
+@@ -357,7 +377,7 @@ void cDvbTuner::ClearEventQueue(void) const
+ }
+ }
+
+-bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
++bool cDvbTuner::GetFrontendStatus(fe_status_t &Status)
+ {
+ ClearEventQueue();
+ while (1) {
+@@ -372,7 +392,7 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
+ //#define DEBUG_SIGNALSTRENGTH
+ //#define DEBUG_SIGNALQUALITY
+
+-int cDvbTuner::GetSignalStrength(void) const
++int cDvbTuner::GetSignalStrength(void)
+ {
+ ClearEventQueue();
+ uint16_t Signal;
+@@ -399,7 +419,7 @@ int cDvbTuner::GetSignalStrength(void) const
+
+ #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
+
+-int cDvbTuner::GetSignalQuality(void) const
++int cDvbTuner::GetSignalQuality(void)
+ {
+ fe_status_t Status;
+ if (GetFrontendStatus(Status)) {
+@@ -484,6 +504,8 @@ static unsigned int FrequencyToHz(unsigned int f)
+
+ bool cDvbTuner::SetFrontend(void)
+ {
++ if (!OpenFrontend())
++ return false;
+ #define MAXFRONTENDCMDS 16
+ #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
+ Frontend[CmdSeq.num].u.data = (d);\
+@@ -511,6 +533,7 @@ bool cDvbTuner::SetFrontend(void)
+ if (Setup.DiSEqC) {
+ const cDiseqc *diseqc = Diseqcs.Get(device, channel.Source(), channel.Frequency(), dtp.Polarization());
+ if (diseqc) {
++ if(lnbSendSignals) { // LNB sharing
+ if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
+ cDiseqc::eDiseqcActions da;
+ for (const char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) {
+@@ -538,6 +561,7 @@ bool cDvbTuner::SetFrontend(void)
+ }
+ diseqcCommands = diseqc->Commands();
+ }
++ } // LNB sharing
+ frequency -= diseqc->Lof();
+ }
+ else {
+@@ -555,9 +579,11 @@ bool cDvbTuner::SetFrontend(void)
+ frequency -= Setup.LnbFrequHi;
+ tone = SEC_TONE_ON;
+ }
+- int volt = (dtp.Polarization() == 'v' || dtp.Polarization() == 'V' || dtp.Polarization() == 'r' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
+- CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
+- CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
++ if(lnbSendSignals) { // LNB sharing
++ int volt = (dtp.Polarization() == 'v' || dtp.Polarization() == 'V' || dtp.Polarization() == 'r' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
++ CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
++ CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
++ } // LNB sharing
+ }
+ frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
+
+@@ -643,9 +669,11 @@ void cDvbTuner::Action(void)
+ bool LostLock = false;
+ fe_status_t Status = (fe_status_t)0;
+ while (Running()) {
+- fe_status_t NewStatus;
+- if (GetFrontendStatus(NewStatus))
+- Status = NewStatus;
++ if (!isIdle) {
++ fe_status_t NewStatus;
++ if (GetFrontendStatus(NewStatus))
++ Status = NewStatus;
++ }
+ cMutexLock MutexLock(&mutex);
+ switch (tunerStatus) {
+ case tsIdle:
+@@ -698,6 +726,45 @@ void cDvbTuner::Action(void)
+ }
+ }
+
++bool cDvbTuner::SetIdle(bool Idle)
++{
++ if (isIdle == Idle)
++ return true;
++ isIdle = Idle;
++ if (Idle)
++ return CloseFrontend();
++ return OpenFrontend();
++}
++
++bool cDvbTuner::OpenFrontend(void)
++{
++ if (fd_frontend >= 0)
++ return true;
++ cMutexLock MutexLock(&mutex);
++ fd_frontend = cDvbDevice::DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
++ if (fd_frontend < 0)
++ return false;
++ if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
++#ifdef LNB_SHARING_VERSION
++ if (lnbSendSignals)
++#endif
++ CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
++ isIdle = false;
++ return true;
++}
++
++bool cDvbTuner::CloseFrontend(void)
++{
++ if (fd_frontend < 0)
++ return true;
++ cMutexLock MutexLock(&mutex);
++ tunerStatus = tsIdle;
++ newSet.Broadcast();
++ close(fd_frontend);
++ fd_frontend = -1;
++ return true;
++}
++
+ // --- cDvbSourceParam -------------------------------------------------------
+
+ class cDvbSourceParam : public cSourceParam {
+@@ -778,7 +845,8 @@ const char *DeliverySystems[] = {
+ NULL
+ };
+
+-cDvbDevice::cDvbDevice(int Adapter, int Frontend)
++cDvbDevice::cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice)
++:cDevice(ParentDevice)
+ {
+ adapter = Adapter;
+ frontend = Frontend;
+@@ -795,7 +863,7 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend)
+
+ fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
+ if (fd_ca >= 0)
+- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, Adapter, Frontend);
+
+ // The DVR device (will be opened and closed as needed):
+
+@@ -835,7 +903,14 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend)
+ else
+ p = (char *)"unknown modulations";
+ isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, DeliverySystems[frontendType], p, frontendInfo.name);
+- dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, frontend, frontendType);
++
++//ML
++ isyslog("LNB-sharing: patch version %s", LNB_SHARING_VERSION);
++ lnbState = -1;
++ SetLnbNrFromSetup();
++ lnbSource = NULL;
++ dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, frontend, frontendType, this);
++//ML-Ende
+ }
+ }
+ else
+@@ -940,6 +1015,31 @@ bool cDvbDevice::Ready(void)
+ return true;
+ }
+
++bool cDvbDevice::SetIdleDevice(bool Idle, bool TestOnly)
++{
++ if (TestOnly) {
++ if (ciAdapter)
++ return ciAdapter->SetIdle(Idle, true);
++ return true;
++ }
++ if (!dvbTuner->SetIdle(Idle))
++ return false;
++ if (ciAdapter && !ciAdapter->SetIdle(Idle, false)) {
++ dvbTuner->SetIdle(!Idle);
++ return false;
++ }
++ if (Idle)
++ StopSectionHandler();
++ else
++ StartSectionHandler();
++ return true;
++}
++
++bool cDvbDevice::CanScanForEPG(void) const
++{
++ return !IsIdle() && !dvbTuner->IsIdle() && ((ciAdapter == NULL) || !ciAdapter->IsIdle());
++}
++
+ bool cDvbDevice::HasCi(void)
+ {
+ return ciAdapter;
+@@ -1051,6 +1151,12 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
+ {
+ bool result = false;
+ bool hasPriority = Priority < 0 || Priority > this->Priority();
++ // LNB Sharing
++ if(hasPriority && GetMaxBadPriority(Channel) >= Priority) hasPriority = false;
++ if (Setup.VerboseLNBlog) {
++ isyslog("LNB %d: ProvidesChannel %d on device %d. Priority is %d, hasPriority is %d", LnbNr(), Channel->Number(), this->DeviceNumber() + 1, Priority, hasPriority);
++ }
++ // LNB Sharing END
+ bool needsDetachReceivers = false;
+
+ if (dvbTuner && ProvidesTransponder(Channel)) {
+@@ -1110,7 +1216,27 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
+ {
+ if (dvbTuner)
+ dvbTuner->Set(Channel);
+- return true;
++ // LNB Sharing
++ cDvbTransponderParameters dtp(Channel->Parameters());
++
++ if (Channel->Frequency() >= Setup.LnbSLOF) {
++ lnbState = 1;
++ } else {
++ lnbState = 0;
++ }
++ if (dtp.Polarization() == 'v' || dtp.Polarization() == 'V') lnbState += 2;
++ lnbSource = (int*) Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization());
++
++ cDevice *tmpDevice;
++ if (tmpDevice = GetBadDevice(Channel)){
++ tmpDevice->DetachAllReceivers();
++ if (tmpDevice->CamSlot() && !tmpDevice->CamSlot()->IsDecrypting())
++ tmpDevice->CamSlot()->Assign(NULL);
++
++ tmpDevice->SwitchChannel(Channel, false);
++ }
++ // LNB Sharing End
++ return true;
+ }
+
+ bool cDvbDevice::HasLock(int TimeoutMs)
+@@ -1151,6 +1277,46 @@ bool cDvbDevice::GetTSPacket(uchar *&Data)
+ return false;
+ }
+
++// LNB - Sharing
++void cDvbDevice::SetLnbNrFromSetup(void)
++{
++ lnbNr = Setup.CardUsesLnbNr[CardIndex()];
++ isyslog("LNB-sharing: setting device %d to use LNB %d", CardIndex() + 1, lnbNr);
++}
++
++bool cDvbDevice::IsShareLnb(const cDevice *Device)
++{
++ return this != Device && this->parentDevice != Device && LnbNr() == Device->LnbNr();
++};
++
++
++bool cDvbDevice::IsLnbConflict(const cChannel *Channel)
++{
++ if(!cSource::IsSat(Channel->Source())) return false; // no conflict if the new channel is not on sat
++ if(!ProvidesSource(cSource::stSat)) return false; // no conflict if this device is not on sat
++//if(MaySwitchTransponder()) return false; // no conflict if this transponder may be switched
++ cDvbTransponderParameters dtp(Channel->Parameters());
++ if (Setup.DiSEqC) {
++ cDiseqc *diseqc;
++ diseqc = Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization() );
++ if (LnbSource() != (int*) diseqc) return true;
++ return false;
++ } else
++ {
++ char requiredState;
++ if (Channel->Frequency() >= Setup.LnbSLOF) {
++ requiredState = 1 ;
++ } else {
++ requiredState = 0;
++ }
++ if (dtp.Polarization() == 'v' || dtp.Polarization() == 'V') requiredState += 2;
++ if(lnbState != requiredState) return true;
++ return false;
++ }
++}
++// LNB - Sharing Ende
++
++
+ // --- cDvbDeviceProbe -------------------------------------------------------
+
+ cList<cDvbDeviceProbe> DvbDeviceProbes;
+diff --git a/dvbdevice.h b/dvbdevice.h
+index 0962548..361dce8 100644
+--- a/dvbdevice.h
++++ b/dvbdevice.h
+@@ -102,7 +102,7 @@ class cDvbTuner;
+ /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
+
+ class cDvbDevice : public cDevice {
+-protected:
++public:
+ static cString DvbName(const char *Name, int Adapter, int Frontend);
+ static int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError = false);
+ private:
+@@ -123,10 +123,29 @@ private:
+ fe_delivery_system frontendType;
+ int fd_dvr, fd_ca;
+ public:
+- cDvbDevice(int Adapter, int Frontend);
++ cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice = NULL);
+ virtual ~cDvbDevice();
+ virtual bool Ready(void);
+
++ virtual bool SetIdleDevice(bool Idle, bool TestOnly);
++ virtual bool CanScanForEPG(void) const;
++
++// LNB Sharing
++private:
++ char lnbState; // Current frequency band and polarization of the DVB-tuner
++ // cDiseqc *lnbSource; // can not #include "diseqc.h". A workaround follows:
++ int *lnbSource; // [DiSEqC] DiSEqC-Source
++ int lnbNr; // Number of LNB used
++
++public:
++ int *LnbSource(void) { return lnbSource; };
++ virtual int LnbNr(void) const { if(ProvidesSource(cSource::stSat)) return lnbNr; return (CardIndex() + 1) * -1;};
++ virtual void SetLnbNrFromSetup(void);
++ virtual bool IsLnbConflict(const cChannel *Channel);
++ virtual bool IsShareLnb(const cDevice *Device);
++// LNB Sharing Ende
++
++
+ // Common Interface facilities:
+
+ private:
+diff --git a/eitscan.c b/eitscan.c
+index 25b7115..c679eda 100644
+--- a/eitscan.c
++++ b/eitscan.c
+@@ -143,7 +143,7 @@ void cEITScanner::Process(void)
+ bool AnyDeviceSwitched = false;
+ for (int i = 0; i < cDevice::NumDevices(); i++) {
+ cDevice *Device = cDevice::GetDevice(i);
+- if (Device) {
++ if (Device && Device->CanScanForEPG()) {
+ for (cScanData *ScanData = scanList->First(); ScanData; ScanData = scanList->Next(ScanData)) {
+ const cChannel *Channel = ScanData->GetChannel();
+ if (Channel) {
+@@ -151,9 +151,11 @@ void cEITScanner::Process(void)
+ if (Device->ProvidesTransponder(Channel)) {
+ if (!Device->Receiving()) {
+ bool MaySwitchTransponder = Device->MaySwitchTransponder();
+- if (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel) && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++//ML
++ if (MaySwitchTransponder && Device->GetMaxBadPriority(Channel) == -2 || (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel)) && Device->GetMaxBadPriority(Channel) <= -1 && now - lastActivity > Setup.EPGScanTimeout * 3600) {
+ if (!MaySwitchTransponder) {
+- if (Device == cDevice::ActualDevice() && !currentChannel) {
++ if ((Device == cDevice::ActualDevice() || Device->GetMaxBadPriority(Channel) == -1) && !currentChannel) {
++//ML-Ende
+ cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
+ currentChannel = Device->CurrentChannel();
+ Skins.Message(mtInfo, tr("Starting EPG scan"));
+diff --git a/menu.c b/menu.c
+index 3978514..51ea473 100644
+--- a/menu.c
++++ b/menu.c
+@@ -2892,6 +2892,23 @@ void cMenuSetupLNB::Setup(void)
+
+ Clear();
+
++//ML
++ int numSatDevices = 0;
++ for (int i = 0; i < cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat)) numSatDevices++;
++ }
++ if (numSatDevices > 1) {
++ char tmp[40];
++ for (int i = 1; i <= cDevice::NumDevices(); i++) {
++ if (cDevice::GetDevice(i - 1)->ProvidesSource(cSource::stSat)) {
++ snprintf( tmp, 40, tr("Setup.LNB$DVB device %d uses LNB No."), i);
++ Add(new cMenuEditIntItem( tmp, &data.CardUsesLnbNr[i - 1], 1, numSatDevices ));
++ }
++ }
++ }
++ Add(new cMenuEditBoolItem(tr("Setup.LNB$Log LNB usage"), &data.VerboseLNBlog));
++//ML-Ende
++
+ Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"), &data.DiSEqC));
+ if (!data.DiSEqC) {
+ Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"), &data.LnbSLOF));
+@@ -2908,6 +2925,10 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
+ int oldDiSEqC = data.DiSEqC;
+ eOSState state = cMenuSetupBase::ProcessKey(Key);
+
++//ML
++ if (Key == kOk) cDevice::SetLnbNr();
++//ML-Ende
++
+ if (Key != kNone && data.DiSEqC != oldDiSEqC)
+ Setup();
+ return state;
+diff --git a/vdr.c b/vdr.c
+index c32e45f..f590ccb 100644
+--- a/vdr.c
++++ b/vdr.c
+@@ -900,26 +900,29 @@ int main(int argc, char *argv[])
+ break;
+ }
+ bool timeout = Now - DeviceUsed[d->DeviceNumber()] > TIMERDEVICETIMEOUT; // only check other devices if they have been left alone for a while
+- if (d->MaySwitchTransponder()) {
++ if (d->MaySwitchTransponder() && d->GetMaxBadPriority(Timer->Channel()) == -2 ) { // LNB Sharing
+ DeviceAvailable = true; // avoids using the actual device below
+ if (timeout)
+ Device = d; // only check other devices if they have been left alone for a while
+ }
+ else if (timeout && !Device && InVpsMargin && !d->Receiving() && d->ProvidesTransponderExclusively(Timer->Channel()))
+- Device = d; // use this one only if no other with less impact can be found
+- }
++ // Device = d; // use this one only if no other with less impact can be found
++ if( d->GetMaxBadPriority(Timer->Channel()) <= -1) Device = d; // LNB Sharing
++ }
+ }
+ if (!Device && InVpsMargin && !DeviceAvailable) {
+ cDevice *d = cDevice::ActualDevice();
+ if (!d->Receiving() && d->ProvidesTransponder(Timer->Channel()) && Now - DeviceUsed[d->DeviceNumber()] > TIMERDEVICETIMEOUT)
+- Device = d; // use the actual device as a last resort
++ // Device = d; // use the actual device as a last resort
++ if( d->GetMaxBadPriority(Timer->Channel()) <= -1) Device = d; // LNB Sharing
++
+ }
+ // Switch the device to the transponder:
+ if (Device) {
+ if (!Device->IsTunedToTransponder(Timer->Channel())) {
+ if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
+ cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
+- dsyslog("switching device %d to channel %d", Device->DeviceNumber() + 1, Timer->Channel()->Number());
++ dsyslog("VDR: switching device %d to channel %d", Device->DeviceNumber() + 1, Timer->Channel()->Number());
+ Device->SwitchChannel(Timer->Channel(), false);
+ DeviceUsed[Device->DeviceNumber()] = Now;
+ }