[PATCH v2] multi-frontend-support for vdr 1.7.21

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

Here's version 2 of my multi-frontend-patch. It's still "dirty", since it changes the constructor of cDvbDevice which will break compilation of some plugins. But I think it might be necessary to look at the relevant plugins since they might need to react on frontend changes. I haven't tested any of those plugins but will have a look at some that I'm using. Maybe there have to be some virtual functions like "BeforeFrontendSwitch" and "AfterFrontendSwitch" so the plugins are even able to know about it.

 Assumption for this patch:
All frontends within one adapter have to be used mutually exclusive. All cards I know behave in this way. If there are cards with multiple frontends which can be used simultaneously I'd like to hear about it.

 Whenever the dvb-api-changes are upstream (the ENUM_DELSYS thingy) I think my patch can easily be converted to use that.

 I'm still working on this patch, it's not finished yet... :-)

 Have fun,

Lars.
diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
index ff3f953..e7fb935 100644
--- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
+++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c
@@ -26,7 +26,8 @@
 int cDvbHdFfDevice::devHdffOffset = -1;
 
 cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend)
-:cDvbDevice(Adapter, Frontend)
+:cDvbDevice(Adapter)
+,frontend(Frontend)
 {
   spuDecoder = NULL;
   audioChannel = 0;
diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
index 4dcfb6a..62540da 100644
--- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
+++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h
@@ -17,12 +17,13 @@
 
 class cDvbHdFfDevice : public cDvbDevice {
 private:
+  int frontend;
   int fd_osd, fd_audio, fd_video;
 protected:
   virtual void MakePrimaryDevice(bool On);
 public:
   static bool Probe(int Adapter, int Frontend);
-  cDvbHdFfDevice(int Adapter, int Frontend);
+  cDvbHdFfDevice(int Adapter, int Frontend = 0);
   virtual ~cDvbHdFfDevice();
   virtual bool HasDecoder(void) const;
 
diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
index 17f842b..68031b5 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
+++ b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c
@@ -24,7 +24,8 @@
 int cDvbSdFfDevice::devVideoOffset = -1;
 
 cDvbSdFfDevice::cDvbSdFfDevice(int Adapter, int Frontend, bool OutputOnly)
-:cDvbDevice(Adapter, Frontend)
+:cDvbDevice(Adapter)
+,frontend(Frontend)
 {
   spuDecoder = NULL;
   digitalAudio = false;
diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h b/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
index bd74cde..c060859 100644
--- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
+++ b/PLUGINS/src/dvbsddevice/dvbsdffdevice.h
@@ -16,6 +16,7 @@
 
 class cDvbSdFfDevice : public cDvbDevice {
 private:
+  int frontend;
   int fd_osd, fd_audio, fd_video, fd_stc;
   bool outputOnly;
 protected:
diff --git a/dvbci.c b/dvbci.c
index 5289bbd..5db673e 100644
--- a/dvbci.c
+++ b/dvbci.c
@@ -10,15 +10,32 @@
 #include "dvbci.h"
 #include <linux/dvb/ca.h>
 #include <sys/ioctl.h>
-#include "device.h"
+#include "dvbdevice.h"
 
 // --- cDvbCiAdapter ---------------------------------------------------------
 
-cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
+cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd, int Adapter, int Ca)
 {
   device = Device;
   SetDescription("CI adapter on device %d", device->DeviceNumber());
   fd = Fd;
+  adapter = Adapter;
+  ca = Ca;
+  gotCaps = false;
+  GetCaps();
+}
+
+cDvbCiAdapter::~cDvbCiAdapter()
+{
+  Cancel(3);
+  CloseCa();
+}
+
+void cDvbCiAdapter::GetCaps(void)
+{
+  if (gotCaps || (fd < 0))
+     return;
+  gotCaps = true;
   ca_caps_t Caps;
   if (ioctl(fd, CA_GET_CAP, &Caps) == 0) {
      if ((Caps.slot_type & CA_CI_LINK) != 0) {
@@ -38,13 +55,31 @@ cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd)
      esyslog("ERROR: can't get CA capabilities on device %d", device->DeviceNumber());
 }
 
-cDvbCiAdapter::~cDvbCiAdapter()
+bool cDvbCiAdapter::OpenCa(void)
 {
-  Cancel(3);
+  if (fd >= 0)
+     return true;
+  fd = cDvbDevice::DvbOpen(DEV_DVB_CA, adapter, ca, O_RDWR);
+  if (fd < 0) {
+     esyslog("ERROR: can't open ca %d/%d", adapter, ca);
+     return false;
+     }
+  GetCaps();
+  return true;
+}
+
+void cDvbCiAdapter::CloseCa(void)
+{
+  if (fd < 0)
+     return;
+  close(fd);
+  fd = -1;
 }
 
 int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
 {
+  if (fd < 0)
+     return 0;
   if (Buffer && MaxLength > 0) {
      struct pollfd pfd[1];
      pfd[0].fd = fd;
@@ -61,6 +96,8 @@ int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength)
 
 void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
 {
+  if (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 +106,8 @@ void cDvbCiAdapter::Write(const uint8_t *Buffer, int Length)
 
 bool cDvbCiAdapter::Reset(int Slot)
 {
+  if (fd < 0)
+     return false;
   if (ioctl(fd, CA_RESET, 1 << Slot) != -1)
      return true;
   else
@@ -78,6 +117,8 @@ bool cDvbCiAdapter::Reset(int Slot)
 
 eModuleStatus cDvbCiAdapter::ModuleStatus(int Slot)
 {
+  if (fd < 0)
+     return msNone;
   ca_slot_info_t sinfo;
   sinfo.num = Slot;
   if (ioctl(fd, CA_GET_SLOT_INFO, &sinfo) != -1) {
@@ -99,10 +140,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 Ca)
 {
   // TODO check whether a CI is actually present?
   if (Device)
-     return new cDvbCiAdapter(Device, Fd);
+     return new cDvbCiAdapter(Device, Fd, Adapter, Ca);
   return NULL;
 }
diff --git a/dvbci.h b/dvbci.h
index adbe40d..0776a7b 100644
--- a/dvbci.h
+++ b/dvbci.h
@@ -16,16 +16,24 @@ class cDvbCiAdapter : public cCiAdapter {
 private:
   cDevice *device;
   int fd;
+  int adapter;
+  int ca;
+  bool gotCaps;
+
+  void GetCaps(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, int Ca);
 public:
   virtual ~cDvbCiAdapter();
-  static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd);
+  bool OpenCa(void);
+  void CloseCa(void);
+  static cDvbCiAdapter *CreateCiAdapter(cDevice *Device, int Fd, int Adapter = -1, int Ca = -1);
   };
 
 #endif //__DVBCI_H
diff --git a/dvbdevice.c b/dvbdevice.c
index a97f274..0e0acd0 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -276,8 +276,9 @@ private:
   bool GetFrontendStatus(fe_status_t &Status) const;
   bool SetFrontend(void);
   virtual void Action(void);
+
 public:
-  cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType);
+  cDvbTuner(int Device, int Adapter, int Frontend, fe_delivery_system FrontendType);
   virtual ~cDvbTuner();
   const cChannel *GetTransponder(void) const { return &channel; }
   uint32_t SubsystemId(void) const { return subsystemId; }
@@ -286,12 +287,15 @@ public:
   bool Locked(int TimeoutMs = 0);
   int GetSignalStrength(void) const;
   int GetSignalQuality(void) const;
+
+  bool OpenFrontend(void);
+  bool CloseFrontend(void);
   };
 
-cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType)
+cDvbTuner::cDvbTuner(int Device, int Adapter, int Frontend, fe_delivery_system FrontendType)
 {
   device = Device;
-  fd_frontend = Fd_Frontend;
+  fd_frontend = -1;
   adapter = Adapter;
   frontend = Frontend;
   frontendType = FrontendType;
@@ -301,10 +305,7 @@ cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_
   lastTimeoutReport = 0;
   diseqcCommands = NULL;
   tunerStatus = tsIdle;
-  if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
-     CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
   SetDescription("tuner on frontend %d/%d", adapter, frontend);
-  Start();
 }
 
 cDvbTuner::~cDvbTuner()
@@ -313,6 +314,7 @@ cDvbTuner::~cDvbTuner()
   newSet.Broadcast();
   locked.Broadcast();
   Cancel(3);
+  CloseFrontend();
 }
 
 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
@@ -349,6 +351,8 @@ bool cDvbTuner::Locked(int TimeoutMs)
 
 void cDvbTuner::ClearEventQueue(void) const
 {
+  if (fd_frontend < 0)
+     return;
   cPoller Poller(fd_frontend);
   if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
      dvb_frontend_event Event;
@@ -359,6 +363,8 @@ void cDvbTuner::ClearEventQueue(void) const
 
 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
 {
+  if (fd_frontend < 0)
+     return false;
   ClearEventQueue();
   while (1) {
         if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
@@ -374,6 +380,8 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
 
 int cDvbTuner::GetSignalStrength(void) const
 {
+  if (fd_frontend < 0)
+     return -1;
   ClearEventQueue();
   uint16_t Signal;
   while (1) {
@@ -401,6 +409,8 @@ int cDvbTuner::GetSignalStrength(void) const
 
 int cDvbTuner::GetSignalQuality(void) const
 {
+  if (fd_frontend < 0)
+     return -1;
   fe_status_t Status;
   if (GetFrontendStatus(Status)) {
      // Actually one would expect these checks to be done from FE_HAS_SIGNAL to FE_HAS_LOCK, but some drivers (like the stb0899) are broken, so FE_HAS_LOCK is the only one that (hopefully) is generally reliable...
@@ -484,6 +494,8 @@ static unsigned int FrequencyToHz(unsigned int f)
 
 bool cDvbTuner::SetFrontend(void)
 {
+  if (fd_frontend < 0)
+     return false;
 #define MAXFRONTENDCMDS 16
 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
                        Frontend[CmdSeq.num].u.data = (d);\
@@ -643,9 +655,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 (fd_frontend >= 0) {
+           fe_status_t NewStatus;
+           if (GetFrontendStatus(NewStatus))
+              Status = NewStatus;
+           }
         cMutexLock MutexLock(&mutex);
         switch (tunerStatus) {
           case tsIdle:
@@ -698,6 +712,37 @@ void cDvbTuner::Action(void)
         }
 }
 
+bool cDvbTuner::OpenFrontend(void)
+{
+  if (fd_frontend >= 0)
+     return true;
+  isyslog("opening frontend %d/%d", adapter, frontend);
+  cMutexLock MutexLock(&mutex);
+  fd_frontend = cDvbDevice::DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
+  if (fd_frontend < 0)
+     return false;
+  if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
+#ifdef LNB_SHARING_VERSION
+     if (lnbSendSignals)
+#endif
+     CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
+  Start();
+  return true;
+}
+
+bool cDvbTuner::CloseFrontend(void)
+{
+  if (fd_frontend < 0)
+     return true;
+  isyslog("closing frontend %d/%d", adapter, frontend);
+  cMutexLock MutexLock(&mutex);
+  tunerStatus = tsIdle;
+  newSet.Broadcast();
+  close(fd_frontend);
+  fd_frontend = -1;
+  return true;
+}
+
 // --- cDvbSourceParam -------------------------------------------------------
 
 class cDvbSourceParam : public cSourceParam {
@@ -778,77 +823,112 @@ const char *DeliverySystems[] = {
   NULL
   };
 
-cDvbDevice::cDvbDevice(int Adapter, int Frontend)
+cDvbDevice::cDvbDevice(int Adapter)
 {
+  numFrontends = 0;
+  currentFrontend = 0;
   adapter = Adapter;
-  frontend = Frontend;
-  ciAdapter = NULL;
-  dvbTuner = NULL;
-  frontendType = SYS_UNDEFINED;
   numProvidedSystems = 0;
-
-  // Devices that are present on all card types:
-
-  int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
-
-  // Common Interface:
-
-  fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
-  if (fd_ca >= 0)
-     ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
-
+  int fd_frontend = -1;
   // The DVR device (will be opened and closed as needed):
-
   fd_dvr = -1;
 
-  // We only check the devices that must be present - the others will be checked before accessing them://XXX
-
-  if (fd_frontend >= 0) {
-     if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) >= 0) {
-        switch (frontendInfo.type) {
-          case FE_QPSK: frontendType = (frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
-          case FE_OFDM: frontendType = SYS_DVBT; break;
-          case FE_QAM:  frontendType = SYS_DVBC_ANNEX_AC; break;
-          case FE_ATSC: frontendType = SYS_ATSC; break;
-          default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend);
-          }
-        }
-     else
-        LOG_ERROR;
-     if (frontendType != SYS_UNDEFINED) {
-        numProvidedSystems++;
-        if (frontendType == SYS_DVBS2)
-           numProvidedSystems++;
-        char Modulations[64];
-        char *p = Modulations;
-        if (frontendInfo.caps & FE_CAN_QPSK)    { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QPSK, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_QAM_16)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_16, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_QAM_32)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_32, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_QAM_64)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_64, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_QAM_128) { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_128, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_QAM_256) { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_256, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_8VSB)    { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(VSB_8, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_16VSB)   { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(VSB_16, ModulationValues)); }
-        if (frontendInfo.caps & FE_CAN_TURBO_FEC){numProvidedSystems++; p += sprintf(p, ",%s", "TURBO_FEC"); }
-        if (p != Modulations)
-           p = Modulations + 1; // skips first ','
-        else
-           p = (char *)"unknown modulations";
-        isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, DeliverySystems[frontendType], p, frontendInfo.name);
-        dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, frontend, frontendType);
-        }
+  for (int f = 0; (numFrontends < MAXDVBFRONTENDS) && Exists(adapter, f); f++) {
+      frontends[numFrontends].frontend = f;
+      frontends[numFrontends].demux = frontends[numFrontends].frontend;
+      frontends[numFrontends].dvr = frontends[numFrontends].frontend;
+      frontends[numFrontends].ca = frontends[numFrontends].frontend;
+      frontends[numFrontends].frontendType = SYS_UNDEFINED;
+      frontends[numFrontends].ciAdapter = NULL;
+      frontends[numFrontends].dvbTuner = NULL;
+
+      // Devices that are present on all card types:
+
+      isyslog("probing frontend %d/%d", adapter, f);
+      fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontends[numFrontends].frontend, O_RDWR | O_NONBLOCK);
+
+      if (fd_frontend >= 0) {
+         // Look for the right devices
+         while ((frontends[numFrontends].demux >= 0) && !Exists(DEV_DVB_DEMUX, adapter, frontends[numFrontends].demux))
+               frontends[numFrontends].demux--;
+         if (frontends[numFrontends].demux < 0) {
+            frontends[numFrontends].demux = frontends[numFrontends].frontend;
+            esyslog("frontend %d/%d has no demux device", adapter, frontends[numFrontends].frontend);
+            }
+         else if (frontends[numFrontends].demux != frontends[numFrontends].frontend)
+            isyslog("frontend %d/%d will use demux%d", adapter, frontends[numFrontends].frontend, frontends[numFrontends].demux);
+
+         while ((frontends[numFrontends].dvr >= 0) && !Exists(DEV_DVB_DVR, adapter, frontends[numFrontends].dvr))
+               frontends[numFrontends].dvr--;
+         if (frontends[numFrontends].dvr < 0) {
+            frontends[numFrontends].dvr = frontends[numFrontends].frontend;
+            esyslog("frontend %d/%d has no dvr device", adapter, frontends[numFrontends].frontend);
+            }
+         else if (frontends[numFrontends].dvr != frontends[numFrontends].frontend)
+            isyslog("frontend %d/%d will use dvr%d", adapter, frontends[numFrontends].frontend, frontends[numFrontends].dvr);
+
+         while ((frontends[numFrontends].ca >= 0) && !Exists(DEV_DVB_CA, adapter, frontends[numFrontends].ca))
+               frontends[numFrontends].ca--;
+         if ((frontends[numFrontends].ca >= 0) && (frontends[numFrontends].ca != frontends[numFrontends].frontend))
+            isyslog("frontend %d/%d will use ca%d", adapter, frontends[numFrontends].frontend, frontends[numFrontends].ca);
+
+         if (ioctl(fd_frontend, FE_GET_INFO, &frontends[numFrontends].frontendInfo) >= 0) {
+            switch (frontends[numFrontends].frontendInfo.type) {
+              case FE_QPSK: frontends[numFrontends].frontendType = (frontends[numFrontends].frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
+              case FE_OFDM: frontends[numFrontends].frontendType = SYS_DVBT; break;
+              case FE_QAM:  frontends[numFrontends].frontendType = SYS_DVBC_ANNEX_AC; break;
+              case FE_ATSC: frontends[numFrontends].frontendType = SYS_ATSC; break;
+              default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontends[numFrontends].frontendInfo.type, adapter, frontends[numFrontends].frontend);
+              }
+            }
+         else
+            LOG_ERROR;
+         if (frontends[numFrontends].frontendType != SYS_UNDEFINED) {
+            numProvidedSystems++;
+            if (frontends[numFrontends].frontendType == SYS_DVBS2)
+               numProvidedSystems++;
+            char Modulations[64];
+            char *p = Modulations;
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QPSK)    { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QPSK, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QAM_16)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_16, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QAM_32)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_32, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QAM_64)  { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_64, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QAM_128) { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_128, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_QAM_256) { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(QAM_256, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_8VSB)    { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(VSB_8, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_16VSB)   { numProvidedSystems++; p += sprintf(p, ",%s", MapToUserString(VSB_16, ModulationValues)); }
+            if (frontends[numFrontends].frontendInfo.caps & FE_CAN_TURBO_FEC){numProvidedSystems++; p += sprintf(p, ",%s", "TURBO_FEC"); }
+            if (p != Modulations)
+               p = Modulations + 1; // skips first ','
+            else
+               p = (char *)"unknown modulations";
+            isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontends[numFrontends].frontend, DeliverySystems[frontends[numFrontends].frontendType], p, frontends[numFrontends].frontendInfo.name);
+            frontends[numFrontends].dvbTuner = new cDvbTuner(CardIndex() + 1, adapter, frontends[numFrontends].frontend, frontends[numFrontends].frontendType);
+            if (frontends[numFrontends].ca >= 0)
+               frontends[numFrontends].ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, -1, adapter, frontends[numFrontends].ca);
+            numFrontends++;
+            }
+         close (fd_frontend);
+         }
+      else
+         esyslog("ERROR: can't open DVB device %d/%d", adapter, frontends[numFrontends].frontend);
+      }
+  if (numFrontends > 0) {
+     if (frontends[currentFrontend].ciAdapter)
+        frontends[currentFrontend].ciAdapter->OpenCa();
+     if (frontends[currentFrontend].dvbTuner)
+        frontends[currentFrontend].dvbTuner->OpenFrontend();
      }
-  else
-     esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend);
-
   StartSectionHandler();
 }
 
 cDvbDevice::~cDvbDevice()
 {
   StopSectionHandler();
-  delete dvbTuner;
-  delete ciAdapter;
+  for (int f = 0; f < numFrontends; f++) {
+      delete frontends[f].dvbTuner;
+      delete frontends[f].ciAdapter;
+      }
   // We're not explicitly closing any device files here, since this sometimes
   // caused segfaults. Besides, the program is about to terminate anyway...
 }
@@ -869,7 +949,12 @@ int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, b
 
 bool cDvbDevice::Exists(int Adapter, int Frontend)
 {
-  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
+  return Exists(DEV_DVB_FRONTEND, Adapter, Frontend);
+}
+
+bool cDvbDevice::Exists(const char *Name, int Adapter, int Frontend)
+{
+  cString FileName = DvbName(Name, Adapter, Frontend);
   if (access(FileName, F_OK) == 0) {
      int f = open(FileName, O_RDONLY);
      if (f >= 0) {
@@ -884,16 +969,16 @@ bool cDvbDevice::Exists(int Adapter, int Frontend)
   return false;
 }
 
-bool cDvbDevice::Probe(int Adapter, int Frontend)
+bool cDvbDevice::Probe(int Adapter)
 {
-  cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend);
-  dsyslog("probing %s", *FileName);
+  cString adapterName = cString::sprintf("%s%d", DEV_DVB_ADAPTER, Adapter);
+  dsyslog("probing %s", *adapterName);
   for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) {
-      if (dp->Probe(Adapter, Frontend))
+      if (dp->Probe(Adapter, 0))
          return true; // a plugin has created the actual device
       }
   dsyslog("creating cDvbDevice");
-  new cDvbDevice(Adapter, Frontend); // it's a "budget" device
+  new cDvbDevice(Adapter); // it's a "budget" device
   return true;
 }
 
@@ -906,23 +991,18 @@ bool cDvbDevice::Initialize(void)
   int Checked = 0;
   int Found = 0;
   for (int Adapter = 0; ; Adapter++) {
-      for (int Frontend = 0; ; Frontend++) {
-          if (Exists(Adapter, Frontend)) {
-             if (Checked++ < MAXDVBDEVICES) {
-                if (UseDevice(NextCardIndex())) {
-                   if (Probe(Adapter, Frontend))
-                      Found++;
-                   }
-                else
-                   NextCardIndex(1); // skips this one
-                }
-             }
-          else if (Frontend == 0)
-             goto LastAdapter;
-          else
-             goto NextAdapter;
-          }
-      NextAdapter: ;
+      if (Exists(DEV_DVB_FRONTEND, Adapter, 0)) {
+         if (Checked++ < MAXDVBDEVICES) {
+            if (UseDevice(NextCardIndex())) {
+               if (Probe(Adapter))
+                  Found++;
+               }
+            else
+               NextCardIndex(1); // skips this one
+            }
+         }
+         else
+            goto LastAdapter;
       }
 LastAdapter:
   NextCardIndex(MAXDVBDEVICES - Checked); // skips the rest
@@ -935,14 +1015,14 @@ LastAdapter:
 
 bool cDvbDevice::Ready(void)
 {
-  if (ciAdapter)
-     return ciAdapter->Ready();
+  if (frontends[currentFrontend].ciAdapter)
+     return frontends[currentFrontend].ciAdapter->Ready();
   return true;
 }
 
 bool cDvbDevice::HasCi(void)
 {
-  return ciAdapter;
+  return frontends[currentFrontend].ciAdapter;
 }
 
 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
@@ -952,7 +1032,7 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
      memset(&pesFilterParams, 0, sizeof(pesFilterParams));
      if (On) {
         if (Handle->handle < 0) {
-           Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true);
+           Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontends[currentFrontend].demux, O_RDWR | O_NONBLOCK, true);
            if (Handle->handle < 0) {
               LOG_ERROR;
               return false;
@@ -987,7 +1067,7 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
 
 int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
 {
-  cString FileName = DvbName(DEV_DVB_DEMUX, adapter, frontend);
+  cString FileName = DvbName(DEV_DVB_DEMUX, adapter, frontends[currentFrontend].demux);
   int f = open(FileName, O_RDWR | O_NONBLOCK);
   if (f >= 0) {
      dmx_sct_filter_params sctFilterParams;
@@ -1014,32 +1094,43 @@ void cDvbDevice::CloseFilter(int Handle)
   close(Handle);
 }
 
-bool cDvbDevice::ProvidesSource(int Source) const
+int cDvbDevice::GetFrontend(int Source) const
 {
   int type = Source & cSource::st_Mask;
-  return type == cSource::stNone
-      || type == cSource::stAtsc  && (frontendType == SYS_ATSC)
-      || type == cSource::stCable && (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B)
-      || type == cSource::stSat   && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
-      || type == cSource::stTerr  && (frontendType == SYS_DVBT);
+  if (type == cSource::stNone)
+     return 0; // can this happen?
+  for (int f = 0; f < numFrontends; f++) {
+      if (type == cSource::stAtsc  && (frontends[f].frontendType == SYS_ATSC)
+          || type == cSource::stCable && (frontends[f].frontendType == SYS_DVBC_ANNEX_AC || frontends[f].frontendType == SYS_DVBC_ANNEX_B)
+          || type == cSource::stSat   && (frontends[f].frontendType == SYS_DVBS || frontends[f].frontendType == SYS_DVBS2)
+          || type == cSource::stTerr  && (frontends[f].frontendType == SYS_DVBT))
+         return f;
+      }
+  return -1;
+}
+
+bool cDvbDevice::ProvidesSource(int Source) const
+{
+  return (GetFrontend(Source) >= 0);
 }
 
 bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
 {
-  if (!ProvidesSource(Channel->Source()))
+  int f = GetFrontend(Channel->Source());
+  if (f < 0)
      return false; // doesn't provide source
   cDvbTransponderParameters dtp(Channel->Parameters());
-  if (dtp.System() == SYS_DVBS2 && frontendType == SYS_DVBS ||
-     dtp.Modulation() == QPSK     && !(frontendInfo.caps & FE_CAN_QPSK) ||
-     dtp.Modulation() == QAM_16   && !(frontendInfo.caps & FE_CAN_QAM_16) ||
-     dtp.Modulation() == QAM_32   && !(frontendInfo.caps & FE_CAN_QAM_32) ||
-     dtp.Modulation() == QAM_64   && !(frontendInfo.caps & FE_CAN_QAM_64) ||
-     dtp.Modulation() == QAM_128  && !(frontendInfo.caps & FE_CAN_QAM_128) ||
-     dtp.Modulation() == QAM_256  && !(frontendInfo.caps & FE_CAN_QAM_256) ||
-     dtp.Modulation() == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) ||
-     dtp.Modulation() == VSB_8    && !(frontendInfo.caps & FE_CAN_8VSB) ||
-     dtp.Modulation() == VSB_16   && !(frontendInfo.caps & FE_CAN_16VSB) ||
-     dtp.Modulation() == PSK_8    && !(frontendInfo.caps & FE_CAN_TURBO_FEC) && dtp.System() == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to determine this condition
+  if (dtp.System() == SYS_DVBS2 && frontends[f].frontendType == SYS_DVBS ||
+     dtp.Modulation() == QPSK     && !(frontends[f].frontendInfo.caps & FE_CAN_QPSK) ||
+     dtp.Modulation() == QAM_16   && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_16) ||
+     dtp.Modulation() == QAM_32   && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_32) ||
+     dtp.Modulation() == QAM_64   && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_64) ||
+     dtp.Modulation() == QAM_128  && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_128) ||
+     dtp.Modulation() == QAM_256  && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_256) ||
+     dtp.Modulation() == QAM_AUTO && !(frontends[f].frontendInfo.caps & FE_CAN_QAM_AUTO) ||
+     dtp.Modulation() == VSB_8    && !(frontends[f].frontendInfo.caps & FE_CAN_8VSB) ||
+     dtp.Modulation() == VSB_16   && !(frontends[f].frontendInfo.caps & FE_CAN_16VSB) ||
+     dtp.Modulation() == PSK_8    && !(frontends[f].frontendInfo.caps & FE_CAN_TURBO_FEC) && dtp.System() == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to determine this condition
      return false; // requires modulation system which frontend doesn't provide
   if (!cSource::IsSat(Channel->Source()) ||
      !Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization()))
@@ -1053,10 +1144,10 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
   bool hasPriority = Priority < 0 || Priority > this->Priority();
   bool needsDetachReceivers = false;
 
-  if (dvbTuner && ProvidesTransponder(Channel)) {
+  if (frontends[currentFrontend].dvbTuner && ProvidesTransponder(Channel)) {
      result = hasPriority;
      if (Priority >= 0 && Receiving(true)) {
-        if (dvbTuner->IsTunedTo(Channel)) {
+        if (frontends[currentFrontend].dvbTuner->IsTunedTo(Channel)) {
            if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
               if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
                  if (CamSlot()->CanDecrypt(Channel))
@@ -1083,7 +1174,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
 
 bool cDvbDevice::ProvidesEIT(void) const
 {
-  return dvbTuner != NULL;
+  return frontends[currentFrontend].dvbTuner != NULL;
 }
 
 int cDvbDevice::NumProvidedSystems(void) const
@@ -1093,34 +1184,64 @@ int cDvbDevice::NumProvidedSystems(void) const
 
 int cDvbDevice::SignalStrength(void) const
 {
-  return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
+  return frontends[currentFrontend].dvbTuner ? frontends[currentFrontend].dvbTuner->GetSignalStrength() : -1;
 }
 
 int cDvbDevice::SignalQuality(void) const
 {
-  return dvbTuner ? dvbTuner->GetSignalQuality() : -1;
+  return frontends[currentFrontend].dvbTuner ? frontends[currentFrontend].dvbTuner->GetSignalQuality() : -1;
 }
 
 const cChannel *cDvbDevice::GetCurrentlyTunedTransponder(void) const
 {
-  return dvbTuner ? dvbTuner->GetTransponder() : NULL;
+  return frontends[currentFrontend].dvbTuner ? frontends[currentFrontend].dvbTuner->GetTransponder() : NULL;
 }
 
 bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
 {
-  return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
+  return frontends[currentFrontend].dvbTuner ? frontends[currentFrontend].dvbTuner->IsTunedTo(Channel) : false;
 }
 
 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
 {
-  if (dvbTuner)
-     dvbTuner->Set(Channel);
+  if (numFrontends > 1) {
+     int f = GetFrontend(Channel->Source());
+     if (f < 0)
+        return false;
+     if (currentFrontend != f) {
+        isyslog("switching frontend on adapter %d from %d to %d", adapter, frontends[currentFrontend].frontend, frontends[f].frontend);
+        StopSectionHandler();
+        if (frontends[currentFrontend].dvbTuner)
+           frontends[currentFrontend].dvbTuner->CloseFrontend();
+        if (frontends[currentFrontend].ciAdapter)
+           frontends[currentFrontend].ciAdapter->CloseCa();
+        currentFrontend = f;
+        if (frontends[currentFrontend].ciAdapter) {
+           frontends[currentFrontend].ciAdapter->OpenCa();
+           bool ready = false;
+           for (time_t t0 = time(NULL); time(NULL) - t0 < 10; ) {
+               if (frontends[currentFrontend].ciAdapter->Ready()) {
+                  ready = true;
+                  break;
+                  }
+               cCondWait::SleepMs(100);
+               }
+           if (!ready)
+              return false;
+           }
+        if (frontends[currentFrontend].dvbTuner)
+           frontends[currentFrontend].dvbTuner->OpenFrontend();
+        StartSectionHandler();
+        }
+     }
+  if (frontends[currentFrontend].dvbTuner)
+     frontends[currentFrontend].dvbTuner->Set(Channel);
   return true;
 }
 
 bool cDvbDevice::HasLock(int TimeoutMs)
 {
-  return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
+  return frontends[currentFrontend].dvbTuner ? frontends[currentFrontend].dvbTuner->Locked(TimeoutMs) : false;
 }
 
 void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
@@ -1131,7 +1252,7 @@ void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
 bool cDvbDevice::OpenDvr(void)
 {
   CloseDvr();
-  fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true);
+  fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontends[currentFrontend].dvr, O_RDONLY | O_NONBLOCK, true);
   if (fd_dvr >= 0)
      tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1);
   return fd_dvr >= 0;
diff --git a/dvbdevice.h b/dvbdevice.h
index e1842b7..1de9a0e 100644
--- a/dvbdevice.h
+++ b/dvbdevice.h
@@ -20,6 +20,8 @@
 #endif
 
 #define MAXDVBDEVICES  8
+#define MAXDVBFRONTENDS 8
+#define MULTI_FRONTEND_PATCH
 
 #define DEV_VIDEO         "/dev/video"
 #define DEV_DVB_ADAPTER   "/dev/dvb/adapter"
@@ -97,18 +99,33 @@ public:
   bool Parse(const char *s);
   };
 
+class cDvbCiAdapter;
 class cDvbTuner;
 
 /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
 
+struct tDvbFrontend {
+  int frontend;
+  int demux;
+  int dvr;
+  int ca;
+
+  dvb_frontend_info frontendInfo;
+  fe_delivery_system frontendType;
+
+  cDvbCiAdapter *ciAdapter;
+  cDvbTuner *dvbTuner;
+  };
+
 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:
   static bool Exists(int Adapter, int Frontend);
+  static bool Exists(const char *Name, int Adapter, int Frontend);
          ///< Checks whether the given adapter/frontend exists.
-  static bool Probe(int Adapter, int Frontend);
+  static bool Probe(int Adapter);
          ///< Probes for existing DVB devices.
 public:
   static bool Initialize(void);
@@ -116,26 +133,20 @@ public:
          ///< Must be called before accessing any DVB functions.
          ///< \return True if any devices are available.
 protected:
-  int adapter, frontend;
+  int adapter;
 private:
-  dvb_frontend_info frontendInfo;
+  tDvbFrontend frontends[MAXDVBFRONTENDS];
+  int numFrontends;
+  int currentFrontend;
+  int GetFrontend(int Source) const;
+
   int numProvidedSystems;
-  fe_delivery_system frontendType;
-  int fd_dvr, fd_ca;
+  int fd_dvr;
 public:
-  cDvbDevice(int Adapter, int Frontend);
+  cDvbDevice(int Adapter);
   virtual ~cDvbDevice();
   virtual bool Ready(void);
 
-// Common Interface facilities:
-
-private:
-  cCiAdapter *ciAdapter;
-
-// Channel facilities
-
-private:
-  cDvbTuner *dvbTuner;
 public:
   virtual bool ProvidesSource(int Source) const;
   virtual bool ProvidesTransponder(const cChannel *Channel) const;
_______________________________________________
vdr mailing list
vdr@xxxxxxxxxxx
http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr

[Index of Archives]     [Linux Media]     [Asterisk]     [DCCP]     [Netdev]     [Xorg]     [Util Linux NG]     [Xfree86]     [Big List of Linux Books]     [Fedora Users]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux