From a13c75ec46f2783b2d258af48d7de28d44666715 Mon Sep 17 00:00:00 2001
From: Stefan Saraev <stefan@saraev.ca>
Date: Sat, 5 May 2012 21:50:48 +0300
Subject: [PATCH 2/2] dynamite

---
 ci.c        |    2 +
 ci.h        |    2 +
 device.c    |  121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 device.h    |   85 ++++++++++++++++++++++++++++++++++++-----
 dvbci.c     |   51 +++++++++++++++++++++++--
 dvbci.h     |   12 +++++-
 dvbdevice.c |   95 ++++++++++++++++++++++++++++++++++++++++++----
 dvbdevice.h |    6 ++-
 8 files changed, 339 insertions(+), 35 deletions(-)

diff --git a/ci.c b/ci.c
index 0135d07..cf21952 100644
--- a/ci.c
+++ b/ci.c
@@ -1571,6 +1571,8 @@ cCamSlot::cCamSlot(cCiAdapter *CiAdapter)
 
 cCamSlot::~cCamSlot()
 {
+  if (ciAdapter && ciAdapter->assignedDevice)
+     ciAdapter->assignedDevice->SetCamSlot(NULL);
   CamSlots.Del(this, false);
   DeleteAllConnections();
 }
diff --git a/ci.h b/ci.h
index 74e0270..d38e2dd 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/device.c b/device.c
index 273757e..f83e789 100644
--- a/device.c
+++ b/device.c
@@ -69,12 +69,22 @@ int cDevice::currentChannel = 1;
 cDevice *cDevice::device[MAXDEVICES] = { NULL };
 cDevice *cDevice::primaryDevice = 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);
 
@@ -105,10 +115,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()
@@ -117,6 +131,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)
@@ -155,6 +192,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;
@@ -349,6 +388,8 @@ bool cDevice::HasCi(void)
 
 void cDevice::SetCamSlot(cCamSlot *CamSlot)
 {
+  if (parentDevice)
+     return parentDevice->SetCamSlot(CamSlot);
   camSlot = CamSlot;
 }
 
@@ -561,6 +602,10 @@ void cDevice::DelLivePids(void)
 
 void cDevice::StartSectionHandler(void)
 {
+  if (parentDevice) {
+     parentDevice->StartSectionHandler();
+     return;
+     }
   if (!sectionHandler) {
      sectionHandler = new cSectionHandler(this);
      AttachFilter(eitFilter = new cEitFilter);
@@ -572,6 +617,10 @@ void cDevice::StartSectionHandler(void)
 
 void cDevice::StopSectionHandler(void)
 {
+  if (parentDevice) {
+     parentDevice->StopSectionHandler();
+     return;
+     }
   if (sectionHandler) {
      delete nitFilter;
      delete sdtFilter;
@@ -598,12 +647,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);
 }
@@ -765,6 +819,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)
@@ -811,19 +866,27 @@ 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
+        }
      }
 }
 
 int cDevice::Occupied(void) const
 {
+  if (parentDevice)
+     return parentDevice->Occupied();
   int Seconds = occupiedTimeout - time(NULL);
   return Seconds > 0 ? Seconds : 0;
 }
 
 void cDevice::SetOccupied(int Seconds)
 {
+  if (parentDevice) {
+     parentDevice->SetOccupied(Seconds);
+     return;
+     }
   if (Seconds >= 0)
      occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT);
 }
@@ -1196,7 +1259,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);
@@ -1215,6 +1281,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()
@@ -1234,6 +1302,8 @@ void cDevice::Detach(cPlayer *Player)
 
 void cDevice::StopReplay(void)
 {
+  if (parentDevice)
+     return parentDevice->StopReplay();
   if (player) {
      Detach(player);
      if (IsPrimaryDevice())
@@ -1516,6 +1586,8 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
 
 int cDevice::Priority(void) const
 {
+  if (parentDevice)
+     return parentDevice->Priority();
   int priority = IDLEPRIORITY;
   if (IsPrimaryDevice() && !Replaying() && ActualDevice() == PrimaryDevice())
      priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing
@@ -1534,6 +1606,8 @@ bool cDevice::Ready(void)
 
 bool cDevice::Receiving(bool Dummy) const
 {
+  if (parentDevice)
+     return parentDevice->Receiving(Dummy);
   cMutexLock MutexLock(&mutexReceiver);
   for (int i = 0; i < MAXRECEIVERS; i++) {
       if (receiver[i])
@@ -1614,10 +1688,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
@@ -1656,6 +1733,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;
@@ -1681,6 +1760,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++) {
@@ -1693,6 +1774,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]);
@@ -1764,3 +1847,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 dc2ea5f..e5e92a3 100644
--- a/device.h
+++ b/device.h
@@ -171,7 +171,6 @@ 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
@@ -198,9 +197,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 cString DeviceName(void) const;
@@ -416,9 +412,6 @@ public:
          ///< shall check whether the channel can be decrypted.
   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
 
@@ -574,9 +567,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);
@@ -761,6 +751,38 @@ public:
        ///< Detaches all receivers from this device for this pid.
   virtual 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
+  // --- dynamite subdevice patch end ---
   };
 
 /// Derived cDevice classes that can receive channels will have to provide
@@ -784,4 +806,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 53a4a22..7fc12a4 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -287,7 +287,7 @@ private:
   enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
   bool SendDiseqc;
   const cDvbDevice *device;
-  int fd_frontend;
+  mutable int fd_frontend;
   int adapter, frontend;
   uint32_t subsystemId;
   int tuneTimeout;
@@ -298,7 +298,7 @@ private:
   const cScr *scr;
   bool lnbPowerTurnedOn;
   eTunerStatus tunerStatus;
-  cMutex mutex;
+  mutable cMutex mutex;
   cCondVar locked;
   cCondVar newSet;
   dvb_diseqc_master_cmd diseqc_cmd;
@@ -313,6 +313,10 @@ private:
   void ResetToneAndVoltage(void);
   bool SetFrontend(void);
   virtual void Action(void);
+
+  mutable bool isIdle;
+  bool OpenFrontend(void) const;
+  bool CloseFrontend(void);
 public:
   cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend);
   virtual ~cDvbTuner();
@@ -324,6 +328,8 @@ public:
   uint32_t SubsystemId(void) const { return subsystemId; }
   bool IsTunedTo(const cChannel *Channel) const;
   void SetChannel(const cChannel *Channel);
+  bool SetIdle(bool Idle);
+  bool IsIdle(void) const { return isIdle; }
   bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
   bool Locked(int TimeoutMs = 0);
   int GetSignalStrength(void) const;
@@ -350,6 +356,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int
   bondedTuner = NULL;
   bondedMaster = false;
   bondedMasterFailed = false;
+  isIdle = false;
   SetDescription("tuner on frontend %d/%d", adapter, frontend);
   Start();
 }
@@ -367,6 +374,8 @@ cDvbTuner::~cDvbTuner()
      ExecuteDiseqc(lastDiseqc, &Frequency);
      }
   */
+  if (device && device->IsSubDevice())
+     CloseFrontend();
 }
 
 bool cDvbTuner::Bond(cDvbTuner *Tuner)
@@ -524,6 +533,8 @@ bool cDvbTuner::Locked(int TimeoutMs)
 
 void cDvbTuner::ClearEventQueue(void) const
 {
+  if (!OpenFrontend())
+     return;
   cPoller Poller(fd_frontend);
   if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
      dvb_frontend_event Event;
@@ -714,6 +725,8 @@ static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTranspon
 
 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);\
@@ -859,9 +872,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);
         int WaitTime = 1000;
         if (SendDiseqc) {
@@ -935,6 +950,40 @@ bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
   return true;
 }
 
+bool cDvbTuner::SetIdle(bool Idle)
+{
+  if (isIdle == Idle)
+     return true;
+  isIdle = Idle;
+  if (Idle)
+     return CloseFrontend();
+  return OpenFrontend();
+}
+
+bool cDvbTuner::OpenFrontend(void) const
+{
+  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;
+  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 {
@@ -1020,7 +1069,8 @@ const char *DeliverySystemNames[] = {
   NULL
   };
 
-cDvbDevice::cDvbDevice(int Adapter, int Frontend)
+cDvbDevice::cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice)
+:cDevice(ParentDevice)
 {
   adapter = Adapter;
   frontend = Frontend;
@@ -1040,7 +1090,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):
 
@@ -1257,7 +1307,11 @@ bool cDvbDevice::BondDevices(const char *Bondings)
          if (d >= 0) {
             int ErrorDevice = 0;
             if (cDevice *Device1 = cDevice::GetDevice(i)) {
+               if (Device1->HasSubDevice())
+                  Device1 = Device1->SubDevice();
                if (cDevice *Device2 = cDevice::GetDevice(d)) {
+                  if (Device2->HasSubDevice())
+                     Device2 = Device2->SubDevice();
                   if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
                      if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
                         if (!DvbDevice1->Bond(DvbDevice2))
@@ -1291,7 +1345,10 @@ bool cDvbDevice::BondDevices(const char *Bondings)
 void cDvbDevice::UnBondDevices(void)
 {
   for (int i = 0; i < cDevice::NumDevices(); i++) {
-      if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i)))
+      cDevice *dev = cDevice::GetDevice(i);
+      if (dev && dev->HasSubDevice())
+         dev = dev->SubDevice();
+      if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(dev))
          d->UnBond();
       }
 }
@@ -1345,6 +1402,26 @@ bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const
   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::HasCi(void)
 {
   return ciAdapter;
@@ -1511,7 +1588,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
 
 bool cDvbDevice::ProvidesEIT(void) const
 {
-  return dvbTuner != NULL;
+  return !IsIdle() && (dvbTuner != NULL) && !dvbTuner->IsIdle() && ((ciAdapter == NULL) || !ciAdapter->IsIdle());
 }
 
 int cDvbDevice::NumProvidedSystems(void) const
diff --git a/dvbdevice.h b/dvbdevice.h
index 4ffcb91..e9b7cd6 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -107,7 +107,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:
@@ -133,12 +133,14 @@ private:
   mutable bool needsDetachBondedReceivers;
   bool QueryDeliverySystems(int fd_frontend);
 public:
-  cDvbDevice(int Adapter, int Frontend);
+  cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice = NULL);
   virtual ~cDvbDevice();
   int Adapter(void) const { return adapter; }
   int Frontend(void) const { return frontend; }
   virtual bool Ready(void);
   virtual cString DeviceName(void) const;
+  virtual bool SetIdleDevice(bool Idle, bool TestOnly);
+
   static bool BondDevices(const char *Bondings);
        ///< Bonds the devices as defined in the given Bondings string.
        ///< A bonding is a sequence of device numbers (starting at 1),
-- 
1.7.2.5

