@@ -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;
@@ -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;
@@ -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;
@@ -16,6 +16,7 @@
class cDvbSdFfDevice : public cDvbDevice {
private:
+ int frontend;
int fd_osd, fd_audio, fd_video, fd_stc;
bool outputOnly;
protected:
@@ -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;
}
@@ -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
@@ -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;
@@ -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;