Hi,

Some friends asked me to modify vdr/write a plugin, because their boxes boot so fast that most of the hardware is not initialized by the time vdr can start. They had to delay it or had to restart vdr when they think the hardware is ready. But since some pci-cards are 'fast enough' they would like to attach the 'slow' cards later while using the 'fast' cards for watching, recording etc. as soon as possible. So 'dynamite' was born (just some kind of wordplay with 'dynamic' and it sounds more 'bombastic' and effective in advertising than 'dyndev' or whatever). :-)


It's clear that this can't be done without patching the device handling inside vdr, so attached is my patch against vdr 1.7.17. It is designed in such a way that the original behaviour of vdr is not changed if the dynamite plugin is not loaded. I know that it is pretty invasive but it is as small as I could to do it. My knowledge of the inner relations of the various classes is certainly limited but I spent hours and hours on reading the source. And it is tested by a brave crew of volunteers. Nonetheless I would like to hear some remarks from the vdr-experts out there.

In the following lines I will try to explain what this patch-plugin-combination is able to do and how it's done. It's a bit lengthy but it's important to me and I know it wouldn't get integrated at all if I don't give a wider overview about the concepts and ideas behind this.

Features:
- attach/detach devices
While vdr is running you can dynamically attach/detach (unused) receiving 
devices to/from it.
This could be a DVB-USB-stick or a very slow initialising piece of hardware 
(firmware load etc.).

New devices are detected via udev, detaching should be made manually. If a frontend is in use udev is not sending a corresponding signal, there are just some messages from the usb subsystem, for example. The mapping to the right frontend and detaching it automatically is not trivial (in other words: it's on the TODO list).
Also OSD integration is completely missing but will be added in the future.

For attaching/detaching there are SVDRP commands (and Service calls for other 
plugins):
PLUG dynamite ATTD /dev/dvb/adapter0/frontend0
PLUG dynamite DETD /dev/dvb/adapter0/frontend0

For a complete list of commands please read the README[1].

- set device on idle
You can set a device to 'idle' mode. It will be ignored by the EIT scanner and 
closes all its handles.
This is handy if you have enough tuners and want so save some power or preserve some frontends from being 'always on'. This is also a 'manual' feature, some kind of automatism is planned (some kind of timeout or similar).
Of course it will be 'reanimated' automatically if a recording is starting or 
someone wants to look live-tv.

- GetTSPacket timeout
It is possible to set a 'GetTSPacket' timeout on the devices. If a device delivers no data for some time it can be automatically detached. This is intended for devices which has known unstable drivers. After detaching such a device a script is started so the driver can be reloaded and the device can be attached again to the vdr (this could happen on its own if the created frontend is signaled by udev).
It avoids restarts of vdr and interrupting recordings on other devices.

How this all is achieved:
The class cDevice is extended by two fields: parentDevice and subDevice.
The dynamite plugin provides a class derived from cDevice which passes all calls to its methods to its subDevice if one is attached. Otherwise it returns the appropriate values so vdr does think it can't receive anything (like the dummydevice output-plugin which can play everything). On the other side there are some methods in cDevice which have to be "parentDevice-aware", since they are called from other derived devices or classes inside vdr. Since parentDevice (and subDevice) are always NULL if dynamite isn't loaded, it's easy to preserve the original behaviour.

To get the cDynamicDevice between the vdr and the device-creating plugins 
dynamite is doing two things:
- it registers a cDvbDeviceProbe-object and moves it to the front of the list
- the patch adds a second list 'cDynamicDeviceProbe' for 'dynamite-aware' 
plugins (like pvrinput as an example)

On startup when vdr iterates through the dvb-devices dynamite captures all devices and adds a cDynamicDevice for every one to the global vdr-device-array. It asks all other cDvbDeviceProbes if they would like to create a device and attaches this as a subDevice. If no cDvbDeviceProbe is left it creates a core cDvbDevice on its own. After this it creates enough cDynamicDevice objects to flood the vdr-device-array. This ensures that vdr and other plugins always communicate with the devices through dynamite (like CAM, CI etc.) and so there is enough room for devices which will be attached later. If you use plugins which create (non-dvb-)devices which can't be handled by dynamite (like xine, iptv, streamdev etc.) they have to be loaded *before* dynamite. Actually it's best to load dynamite as the *last* plugin because then it can ensure that its cDvbDeviceProbe will be the first one. The dirtiest part of the patch is the modification of the constructor of cDevice. If the new 'parentDevice' parameter would be made non-optional, I don't have to use this ugly global placeholder... If it really gets integrated in vdr (I don't abandon hope on this), that would definitly gets cleaned up. But that would force changes on many plugins so I chose 'the dirty way'.

Plugins which would like to use the functionallity of dynamite had to alter 
their device detection in their startup phase:
- if dynamite is loaded just register a cDynamicDeviceProbe object and use the helper function QueueDynamicDeviceCommand from cDynamicDeviceProbe to queue the strings it needs to create the device objects.
- if dynamite is not present create the devices like before
- make sure you close all handles on destruction at the latest

Look at pvrinput[3] as an example (cPvrDevice::Initialize at device.c:269 and 
at the end of that file)
pvrinput uses strings like '/dev/video0' to identify the device it has to create. But these 'attach'-strings doesn't necessarily need to be real device paths. It's just a parameter to the Attach method of your cDynamicDeviceProbe object - so your fantasy limits the possibilities. :-) In the dynamite source you'll find a cDynamiteDeviceProbe which reacts on strings like 'dummydevice0' or 'dummydevice1'. It helped me while developing the SVDRP commands since I had only one DVB-T stick at my dev-vdr. You can activate it with a commandline argument.

There are some caveats if you would like to use other patches together with dynamite if they also extend cDevice. Every new method has to be investigated if it should return its own value or the one of the parentDevice or subDevice (if it's present). And every new virtual method has to be added to cDynamicDevice at the dynamite plugin. If I had 'the power' I would integrate the functionality of cDynamicDevice completly into cDevice and leave the controlling at the plugin - but I'm realistic enough to know, that that would need a lot of convincing for my part... :-) As an example I added a reworked version of the lnb-sharing-patch to my repository[2]. Also I include some methods added by the patches yaVDR uses since this is my preferred distribution.

So, anything left to say? It took me a while to write all this stuff down so for now I don't know if I had forgotten something important or not. I'm looking forward for comments.

Of course I'm never responsible for broken or missed recordings (as no one is)! 
:-)

Thanks for reading!

Regards,
Lars.
--
P.S.: If you have a Sundtek stick and are interested in dynamite, please check out the sundtek-plugin[4]. It monitors devices via the libmedia/mediasrv and can even react on unplugging a device which is in use.
--
[1] 
http://projects.vdr-developer.org/projects/plg-dynamite/repository/revisions/master/entry/README
[2] git://projects.vdr-developer.org/vdr-plugin-dynamite.git
[3] http://projects.vdr-developer.org/projects/plg-pvrinput
[4] https://github.com/flensrocker/vdr-plugin-sundtek
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/device.c b/device.c
index 60340c0..ba2a014 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)
@@ -158,6 +195,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;
@@ -328,6 +367,8 @@ bool cDevice::HasCi(void)
 
 void cDevice::SetCamSlot(cCamSlot *CamSlot)
 {
+  if (parentDevice)
+     return parentDevice->SetCamSlot(CamSlot);
   camSlot = CamSlot;
 }
 
@@ -531,6 +572,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 +587,10 @@ void cDevice::StartSectionHandler(void)
 
 void cDevice::StopSectionHandler(void)
 {
+  if (parentDevice) {
+     parentDevice->StopSectionHandler();
+     return;
+     }
   if (sectionHandler) {
      delete nitFilter;
      delete sdtFilter;
@@ -568,12 +617,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);
 }
@@ -720,6 +774,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)
@@ -766,8 +821,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
+        }
      }
 }
 
@@ -1138,7 +1195,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);
@@ -1157,6 +1217,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()
@@ -1176,6 +1238,8 @@ void cDevice::Detach(cPlayer *Player)
 
 void cDevice::StopReplay(void)
 {
+  if (parentDevice)
+     return parentDevice->StopReplay();
   if (player) {
      Detach(player);
      if (IsPrimaryDevice())
@@ -1458,6 +1522,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])
@@ -1473,6 +1539,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;
@@ -1552,10 +1620,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
@@ -1594,6 +1665,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;
@@ -1619,6 +1692,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++) {
@@ -1631,6 +1706,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]);
@@ -1702,3 +1779,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 1598af2..7340392 100644
--- a/device.h
+++ b/device.h
@@ -163,7 +163,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
@@ -190,9 +189,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;
@@ -365,9 +361,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
 
@@ -524,9 +517,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);
@@ -712,6 +702,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
@@ -735,4 +758,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 f32b350..755d9e0 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -259,6 +259,7 @@ private:
   int device;
   int fd_frontend;
   int adapter, frontend;
+  cDvbDevice *dvbdevice;
   int tuneTimeout;
   int lockTimeout;
   time_t lastTimeoutReport;
@@ -269,30 +270,37 @@ private:
   cMutex mutex;
   cCondVar locked;
   cCondVar newSet;
+  bool isIdle;
+  bool OpenFrontend(void);
+  bool CloseFrontend(void);
   bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
   bool SetFrontend(void);
   virtual void Action(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; }
   bool IsTunedTo(const cChannel *Channel) const;
   void Set(const cChannel *Channel);
   bool Locked(int TimeoutMs = 0);
+  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)
 {
   device = Device;
   fd_frontend = Fd_Frontend;
   adapter = Adapter;
   frontend = Frontend;
   frontendType = FrontendType;
+  dvbdevice = Dvbdevice;
   tuneTimeout = 0;
   lockTimeout = 0;
   lastTimeoutReport = 0;
   diseqcCommands = NULL;
   tunerStatus = tsIdle;
+  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
   SetDescription("tuner on frontend %d/%d", adapter, frontend);
@@ -305,6 +313,8 @@ cDvbTuner::~cDvbTuner()
   newSet.Broadcast();
   locked.Broadcast();
   Cancel(3);
+  if (dvbdevice && dvbdevice->IsSubDevice())
+     CloseFrontend();
 }
 
 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
@@ -339,8 +349,49 @@ bool cDvbTuner::Locked(int TimeoutMs)
   return tunerStatus >= tsLocked;
 }
 
+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;
+}
+
 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
 {
+  if (!OpenFrontend())
+     return false;
   if (TimeoutMs) {
      cPoller Poller(fd_frontend);
      if (Poller.Poll(TimeoutMs)) {
@@ -367,6 +418,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);\
@@ -526,9 +579,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, 10))
-           Status = NewStatus;
+        if (!isIdle) {
+           fe_status_t NewStatus;
+           if (GetFrontendStatus(NewStatus, 10))
+              Status = NewStatus;
+           }
         cMutexLock MutexLock(&mutex);
         switch (tunerStatus) {
           case tsIdle:
@@ -661,7 +716,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;
@@ -678,7 +734,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):
 
@@ -718,7 +774,7 @@ 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);
+        dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, 
frontend, frontendType, this);
         }
      }
   else
@@ -823,6 +879,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;
diff --git a/dvbdevice.h b/dvbdevice.h
index ff606fd..7ca1a87 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,13 @@ 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;
+
 // Common Interface facilities:
 
 private:
diff --git a/eitscan.c b/eitscan.c
index 2b43e71..ff5e3e4 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) {
_______________________________________________
vdr mailing list
vdr@linuxtv.org
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

Reply via email to