From 1500458aeac572780b2045bdff8581d692451772 Mon Sep 17 00:00:00 2001 From: Lars Hanisch Date: Wed, 3 Apr 2013 21:22:25 +0200 Subject: add patches for vdr 2.0.0 --- .../vdr-1.7.42-dynamite+externalci+rotorng.patch | 1070 ++++++++++++++++++++ patches/vdr-1.7.42-dynamite+externalci.patch | 988 ++++++++++++++++++ patches/vdr-1.7.42-dynamite.patch | 818 +++++++++++++++ .../vdr-2.0.0-dynamite+externalci+rotorng.patch | 1070 ++++++++++++++++++++ patches/vdr-2.0.0-dynamite+externalci.patch | 988 ++++++++++++++++++ patches/vdr-2.0.0-dynamite.patch | 818 +++++++++++++++ 6 files changed, 5752 insertions(+) create mode 100644 patches/vdr-1.7.42-dynamite+externalci+rotorng.patch create mode 100644 patches/vdr-1.7.42-dynamite+externalci.patch create mode 100644 patches/vdr-1.7.42-dynamite.patch create mode 100644 patches/vdr-2.0.0-dynamite+externalci+rotorng.patch create mode 100644 patches/vdr-2.0.0-dynamite+externalci.patch create mode 100644 patches/vdr-2.0.0-dynamite.patch diff --git a/patches/vdr-1.7.42-dynamite+externalci+rotorng.patch b/patches/vdr-1.7.42-dynamite+externalci+rotorng.patch new file mode 100644 index 0000000..ea92508 --- /dev/null +++ b/patches/vdr-1.7.42-dynamite+externalci+rotorng.patch @@ -0,0 +1,1070 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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..818ea29 100644 +--- a/ci.h ++++ b/ci.h +@@ -72,6 +72,7 @@ public: + }; + + class cDevice; ++class cTSBufferBase; + class cCamSlot; + + enum eModuleStatus { msNone, msReset, msPresent, msReady }; +@@ -115,6 +116,13 @@ 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. ++#define EXTERNALCI_PATCH ++ virtual cTSBufferBase *GetTSBuffer(int FdDvr) { return NULL; } ++ ///< Derived classes can return a special TS buffer with features ++ ///< like rerouting the stream through an external ci. ++ ///< The caller must delete the buffer. ++ 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..ed3b1da 100644 +--- a/device.h ++++ b/device.h +@@ -24,6 +24,7 @@ + #include "spu.h" + #include "thread.h" + #include "tools.h" ++#include + + #define MAXDEVICES 16 // the maximum number of devices in the system + #define MAXPIDHANDLES 64 // the maximum number of different PIDs per device +@@ -169,7 +170,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 +@@ -196,9 +196,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 DeviceType(void) const; +@@ -338,6 +335,7 @@ public: + ///< Returns true if the device is currently showing any programme to + ///< the user, either through replaying or live. + ++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) {return false;} + // PID handle facilities + + private: +@@ -423,9 +421,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 + +@@ -586,9 +581,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); +@@ -802,6 +794,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 +@@ -812,7 +836,14 @@ public: + /// sure the returned data points to a TS packet and automatically + /// re-synchronizes after broken packets. + +-class cTSBuffer : public cThread { ++class cTSBufferBase { ++public: ++ cTSBufferBase() {} ++ virtual ~cTSBufferBase() {} ++ virtual uchar *Get(void) = 0; ++ }; ++ ++class cTSBuffer : public cTSBufferBase, public cThread { + private: + int f; + int cardIndex; +@@ -821,8 +852,51 @@ private: + virtual void Action(void); + public: + cTSBuffer(int File, int Size, int CardIndex); +- ~cTSBuffer(); +- uchar *Get(void); ++ virtual ~cTSBuffer(); ++ virtual 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 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 DynamicDeviceProbes; ++ + #endif //__DEVICE_H +diff --git a/dvbci.c b/dvbci.c +index 5289bbd..baa70bc 100644 +--- a/dvbci.c ++++ b/dvbci.c +@@ -10,41 +10,70 @@ + #include "dvbci.h" + #include + #include +-#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; +- ca_caps_t Caps; +- if (ioctl(fd, CA_GET_CAP, &Caps) == 0) { +- if ((Caps.slot_type & CA_CI_LINK) != 0) { +- int NumSlots = Caps.slot_num; +- if (NumSlots > 0) { +- for (int i = 0; i < NumSlots; i++) +- new cCamSlot(this); +- Start(); +- } +- else +- esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber()); +- } +- else +- isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber()); +- } +- else +- esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber()); ++ adapter = Adapter; ++ frontend = Frontend; ++ idle = false; ++ GetNumCamSlots(Device, Fd, this); ++ Start(); + } + + 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; ++} ++ ++cTSBufferBase *cDvbCiAdapter::GetTSBuffer(int FdDvr) ++{ ++ if (device) ++ return new cTSBuffer(FdDvr, MEGABYTE(5), device->CardIndex() + 1); ++ return NULL; + } + + 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 +90,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 +100,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 +111,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 +134,60 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query) + return true; + } + +-cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) ++int cDvbCiAdapter::GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter) + { +- // TODO check whether a CI is actually present? +- if (Device) +- return new cDvbCiAdapter(Device, Fd); +- return NULL; ++ int NumSlots = -1; ++ if (Fd >= 0) { ++ ca_caps_t Caps; ++ if (ioctl(Fd, CA_GET_CAP, &Caps) == 0) { ++ if ((Caps.slot_type & CA_CI_LINK) != 0) { ++ NumSlots = Caps.slot_num; ++ if (NumSlots == 0) ++ esyslog("ERROR: no CAM slots found on device %d", Device->DeviceNumber()); ++ else if (CiAdapter != NULL) { ++ for (int i = 0; i < NumSlots; i++) ++ new cCamSlot(CiAdapter); ++ } ++ else ++ return NumSlots; ++ } ++ else ++ isyslog("device %d doesn't support CI link layer interface", Device->DeviceNumber()); ++ } ++ else ++ esyslog("ERROR: can't get CA capabilities on device %d", Device->DeviceNumber()); ++ } ++ return -1; ++} ++ ++cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) ++{ ++ // don't create a ci-adapter if it's not useable ++ if (Device && (Fd >= 0) && (GetNumCamSlots(Device, Fd, NULL) > 0)) ++ return new cDvbCiAdapter(Device, Fd, Adapter, Frontend); ++ ++ if (Fd >= 0) ++ close(Fd); ++ ++ // try to find an external ci-adapter ++ for (cDvbCiAdapterProbe *cp = DvbCiAdapterProbes.First(); cp; cp = DvbCiAdapterProbes.Next(cp)) { ++ cDvbCiAdapter *ca = cp->Probe(Device); ++ if (ca) ++ return ca; ++ } ++ return NULL; ++} ++ ++// --- cDvbCiAdapterProbe ------------------------------------------------------- ++ ++cList DvbCiAdapterProbes; ++ ++cDvbCiAdapterProbe::cDvbCiAdapterProbe(void) ++{ ++ DvbCiAdapterProbes.Add(this); ++} ++ ++cDvbCiAdapterProbe::~cDvbCiAdapterProbe() ++{ ++ DvbCiAdapterProbes.Del(this, false); + } +diff --git a/dvbci.h b/dvbci.h +index adbe40d..d908b2f 100644 +--- a/dvbci.h ++++ b/dvbci.h +@@ -16,16 +16,48 @@ 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 cTSBufferBase *GetTSBuffer(int FdDvr); ++ static int GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter); ++ ///< Tests if the CA device is usable for vdr. ++ ///< If CiAdapter is not NULL it will create the CamSlots for the given ci-adapter. ++ 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); ++ }; ++ ++// A plugin that implements an external DVB ci-adapter derived from cDvbCiAdapter needs to create ++// a cDvbCiAdapterProbe derived object on the heap in order to have its Probe() ++// function called, where it can actually create the appropriate ci-adapter. ++// The cDvbCiAdapterProbe object must be created in the plugin's constructor, ++// and deleted in its destructor. ++// Every plugin has to track its own list of already used device nodes. ++// The Probes are always called if the base cDvbCiAdapter can't create a ci-adapter on its own. ++ ++class cDvbCiAdapterProbe : public cListObject { ++public: ++ cDvbCiAdapterProbe(void); ++ virtual ~cDvbCiAdapterProbe(); ++ virtual cDvbCiAdapter *Probe(cDevice *Device) = 0; ++ ///< Probes for a DVB ci-adapter for the given Device and creates the appropriate ++ ///< object derived from cDvbCiAdapter if applicable. ++ ///< Returns NULL if no adapter has been created. + }; + ++extern cList DvbCiAdapterProbes; ++ + #endif //__DVBCI_H +diff --git a/dvbdevice.c b/dvbdevice.c +index ab6149f..351f1ed 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -288,9 +288,10 @@ class cDvbTuner : public cThread { + private: + static cMutex bondMutex; + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; ++ bool SendDiseqc; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +302,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -311,11 +312,16 @@ private: + cDvbTuner *GetBondedMaster(void); + bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; } + void ClearEventQueue(void) const; ++ dvb_diseqc_master_cmd diseqc_cmd; + bool GetFrontendStatus(fe_status_t &Status) const; + void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency); + 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(); +@@ -327,9 +333,13 @@ public: + uint32_t SubsystemId(void) const { return subsystemId; } + bool IsTunedTo(const cChannel *Channel) const; + void SetChannel(const cChannel *Channel); ++ bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -339,6 +349,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + frontendType = SYS_UNDEFINED; + device = Device; + fd_frontend = Fd_Frontend; ++ SendDiseqc=false; + adapter = Adapter; + frontend = Frontend; + subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); +@@ -351,6 +362,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +380,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +526,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; +@@ -720,8 +736,28 @@ static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTranspon + return ds; + } + ++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) ++{ ++ cMutexLock MutexLock(&mutex); ++ cDvbTransponderParameters dtp(channel.Parameters()); ++ ++ // Determine the required frontend type: ++ int frontendType = GetRequiredDeliverySystem(&channel, &dtp); ++ ++ if ((frontendType!=SYS_DVBS2 && frontendType!=SYS_DVBS) || SendDiseqc) ++ return false; ++ if (!OpenFrontend()) ++ return false; ++ diseqc_cmd=cmd; ++ SendDiseqc=true; ++ newSet.Broadcast(); ++ return true; ++} ++ + 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);\ +@@ -868,10 +904,16 @@ 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); ++ if (SendDiseqc) { ++ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd)); ++ SendDiseqc=false; ++ } + int WaitTime = 1000; + switch (tunerStatus) { + case tsIdle: +@@ -923,6 +965,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1084,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; +@@ -1026,9 +1103,8 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + + // Common Interface: + +- fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); +- if (fd_ca >= 0) +- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); ++ int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); ++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend); + + // The DVR device (will be opened and closed as needed): + +@@ -1256,7 +1332,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,7 +1370,10 @@ bool cDvbDevice::BondDevices(const char *Bondings) + void cDvbDevice::UnBondDevices(void) + { + for (int i = 0; i < cDevice::NumDevices(); i++) { +- if (cDvbDevice *d = dynamic_cast(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,6 +1427,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; +@@ -1513,7 +1616,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 +@@ -1558,6 +1661,11 @@ bool cDvbDevice::HasLock(int TimeoutMs) const + return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false; + } + ++bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) ++{ ++ return dvbTuner->SendDiseqcCmd(cmd); ++} ++ + void cDvbDevice::SetTransferModeForDolbyDigital(int Mode) + { + setTransferModeForDolbyDigital = Mode; +@@ -1567,8 +1675,12 @@ bool cDvbDevice::OpenDvr(void) + { + CloseDvr(); + fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); +- if (fd_dvr >= 0) +- tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ if (fd_dvr >= 0) { ++ if (ciAdapter) ++ tsBuffer = ciAdapter->GetTSBuffer(fd_dvr); ++ if (tsBuffer == NULL) ++ tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ } + return fd_dvr >= 0; + } + +diff --git a/dvbdevice.h b/dvbdevice.h +index b4d07f5..339c52b 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: +@@ -127,19 +127,20 @@ private: + int deliverySystems[MAXDELIVERYSYSTEMS]; + int numDeliverySystems; + int numModulations; +- int fd_dvr, fd_ca; ++ int fd_dvr; + static cMutex bondMutex; + cDvbDevice *bondedDevice; + 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 DeviceType(void) const; + 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), +@@ -193,6 +194,7 @@ protected: + virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); + public: + virtual bool HasLock(int TimeoutMs = 0) const; ++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); + + // PID handle facilities + +@@ -225,7 +227,7 @@ public: + // Receiver facilities + + private: +- cTSBuffer *tsBuffer; ++ cTSBufferBase *tsBuffer; + protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); diff --git a/patches/vdr-1.7.42-dynamite+externalci.patch b/patches/vdr-1.7.42-dynamite+externalci.patch new file mode 100644 index 0000000..dd421af --- /dev/null +++ b/patches/vdr-1.7.42-dynamite+externalci.patch @@ -0,0 +1,988 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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..818ea29 100644 +--- a/ci.h ++++ b/ci.h +@@ -72,6 +72,7 @@ public: + }; + + class cDevice; ++class cTSBufferBase; + class cCamSlot; + + enum eModuleStatus { msNone, msReset, msPresent, msReady }; +@@ -115,6 +116,13 @@ 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. ++#define EXTERNALCI_PATCH ++ virtual cTSBufferBase *GetTSBuffer(int FdDvr) { return NULL; } ++ ///< Derived classes can return a special TS buffer with features ++ ///< like rerouting the stream through an external ci. ++ ///< The caller must delete the buffer. ++ 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..95acccd 100644 +--- a/device.h ++++ b/device.h +@@ -169,7 +169,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 +@@ -196,9 +195,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 DeviceType(void) const; +@@ -423,9 +419,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 + +@@ -586,9 +579,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); +@@ -802,6 +792,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 +@@ -812,7 +834,14 @@ public: + /// sure the returned data points to a TS packet and automatically + /// re-synchronizes after broken packets. + +-class cTSBuffer : public cThread { ++class cTSBufferBase { ++public: ++ cTSBufferBase() {} ++ virtual ~cTSBufferBase() {} ++ virtual uchar *Get(void) = 0; ++ }; ++ ++class cTSBuffer : public cTSBufferBase, public cThread { + private: + int f; + int cardIndex; +@@ -821,8 +850,51 @@ private: + virtual void Action(void); + public: + cTSBuffer(int File, int Size, int CardIndex); +- ~cTSBuffer(); +- uchar *Get(void); ++ virtual ~cTSBuffer(); ++ virtual 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 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 DynamicDeviceProbes; ++ + #endif //__DEVICE_H +diff --git a/dvbci.c b/dvbci.c +index 5289bbd..baa70bc 100644 +--- a/dvbci.c ++++ b/dvbci.c +@@ -10,41 +10,70 @@ + #include "dvbci.h" + #include + #include +-#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; +- ca_caps_t Caps; +- if (ioctl(fd, CA_GET_CAP, &Caps) == 0) { +- if ((Caps.slot_type & CA_CI_LINK) != 0) { +- int NumSlots = Caps.slot_num; +- if (NumSlots > 0) { +- for (int i = 0; i < NumSlots; i++) +- new cCamSlot(this); +- Start(); +- } +- else +- esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber()); +- } +- else +- isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber()); +- } +- else +- esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber()); ++ adapter = Adapter; ++ frontend = Frontend; ++ idle = false; ++ GetNumCamSlots(Device, Fd, this); ++ Start(); + } + + 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; ++} ++ ++cTSBufferBase *cDvbCiAdapter::GetTSBuffer(int FdDvr) ++{ ++ if (device) ++ return new cTSBuffer(FdDvr, MEGABYTE(5), device->CardIndex() + 1); ++ return NULL; + } + + 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 +90,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 +100,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 +111,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 +134,60 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query) + return true; + } + +-cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) ++int cDvbCiAdapter::GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter) + { +- // TODO check whether a CI is actually present? +- if (Device) +- return new cDvbCiAdapter(Device, Fd); +- return NULL; ++ int NumSlots = -1; ++ if (Fd >= 0) { ++ ca_caps_t Caps; ++ if (ioctl(Fd, CA_GET_CAP, &Caps) == 0) { ++ if ((Caps.slot_type & CA_CI_LINK) != 0) { ++ NumSlots = Caps.slot_num; ++ if (NumSlots == 0) ++ esyslog("ERROR: no CAM slots found on device %d", Device->DeviceNumber()); ++ else if (CiAdapter != NULL) { ++ for (int i = 0; i < NumSlots; i++) ++ new cCamSlot(CiAdapter); ++ } ++ else ++ return NumSlots; ++ } ++ else ++ isyslog("device %d doesn't support CI link layer interface", Device->DeviceNumber()); ++ } ++ else ++ esyslog("ERROR: can't get CA capabilities on device %d", Device->DeviceNumber()); ++ } ++ return -1; ++} ++ ++cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) ++{ ++ // don't create a ci-adapter if it's not useable ++ if (Device && (Fd >= 0) && (GetNumCamSlots(Device, Fd, NULL) > 0)) ++ return new cDvbCiAdapter(Device, Fd, Adapter, Frontend); ++ ++ if (Fd >= 0) ++ close(Fd); ++ ++ // try to find an external ci-adapter ++ for (cDvbCiAdapterProbe *cp = DvbCiAdapterProbes.First(); cp; cp = DvbCiAdapterProbes.Next(cp)) { ++ cDvbCiAdapter *ca = cp->Probe(Device); ++ if (ca) ++ return ca; ++ } ++ return NULL; ++} ++ ++// --- cDvbCiAdapterProbe ------------------------------------------------------- ++ ++cList DvbCiAdapterProbes; ++ ++cDvbCiAdapterProbe::cDvbCiAdapterProbe(void) ++{ ++ DvbCiAdapterProbes.Add(this); ++} ++ ++cDvbCiAdapterProbe::~cDvbCiAdapterProbe() ++{ ++ DvbCiAdapterProbes.Del(this, false); + } +diff --git a/dvbci.h b/dvbci.h +index adbe40d..d908b2f 100644 +--- a/dvbci.h ++++ b/dvbci.h +@@ -16,16 +16,48 @@ 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 cTSBufferBase *GetTSBuffer(int FdDvr); ++ static int GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter); ++ ///< Tests if the CA device is usable for vdr. ++ ///< If CiAdapter is not NULL it will create the CamSlots for the given ci-adapter. ++ 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); ++ }; ++ ++// A plugin that implements an external DVB ci-adapter derived from cDvbCiAdapter needs to create ++// a cDvbCiAdapterProbe derived object on the heap in order to have its Probe() ++// function called, where it can actually create the appropriate ci-adapter. ++// The cDvbCiAdapterProbe object must be created in the plugin's constructor, ++// and deleted in its destructor. ++// Every plugin has to track its own list of already used device nodes. ++// The Probes are always called if the base cDvbCiAdapter can't create a ci-adapter on its own. ++ ++class cDvbCiAdapterProbe : public cListObject { ++public: ++ cDvbCiAdapterProbe(void); ++ virtual ~cDvbCiAdapterProbe(); ++ virtual cDvbCiAdapter *Probe(cDevice *Device) = 0; ++ ///< Probes for a DVB ci-adapter for the given Device and creates the appropriate ++ ///< object derived from cDvbCiAdapter if applicable. ++ ///< Returns NULL if no adapter has been created. + }; + ++extern cList DvbCiAdapterProbes; ++ + #endif //__DVBCI_H +diff --git a/dvbdevice.c b/dvbdevice.c +index ab6149f..11e9fd8 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -290,7 +290,7 @@ private: + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +301,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -316,6 +316,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(); +@@ -330,6 +334,9 @@ public: + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -351,6 +358,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +376,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +522,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; +@@ -722,6 +734,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);\ +@@ -868,9 +882,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; + switch (tunerStatus) { +@@ -923,6 +939,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1058,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; +@@ -1026,9 +1077,8 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + + // Common Interface: + +- fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); +- if (fd_ca >= 0) +- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); ++ int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); ++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend); + + // The DVR device (will be opened and closed as needed): + +@@ -1256,7 +1306,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,7 +1344,10 @@ bool cDvbDevice::BondDevices(const char *Bondings) + void cDvbDevice::UnBondDevices(void) + { + for (int i = 0; i < cDevice::NumDevices(); i++) { +- if (cDvbDevice *d = dynamic_cast(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,6 +1401,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; +@@ -1513,7 +1590,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 +@@ -1567,8 +1644,12 @@ bool cDvbDevice::OpenDvr(void) + { + CloseDvr(); + fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); +- if (fd_dvr >= 0) +- tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ if (fd_dvr >= 0) { ++ if (ciAdapter) ++ tsBuffer = ciAdapter->GetTSBuffer(fd_dvr); ++ if (tsBuffer == NULL) ++ tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ } + return fd_dvr >= 0; + } + +diff --git a/dvbdevice.h b/dvbdevice.h +index b4d07f5..f66720d 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: +@@ -127,19 +127,20 @@ private: + int deliverySystems[MAXDELIVERYSYSTEMS]; + int numDeliverySystems; + int numModulations; +- int fd_dvr, fd_ca; ++ int fd_dvr; + static cMutex bondMutex; + cDvbDevice *bondedDevice; + 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 DeviceType(void) const; + 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), +@@ -225,7 +226,7 @@ public: + // Receiver facilities + + private: +- cTSBuffer *tsBuffer; ++ cTSBufferBase *tsBuffer; + protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); diff --git a/patches/vdr-1.7.42-dynamite.patch b/patches/vdr-1.7.42-dynamite.patch new file mode 100644 index 0000000..3782e05 --- /dev/null +++ b/patches/vdr-1.7.42-dynamite.patch @@ -0,0 +1,818 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..3217e9d 100644 +--- a/device.h ++++ b/device.h +@@ -169,7 +169,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 +@@ -196,9 +195,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 DeviceType(void) const; +@@ -423,9 +419,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 + +@@ -586,9 +579,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); +@@ -802,6 +792,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 +@@ -825,4 +847,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 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 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 + #include +-#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 ab6149f..47cf585 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -290,7 +290,7 @@ private: + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +301,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -316,6 +316,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(); +@@ -330,6 +334,9 @@ public: + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -351,6 +358,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +376,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +522,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; +@@ -722,6 +734,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);\ +@@ -868,9 +882,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; + switch (tunerStatus) { +@@ -923,6 +939,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1058,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; +@@ -1028,7 +1079,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): + +@@ -1256,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,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(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,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; +@@ -1513,7 +1591,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 b4d07f5..6f785ec 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,13 +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 DeviceType(void) const; + 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), diff --git a/patches/vdr-2.0.0-dynamite+externalci+rotorng.patch b/patches/vdr-2.0.0-dynamite+externalci+rotorng.patch new file mode 100644 index 0000000..ea92508 --- /dev/null +++ b/patches/vdr-2.0.0-dynamite+externalci+rotorng.patch @@ -0,0 +1,1070 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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..818ea29 100644 +--- a/ci.h ++++ b/ci.h +@@ -72,6 +72,7 @@ public: + }; + + class cDevice; ++class cTSBufferBase; + class cCamSlot; + + enum eModuleStatus { msNone, msReset, msPresent, msReady }; +@@ -115,6 +116,13 @@ 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. ++#define EXTERNALCI_PATCH ++ virtual cTSBufferBase *GetTSBuffer(int FdDvr) { return NULL; } ++ ///< Derived classes can return a special TS buffer with features ++ ///< like rerouting the stream through an external ci. ++ ///< The caller must delete the buffer. ++ 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..ed3b1da 100644 +--- a/device.h ++++ b/device.h +@@ -24,6 +24,7 @@ + #include "spu.h" + #include "thread.h" + #include "tools.h" ++#include + + #define MAXDEVICES 16 // the maximum number of devices in the system + #define MAXPIDHANDLES 64 // the maximum number of different PIDs per device +@@ -169,7 +170,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 +@@ -196,9 +196,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 DeviceType(void) const; +@@ -338,6 +335,7 @@ public: + ///< Returns true if the device is currently showing any programme to + ///< the user, either through replaying or live. + ++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) {return false;} + // PID handle facilities + + private: +@@ -423,9 +421,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 + +@@ -586,9 +581,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); +@@ -802,6 +794,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 +@@ -812,7 +836,14 @@ public: + /// sure the returned data points to a TS packet and automatically + /// re-synchronizes after broken packets. + +-class cTSBuffer : public cThread { ++class cTSBufferBase { ++public: ++ cTSBufferBase() {} ++ virtual ~cTSBufferBase() {} ++ virtual uchar *Get(void) = 0; ++ }; ++ ++class cTSBuffer : public cTSBufferBase, public cThread { + private: + int f; + int cardIndex; +@@ -821,8 +852,51 @@ private: + virtual void Action(void); + public: + cTSBuffer(int File, int Size, int CardIndex); +- ~cTSBuffer(); +- uchar *Get(void); ++ virtual ~cTSBuffer(); ++ virtual 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 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 DynamicDeviceProbes; ++ + #endif //__DEVICE_H +diff --git a/dvbci.c b/dvbci.c +index 5289bbd..baa70bc 100644 +--- a/dvbci.c ++++ b/dvbci.c +@@ -10,41 +10,70 @@ + #include "dvbci.h" + #include + #include +-#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; +- ca_caps_t Caps; +- if (ioctl(fd, CA_GET_CAP, &Caps) == 0) { +- if ((Caps.slot_type & CA_CI_LINK) != 0) { +- int NumSlots = Caps.slot_num; +- if (NumSlots > 0) { +- for (int i = 0; i < NumSlots; i++) +- new cCamSlot(this); +- Start(); +- } +- else +- esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber()); +- } +- else +- isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber()); +- } +- else +- esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber()); ++ adapter = Adapter; ++ frontend = Frontend; ++ idle = false; ++ GetNumCamSlots(Device, Fd, this); ++ Start(); + } + + 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; ++} ++ ++cTSBufferBase *cDvbCiAdapter::GetTSBuffer(int FdDvr) ++{ ++ if (device) ++ return new cTSBuffer(FdDvr, MEGABYTE(5), device->CardIndex() + 1); ++ return NULL; + } + + 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 +90,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 +100,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 +111,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 +134,60 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query) + return true; + } + +-cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) ++int cDvbCiAdapter::GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter) + { +- // TODO check whether a CI is actually present? +- if (Device) +- return new cDvbCiAdapter(Device, Fd); +- return NULL; ++ int NumSlots = -1; ++ if (Fd >= 0) { ++ ca_caps_t Caps; ++ if (ioctl(Fd, CA_GET_CAP, &Caps) == 0) { ++ if ((Caps.slot_type & CA_CI_LINK) != 0) { ++ NumSlots = Caps.slot_num; ++ if (NumSlots == 0) ++ esyslog("ERROR: no CAM slots found on device %d", Device->DeviceNumber()); ++ else if (CiAdapter != NULL) { ++ for (int i = 0; i < NumSlots; i++) ++ new cCamSlot(CiAdapter); ++ } ++ else ++ return NumSlots; ++ } ++ else ++ isyslog("device %d doesn't support CI link layer interface", Device->DeviceNumber()); ++ } ++ else ++ esyslog("ERROR: can't get CA capabilities on device %d", Device->DeviceNumber()); ++ } ++ return -1; ++} ++ ++cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) ++{ ++ // don't create a ci-adapter if it's not useable ++ if (Device && (Fd >= 0) && (GetNumCamSlots(Device, Fd, NULL) > 0)) ++ return new cDvbCiAdapter(Device, Fd, Adapter, Frontend); ++ ++ if (Fd >= 0) ++ close(Fd); ++ ++ // try to find an external ci-adapter ++ for (cDvbCiAdapterProbe *cp = DvbCiAdapterProbes.First(); cp; cp = DvbCiAdapterProbes.Next(cp)) { ++ cDvbCiAdapter *ca = cp->Probe(Device); ++ if (ca) ++ return ca; ++ } ++ return NULL; ++} ++ ++// --- cDvbCiAdapterProbe ------------------------------------------------------- ++ ++cList DvbCiAdapterProbes; ++ ++cDvbCiAdapterProbe::cDvbCiAdapterProbe(void) ++{ ++ DvbCiAdapterProbes.Add(this); ++} ++ ++cDvbCiAdapterProbe::~cDvbCiAdapterProbe() ++{ ++ DvbCiAdapterProbes.Del(this, false); + } +diff --git a/dvbci.h b/dvbci.h +index adbe40d..d908b2f 100644 +--- a/dvbci.h ++++ b/dvbci.h +@@ -16,16 +16,48 @@ 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 cTSBufferBase *GetTSBuffer(int FdDvr); ++ static int GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter); ++ ///< Tests if the CA device is usable for vdr. ++ ///< If CiAdapter is not NULL it will create the CamSlots for the given ci-adapter. ++ 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); ++ }; ++ ++// A plugin that implements an external DVB ci-adapter derived from cDvbCiAdapter needs to create ++// a cDvbCiAdapterProbe derived object on the heap in order to have its Probe() ++// function called, where it can actually create the appropriate ci-adapter. ++// The cDvbCiAdapterProbe object must be created in the plugin's constructor, ++// and deleted in its destructor. ++// Every plugin has to track its own list of already used device nodes. ++// The Probes are always called if the base cDvbCiAdapter can't create a ci-adapter on its own. ++ ++class cDvbCiAdapterProbe : public cListObject { ++public: ++ cDvbCiAdapterProbe(void); ++ virtual ~cDvbCiAdapterProbe(); ++ virtual cDvbCiAdapter *Probe(cDevice *Device) = 0; ++ ///< Probes for a DVB ci-adapter for the given Device and creates the appropriate ++ ///< object derived from cDvbCiAdapter if applicable. ++ ///< Returns NULL if no adapter has been created. + }; + ++extern cList DvbCiAdapterProbes; ++ + #endif //__DVBCI_H +diff --git a/dvbdevice.c b/dvbdevice.c +index ab6149f..351f1ed 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -288,9 +288,10 @@ class cDvbTuner : public cThread { + private: + static cMutex bondMutex; + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; ++ bool SendDiseqc; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +302,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -311,11 +312,16 @@ private: + cDvbTuner *GetBondedMaster(void); + bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; } + void ClearEventQueue(void) const; ++ dvb_diseqc_master_cmd diseqc_cmd; + bool GetFrontendStatus(fe_status_t &Status) const; + void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency); + 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(); +@@ -327,9 +333,13 @@ public: + uint32_t SubsystemId(void) const { return subsystemId; } + bool IsTunedTo(const cChannel *Channel) const; + void SetChannel(const cChannel *Channel); ++ bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -339,6 +349,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + frontendType = SYS_UNDEFINED; + device = Device; + fd_frontend = Fd_Frontend; ++ SendDiseqc=false; + adapter = Adapter; + frontend = Frontend; + subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); +@@ -351,6 +362,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +380,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +526,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; +@@ -720,8 +736,28 @@ static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTranspon + return ds; + } + ++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) ++{ ++ cMutexLock MutexLock(&mutex); ++ cDvbTransponderParameters dtp(channel.Parameters()); ++ ++ // Determine the required frontend type: ++ int frontendType = GetRequiredDeliverySystem(&channel, &dtp); ++ ++ if ((frontendType!=SYS_DVBS2 && frontendType!=SYS_DVBS) || SendDiseqc) ++ return false; ++ if (!OpenFrontend()) ++ return false; ++ diseqc_cmd=cmd; ++ SendDiseqc=true; ++ newSet.Broadcast(); ++ return true; ++} ++ + 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);\ +@@ -868,10 +904,16 @@ 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); ++ if (SendDiseqc) { ++ CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd)); ++ SendDiseqc=false; ++ } + int WaitTime = 1000; + switch (tunerStatus) { + case tsIdle: +@@ -923,6 +965,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1084,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; +@@ -1026,9 +1103,8 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + + // Common Interface: + +- fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); +- if (fd_ca >= 0) +- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); ++ int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); ++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend); + + // The DVR device (will be opened and closed as needed): + +@@ -1256,7 +1332,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,7 +1370,10 @@ bool cDvbDevice::BondDevices(const char *Bondings) + void cDvbDevice::UnBondDevices(void) + { + for (int i = 0; i < cDevice::NumDevices(); i++) { +- if (cDvbDevice *d = dynamic_cast(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,6 +1427,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; +@@ -1513,7 +1616,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 +@@ -1558,6 +1661,11 @@ bool cDvbDevice::HasLock(int TimeoutMs) const + return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false; + } + ++bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) ++{ ++ return dvbTuner->SendDiseqcCmd(cmd); ++} ++ + void cDvbDevice::SetTransferModeForDolbyDigital(int Mode) + { + setTransferModeForDolbyDigital = Mode; +@@ -1567,8 +1675,12 @@ bool cDvbDevice::OpenDvr(void) + { + CloseDvr(); + fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); +- if (fd_dvr >= 0) +- tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ if (fd_dvr >= 0) { ++ if (ciAdapter) ++ tsBuffer = ciAdapter->GetTSBuffer(fd_dvr); ++ if (tsBuffer == NULL) ++ tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ } + return fd_dvr >= 0; + } + +diff --git a/dvbdevice.h b/dvbdevice.h +index b4d07f5..339c52b 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: +@@ -127,19 +127,20 @@ private: + int deliverySystems[MAXDELIVERYSYSTEMS]; + int numDeliverySystems; + int numModulations; +- int fd_dvr, fd_ca; ++ int fd_dvr; + static cMutex bondMutex; + cDvbDevice *bondedDevice; + 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 DeviceType(void) const; + 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), +@@ -193,6 +194,7 @@ protected: + virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); + public: + virtual bool HasLock(int TimeoutMs = 0) const; ++ virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); + + // PID handle facilities + +@@ -225,7 +227,7 @@ public: + // Receiver facilities + + private: +- cTSBuffer *tsBuffer; ++ cTSBufferBase *tsBuffer; + protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); diff --git a/patches/vdr-2.0.0-dynamite+externalci.patch b/patches/vdr-2.0.0-dynamite+externalci.patch new file mode 100644 index 0000000..dd421af --- /dev/null +++ b/patches/vdr-2.0.0-dynamite+externalci.patch @@ -0,0 +1,988 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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..818ea29 100644 +--- a/ci.h ++++ b/ci.h +@@ -72,6 +72,7 @@ public: + }; + + class cDevice; ++class cTSBufferBase; + class cCamSlot; + + enum eModuleStatus { msNone, msReset, msPresent, msReady }; +@@ -115,6 +116,13 @@ 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. ++#define EXTERNALCI_PATCH ++ virtual cTSBufferBase *GetTSBuffer(int FdDvr) { return NULL; } ++ ///< Derived classes can return a special TS buffer with features ++ ///< like rerouting the stream through an external ci. ++ ///< The caller must delete the buffer. ++ 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..95acccd 100644 +--- a/device.h ++++ b/device.h +@@ -169,7 +169,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 +@@ -196,9 +195,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 DeviceType(void) const; +@@ -423,9 +419,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 + +@@ -586,9 +579,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); +@@ -802,6 +792,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 +@@ -812,7 +834,14 @@ public: + /// sure the returned data points to a TS packet and automatically + /// re-synchronizes after broken packets. + +-class cTSBuffer : public cThread { ++class cTSBufferBase { ++public: ++ cTSBufferBase() {} ++ virtual ~cTSBufferBase() {} ++ virtual uchar *Get(void) = 0; ++ }; ++ ++class cTSBuffer : public cTSBufferBase, public cThread { + private: + int f; + int cardIndex; +@@ -821,8 +850,51 @@ private: + virtual void Action(void); + public: + cTSBuffer(int File, int Size, int CardIndex); +- ~cTSBuffer(); +- uchar *Get(void); ++ virtual ~cTSBuffer(); ++ virtual 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 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 DynamicDeviceProbes; ++ + #endif //__DEVICE_H +diff --git a/dvbci.c b/dvbci.c +index 5289bbd..baa70bc 100644 +--- a/dvbci.c ++++ b/dvbci.c +@@ -10,41 +10,70 @@ + #include "dvbci.h" + #include + #include +-#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; +- ca_caps_t Caps; +- if (ioctl(fd, CA_GET_CAP, &Caps) == 0) { +- if ((Caps.slot_type & CA_CI_LINK) != 0) { +- int NumSlots = Caps.slot_num; +- if (NumSlots > 0) { +- for (int i = 0; i < NumSlots; i++) +- new cCamSlot(this); +- Start(); +- } +- else +- esyslog("ERROR: no CAM slots found on device %d", device->DeviceNumber()); +- } +- else +- isyslog("device %d doesn't support CI link layer interface", device->DeviceNumber()); +- } +- else +- esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber()); ++ adapter = Adapter; ++ frontend = Frontend; ++ idle = false; ++ GetNumCamSlots(Device, Fd, this); ++ Start(); + } + + 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; ++} ++ ++cTSBufferBase *cDvbCiAdapter::GetTSBuffer(int FdDvr) ++{ ++ if (device) ++ return new cTSBuffer(FdDvr, MEGABYTE(5), device->CardIndex() + 1); ++ return NULL; + } + + 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 +90,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 +100,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 +111,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 +134,60 @@ bool cDvbCiAdapter::Assign(cDevice *Device, bool Query) + return true; + } + +-cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd) ++int cDvbCiAdapter::GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter) + { +- // TODO check whether a CI is actually present? +- if (Device) +- return new cDvbCiAdapter(Device, Fd); +- return NULL; ++ int NumSlots = -1; ++ if (Fd >= 0) { ++ ca_caps_t Caps; ++ if (ioctl(Fd, CA_GET_CAP, &Caps) == 0) { ++ if ((Caps.slot_type & CA_CI_LINK) != 0) { ++ NumSlots = Caps.slot_num; ++ if (NumSlots == 0) ++ esyslog("ERROR: no CAM slots found on device %d", Device->DeviceNumber()); ++ else if (CiAdapter != NULL) { ++ for (int i = 0; i < NumSlots; i++) ++ new cCamSlot(CiAdapter); ++ } ++ else ++ return NumSlots; ++ } ++ else ++ isyslog("device %d doesn't support CI link layer interface", Device->DeviceNumber()); ++ } ++ else ++ esyslog("ERROR: can't get CA capabilities on device %d", Device->DeviceNumber()); ++ } ++ return -1; ++} ++ ++cDvbCiAdapter *cDvbCiAdapter::CreateCiAdapter(cDevice *Device, int Fd, int Adapter, int Frontend) ++{ ++ // don't create a ci-adapter if it's not useable ++ if (Device && (Fd >= 0) && (GetNumCamSlots(Device, Fd, NULL) > 0)) ++ return new cDvbCiAdapter(Device, Fd, Adapter, Frontend); ++ ++ if (Fd >= 0) ++ close(Fd); ++ ++ // try to find an external ci-adapter ++ for (cDvbCiAdapterProbe *cp = DvbCiAdapterProbes.First(); cp; cp = DvbCiAdapterProbes.Next(cp)) { ++ cDvbCiAdapter *ca = cp->Probe(Device); ++ if (ca) ++ return ca; ++ } ++ return NULL; ++} ++ ++// --- cDvbCiAdapterProbe ------------------------------------------------------- ++ ++cList DvbCiAdapterProbes; ++ ++cDvbCiAdapterProbe::cDvbCiAdapterProbe(void) ++{ ++ DvbCiAdapterProbes.Add(this); ++} ++ ++cDvbCiAdapterProbe::~cDvbCiAdapterProbe() ++{ ++ DvbCiAdapterProbes.Del(this, false); + } +diff --git a/dvbci.h b/dvbci.h +index adbe40d..d908b2f 100644 +--- a/dvbci.h ++++ b/dvbci.h +@@ -16,16 +16,48 @@ 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 cTSBufferBase *GetTSBuffer(int FdDvr); ++ static int GetNumCamSlots(cDevice *Device, int Fd, cCiAdapter *CiAdapter); ++ ///< Tests if the CA device is usable for vdr. ++ ///< If CiAdapter is not NULL it will create the CamSlots for the given ci-adapter. ++ 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); ++ }; ++ ++// A plugin that implements an external DVB ci-adapter derived from cDvbCiAdapter needs to create ++// a cDvbCiAdapterProbe derived object on the heap in order to have its Probe() ++// function called, where it can actually create the appropriate ci-adapter. ++// The cDvbCiAdapterProbe object must be created in the plugin's constructor, ++// and deleted in its destructor. ++// Every plugin has to track its own list of already used device nodes. ++// The Probes are always called if the base cDvbCiAdapter can't create a ci-adapter on its own. ++ ++class cDvbCiAdapterProbe : public cListObject { ++public: ++ cDvbCiAdapterProbe(void); ++ virtual ~cDvbCiAdapterProbe(); ++ virtual cDvbCiAdapter *Probe(cDevice *Device) = 0; ++ ///< Probes for a DVB ci-adapter for the given Device and creates the appropriate ++ ///< object derived from cDvbCiAdapter if applicable. ++ ///< Returns NULL if no adapter has been created. + }; + ++extern cList DvbCiAdapterProbes; ++ + #endif //__DVBCI_H +diff --git a/dvbdevice.c b/dvbdevice.c +index ab6149f..11e9fd8 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -290,7 +290,7 @@ private: + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +301,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -316,6 +316,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(); +@@ -330,6 +334,9 @@ public: + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -351,6 +358,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +376,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +522,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; +@@ -722,6 +734,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);\ +@@ -868,9 +882,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; + switch (tunerStatus) { +@@ -923,6 +939,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1058,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; +@@ -1026,9 +1077,8 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + + // Common Interface: + +- fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); +- if (fd_ca >= 0) +- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); ++ int fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); ++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca, adapter, frontend); + + // The DVR device (will be opened and closed as needed): + +@@ -1256,7 +1306,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,7 +1344,10 @@ bool cDvbDevice::BondDevices(const char *Bondings) + void cDvbDevice::UnBondDevices(void) + { + for (int i = 0; i < cDevice::NumDevices(); i++) { +- if (cDvbDevice *d = dynamic_cast(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,6 +1401,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; +@@ -1513,7 +1590,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 +@@ -1567,8 +1644,12 @@ bool cDvbDevice::OpenDvr(void) + { + CloseDvr(); + fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); +- if (fd_dvr >= 0) +- tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ if (fd_dvr >= 0) { ++ if (ciAdapter) ++ tsBuffer = ciAdapter->GetTSBuffer(fd_dvr); ++ if (tsBuffer == NULL) ++ tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5), CardIndex() + 1); ++ } + return fd_dvr >= 0; + } + +diff --git a/dvbdevice.h b/dvbdevice.h +index b4d07f5..f66720d 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: +@@ -127,19 +127,20 @@ private: + int deliverySystems[MAXDELIVERYSYSTEMS]; + int numDeliverySystems; + int numModulations; +- int fd_dvr, fd_ca; ++ int fd_dvr; + static cMutex bondMutex; + cDvbDevice *bondedDevice; + 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 DeviceType(void) const; + 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), +@@ -225,7 +226,7 @@ public: + // Receiver facilities + + private: +- cTSBuffer *tsBuffer; ++ cTSBufferBase *tsBuffer; + protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); diff --git a/patches/vdr-2.0.0-dynamite.patch b/patches/vdr-2.0.0-dynamite.patch new file mode 100644 index 0000000..3782e05 --- /dev/null +++ b/patches/vdr-2.0.0-dynamite.patch @@ -0,0 +1,818 @@ +diff --git a/ci.c b/ci.c +index 9a4a829..88b50e7 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 ff51a11..dfdc081 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 cDevice::deviceHooks; +- +-cDevice::cDevice(void) ++cDevice *cDevice::nextParentDevice = NULL; ++ ++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); + +@@ -106,10 +116,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() +@@ -118,6 +132,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) +@@ -156,6 +193,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; +@@ -356,6 +395,8 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) ++ return parentDevice->SetCamSlot(CamSlot); + camSlot = CamSlot; + } + +@@ -568,6 +609,10 @@ void cDevice::DelLivePids(void) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -579,6 +624,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -610,12 +659,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); + } +@@ -777,6 +831,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) +@@ -823,19 +878,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); + } +@@ -1216,7 +1279,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); +@@ -1235,6 +1301,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() +@@ -1254,6 +1322,8 @@ void cDevice::Detach(cPlayer *Player) + + void cDevice::StopReplay(void) + { ++ if (parentDevice) ++ return parentDevice->StopReplay(); + if (player) { + Detach(player); + if (IsPrimaryDevice()) +@@ -1534,6 +1604,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() && HasProgramme()) + priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing +@@ -1552,6 +1624,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]) +@@ -1632,10 +1706,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 +@@ -1674,6 +1751,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; +@@ -1699,6 +1778,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++) { +@@ -1783,3 +1864,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList DynamicDeviceProbes; ++ ++cList 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 fd010d4..3217e9d 100644 +--- a/device.h ++++ b/device.h +@@ -169,7 +169,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 +@@ -196,9 +195,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 DeviceType(void) const; +@@ -423,9 +419,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 + +@@ -586,9 +579,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); +@@ -802,6 +792,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 +@@ -825,4 +847,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 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 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 + #include +-#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 ab6149f..47cf585 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -290,7 +290,7 @@ private: + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + int frontendType; + const cDvbDevice *device; +- int fd_frontend; ++ mutable int fd_frontend; + int adapter, frontend; + uint32_t subsystemId; + int tuneTimeout; +@@ -301,7 +301,7 @@ private: + const cScr *scr; + bool lnbPowerTurnedOn; + eTunerStatus tunerStatus; +- cMutex mutex; ++ mutable cMutex mutex; + cCondVar locked; + cCondVar newSet; + cDvbTuner *bondedTuner; +@@ -316,6 +316,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(); +@@ -330,6 +334,9 @@ public: + bool Locked(int TimeoutMs = 0); + int GetSignalStrength(void) const; + int GetSignalQuality(void) const; ++ ++ bool SetIdle(bool Idle); ++ bool IsIdle(void) const { return isIdle; } + }; + + cMutex cDvbTuner::bondMutex; +@@ -351,6 +358,7 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int + tunerStatus = tsIdle; + bondedTuner = NULL; + bondedMaster = false; ++ isIdle = false; + SetDescription("tuner on frontend %d/%d", adapter, frontend); + Start(); + } +@@ -368,6 +376,8 @@ cDvbTuner::~cDvbTuner() + ExecuteDiseqc(lastDiseqc, &Frequency); + } + */ ++ if (device && device->IsSubDevice()) ++ CloseFrontend(); + } + + bool cDvbTuner::Bond(cDvbTuner *Tuner) +@@ -512,6 +522,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; +@@ -722,6 +734,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);\ +@@ -868,9 +882,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; + switch (tunerStatus) { +@@ -923,6 +939,40 @@ void cDvbTuner::Action(void) + } + } + ++bool cDvbTuner::SetIdle(bool Idle) ++{ ++ if (isIdle == Idle) ++ return true; ++ isIdle = Idle; ++ if (Idle) ++ return CloseFrontend(); ++ return OpenFrontend(); ++} ++ ++bool cDvbTuner::OpenFrontend(void) 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 { +@@ -1008,7 +1058,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; +@@ -1028,7 +1079,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): + +@@ -1256,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(Device1)) { + if (cDvbDevice *DvbDevice2 = dynamic_cast(Device2)) { + if (!DvbDevice1->Bond(DvbDevice2)) +@@ -1290,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(cDevice::GetDevice(i))) ++ cDevice *dev = cDevice::GetDevice(i); ++ if (dev && dev->HasSubDevice()) ++ dev = dev->SubDevice(); ++ if (cDvbDevice *d = dynamic_cast(dev)) + d->UnBond(); + } + } +@@ -1344,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; +@@ -1513,7 +1591,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 b4d07f5..6f785ec 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,13 +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 DeviceType(void) const; + 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), -- cgit v1.2.3