[ANNOUNCE] dynamite plugin

Message ID 4D8A6180.4040804@flensrocker.de
State New
Headers

Commit Message

L. Hanisch March 23, 2011, 9:09 p.m. UTC
  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
  

Comments

syrius.ml@no-log.org March 24, 2011, 12:57 a.m. UTC | #1
"L. Hanisch" <dvb@flensrocker.de> writes:

> Features:
> - attach/detach devices
[...]
> - set device on idle
[...]
> - 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.

Thanks for that !
I'll be trying your patch this week end !
This is probably one of my most wanted vdr feature and it sounds like
you've just implemented it :)

questions :
- have you considered changing the way vdr detects adapter on startup ?
  for instance vdr won't detect /dev/dvb/adapter1 if
  /dev/dvb/adapter0 does not exist. this might happen when using udev
  rules and restarting vdr while a module isn't loaded

- what about being able to use aliases like /dev/dvb/dvb_s2_card_one
  with the -D arg ? (tho it's not really important)

- what about a github tree of vdr with your patch already applied ?

--
  
Gerald Dachs March 24, 2011, 7:16 a.m. UTC | #2
> "L. Hanisch" <dvb@flensrocker.de> writes:
> questions :
> - have you considered changing the way vdr detects adapter on startup ?
>   for instance vdr won't detect /dev/dvb/adapter1 if
>   /dev/dvb/adapter0 does not exist. this might happen when using udev
>   rules and restarting vdr while a module isn't loaded

Not sure what you mean. There is no reason anymore to restart vdr, even not
after resume from suspend2ram. Dynamite is signaled by udev that a new
adapter is detected. So it is not important whether the vdr sees an
adapter, or not.

> - what about a github tree of vdr with your patch already applied ?

I know it is not what you asked for, but the vdr 1.7.17 in the
unstable-vdr PPA from yaVDR uses the plugin already.

Gerald
  
L. Hanisch March 24, 2011, 7:58 a.m. UTC | #3
Hi,

Am 24.03.2011 01:57, schrieb syrius.ml@no-log.org:
> "L. Hanisch"<dvb@flensrocker.de>  writes:
>
>> Features:
>> - attach/detach devices
> [...]
>> - set device on idle
> [...]
>> - 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.
>
> Thanks for that !
> I'll be trying your patch this week end !
> This is probably one of my most wanted vdr feature and it sounds like
> you've just implemented it :)

  Cool, nice to hear! :)

> questions :
> - have you considered changing the way vdr detects adapter on startup ?
>    for instance vdr won't detect /dev/dvb/adapter1 if
>    /dev/dvb/adapter0 does not exist. this might happen when using udev
>    rules and restarting vdr while a module isn't loaded

  If all devices are loaded after the start of vdr every device will be attached regardless of its adapter nr. But if it 
gets initialized before the startup it might get overseen (which might be intended by some users). I'll think about some 
kind of udev-device-enumration at startup.
  BTW the -D argument of vdr is ignored for now - have to implement that as well.

>
> - what about being able to use aliases like /dev/dvb/dvb_s2_card_one
>    with the -D arg ? (tho it's not really important)
>
> - what about a github tree of vdr with your patch already applied ?

  Maybe - shouldn't be too bad. :)

Thanks,
Lars.
  
Mika Laitio March 24, 2011, 9:38 a.m. UTC | #4
>> - what about being able to use aliases like /dev/dvb/dvb_s2_card_one
>>    with the -D arg ? (tho it's not really important)
>>
>> - what about a github tree of vdr with your patch already applied ?
>
>  Maybe - shouldn't be too bad. :)

Maybe just adding a patch branch to 
http://projects.vdr-developer.org/git/?p=vdr.git;a=summary ?
It could also contain some other known patches for 17.7, like the 
vdr-1.7.17-updatemarks-2.diff or vdr-1.7.17-updatemarks-3.diff...

Mika
  
Holger Schvestka March 24, 2011, 9:41 a.m. UTC | #5
Am Donnerstag, den 24.03.2011, 11:38 +0200 schrieb Mika Laitio:
> >> - what about being able to use aliases like /dev/dvb/dvb_s2_card_one
> >>    with the -D arg ? (tho it's not really important)
> >>
> >> - what about a github tree of vdr with your patch already applied ?
> >
> >  Maybe - shouldn't be too bad. :)
> 
> Maybe just adding a patch branch to 
> http://projects.vdr-developer.org/git/?p=vdr.git;a=summary ?
> It could also contain some other known patches for 17.7, like the 
> vdr-1.7.17-updatemarks-2.diff or vdr-1.7.17-updatemarks-3.diff...
> 

maybe kls adopts all this patches in vdr-1.7.18 ?


> Mika
> 
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
  
Dieter Hametner March 24, 2011, 9:03 p.m. UTC | #6
Hi

Am Donnerstag, 24. März 2011 schrieb Holger Schvestka:
> Am Donnerstag, den 24.03.2011, 11:38 +0200 schrieb Mika Laitio:
> > [...]
> > Maybe just adding a patch branch to
> > http://projects.vdr-developer.org/git/?p=vdr.git;a=summary ?
> > It could also contain some other known patches for 17.7, like the
> > vdr-1.7.17-updatemarks-2.diff or vdr-1.7.17-updatemarks-3.diff...
> 
> maybe kls adopts all this patches in vdr-1.7.18 ?

Even if kls does not adopt this patch that fast, I will not add it to the 
above git repository. That GIT is maintained by me and it will contain only 
releases provided by kls. That is and will remain its intended purpose.

Of course one of the intentions of that GIT is it to be cloned :)
Host the clone on http://projects.vdr-developer.org and and apply the patch 
you mentioned yourself.

The site projects.vdr-developer.org is open for everybody who want's to host a 
vdr related project ;)


Regards
Tadi
  
L. Hanisch March 25, 2011, 12:47 a.m. UTC | #7
Hi,

Am 24.03.2011 01:57, schrieb syrius.ml@no-log.org:
> questions :
> - have you considered changing the way vdr detects adapter on startup ?
>    for instance vdr won't detect /dev/dvb/adapter1 if
>    /dev/dvb/adapter0 does not exist. this might happen when using udev
>    rules and restarting vdr while a module isn't loaded

dynamite 0.0.6 now uses udev device enumeration to detect all dvb frontends regardless of their numbering.
If you would like to keep an adapter from being used by vdr you can set an udev environment property on that device.

The following would ensure that /dev/dvb/adapter2/frontend* is not attached:

  ACTION=="add", SUBSYSTEM=="dvb", ENV{DVB_ADAPTER_NUM}=="2", ENV{dynamite_attach}="no"

This acts as an replacement for the "-D" option of the core vdr. Since the card index of dynamically attached devices 
are not necessarily related to their adapter number it wouldn't work as expected any more.
(the -D option is only used in the vdr internal dvb device enumeration - if you add a device afterwards it would get 
attached even if it gets a "forbidden" adapter nr)


If you run multiple instances of vdr with different ids (option -i) you can associate the devices to the different vdrs 
with the property "dynamite_instanceid".

This attaches /dev/dvb/adapter2/frontend* to "vdr -i 1" and all other adapter to "vdr -i 0":

ACTION=="add", SUBSYSTEM=="dvb", ENV{DVB_ADAPTER_NUM}!="2", ENV{dynamite_instanceid}="0"
ACTION=="add", SUBSYSTEM=="dvb", ENV{DVB_ADAPTER_NUM}=="2", ENV{dynamite_instanceid}="1"

Regards,
Lars.
  
syrius.ml@no-log.org March 28, 2011, 10:24 a.m. UTC | #8
"L. Hanisch" <dvb@flensrocker.de> writes:

>> - have you considered changing the way vdr detects adapter on startup ?
>>    for instance vdr won't detect /dev/dvb/adapter1 if
>>    /dev/dvb/adapter0 does not exist. this might happen when using udev
>>    rules and restarting vdr while a module isn't loaded
>
> dynamite 0.0.6 now uses udev device enumeration to detect all dvb frontends regardless of their numbering.
> If you would like to keep an adapter from being used by vdr you can set an udev environment property on that device.

Hi !

Thanks a lot !
I'm using 0.0.6b and the new udev rules.
My setup is :
- first vdr (the one in production that has to be working, especially
  when i'm not @home)
  genuine vdr 1.7.17, 2 dvb-s cards, diseqc

- 2nd vdr
  dynamite vdr 1.7.17, 1 dvb-s ff card which keeps crashing + 1 dvb-t
  usb stick

It is working perfectly, i haven't tried to remove/add adapter yet.
I will do later today.

Just another question:
When i decide to only use one vdr instance, i'll be needing the diseqc
trick in diseqc.conf (the one that replaces the sourcecaps patch), how
do you think it could work ?
I guess the quickest workaround would be to add a new udev param to
force the card index in vdr.

I've been having issues with udev rules to order dvb devices in the
past, instead i'm using a simple simple script that gives vdr the
correct card number.
ex:
vdr $(get-dvb-device.sh main)
"get-dvb-devices.sh main" returns "-D 1 -D 4" for example. that way
i'm sure my main vdr always uses my two stable dvb-s cards

--
  
L. Hanisch March 28, 2011, 5:36 p.m. UTC | #9
Hi,

Am 28.03.2011 12:24, schrieb syrius.ml@no-log.org:
> "L. Hanisch"<dvb@flensrocker.de>  writes:
>
>>> - have you considered changing the way vdr detects adapter on startup ?
>>>     for instance vdr won't detect /dev/dvb/adapter1 if
>>>     /dev/dvb/adapter0 does not exist. this might happen when using udev
>>>     rules and restarting vdr while a module isn't loaded
>>
>> dynamite 0.0.6 now uses udev device enumeration to detect all dvb frontends regardless of their numbering.
>> If you would like to keep an adapter from being used by vdr you can set an udev environment property on that device.
>
> Hi !
>
> Thanks a lot !
> I'm using 0.0.6b and the new udev rules.
> My setup is :
> - first vdr (the one in production that has to be working, especially
>    when i'm not @home)
>    genuine vdr 1.7.17, 2 dvb-s cards, diseqc
>
> - 2nd vdr
>    dynamite vdr 1.7.17, 1 dvb-s ff card which keeps crashing + 1 dvb-t
>    usb stick
>
> It is working perfectly, i haven't tried to remove/add adapter yet.
> I will do later today.

  Thanks for testing this!

> Just another question:
> When i decide to only use one vdr instance, i'll be needing the diseqc
> trick in diseqc.conf (the one that replaces the sourcecaps patch), how
> do you think it could work ?
> I guess the quickest workaround would be to add a new udev param to
> force the card index in vdr.

  I only have DVB-C and only limited knowledge about DVB-S, Diseqc and all this stuff. But I found a description of the 
sourcecaps patch. When I have read and understood it, I'll get in touch with you.
  It seems possible to configure this via udev and solve it with a device hook. But I haven't tried it and don't know 
exactly how the device hooks works. (a little bit of brainstorming)

> I've been having issues with udev rules to order dvb devices in the
> past, instead i'm using a simple simple script that gives vdr the
> correct card number.
> ex:
> vdr $(get-dvb-device.sh main)
> "get-dvb-devices.sh main" returns "-D 1 -D 4" for example. that way
> i'm sure my main vdr always uses my two stable dvb-s cards

Regards,
Lars.
  
syrius.ml@no-log.org March 29, 2011, 8:10 p.m. UTC | #10
"L. Hanisch" <dvb@flensrocker.de> writes:

Hi, 

>> It is working perfectly, i haven't tried to remove/add adapter yet.
>> I will do later today.
>
>  Thanks for testing this!

No issues with detaching and reattaching, that's a very nice feature !

>> Just another question:
>> When i decide to only use one vdr instance, i'll be needing the diseqc
>> trick in diseqc.conf (the one that replaces the sourcecaps patch), how
>> do you think it could work ?
>> I guess the quickest workaround would be to add a new udev param to
>> force the card index in vdr.
>
>  I only have DVB-C and only limited knowledge about DVB-S, Diseqc and
> all this stuff. But I found a description of the sourcecaps
> patch. When I have read and understood it, I'll get in touch with you.
>  It seems possible to configure this via udev and solve it with a
> device hook. But I haven't tried it and don't know exactly how the
> device hooks works. (a little bit of brainstorming)

Ok, Thanks again for that. I wish i wasn't that clueless about c++ and
vdr internals.

--
  

Patch

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) {