1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Added support for DVB devices with more than one frontend that all use the same dvr and demux

This commit is contained in:
Klaus Schmidinger 2018-10-29 12:16:35 +01:00
parent bcee8ad43d
commit cbc77f1f25
3 changed files with 228 additions and 94 deletions

View File

@ -9348,7 +9348,7 @@ Video Disk Recorder Revision History
Senzel). Senzel).
- Official release. - Official release.
2018-09-23: Version 2.4.1 2018-10-29: Version 2.4.1
- Fixed handling the tfRecording flag in the SVDRP commands MODT and UPDT (reported - Fixed handling the tfRecording flag in the SVDRP commands MODT and UPDT (reported
by Johann Friedrichs). by Johann Friedrichs).
@ -9367,3 +9367,7 @@ Video Disk Recorder Revision History
Binder). Binder).
- Now deactivating MTD support if a non MCD capable CAM is inserted after removing - Now deactivating MTD support if a non MCD capable CAM is inserted after removing
a previously used CAM that is MCD capable (thanks to Helmut Binder). a previously used CAM that is MCD capable (thanks to Helmut Binder).
- Added support for DVB devices with more than one frontend that all use the same
dvr and demux. Note that in order for this to work, you must not set symbolic
links like "demux1 -> demux0" and "dvr1 -> dvr0", as is mentioned in some user
manuals of multi frontend DVB cards.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbdevice.c 4.16 2018/02/15 15:37:01 kls Exp $ * $Id: dvbdevice.c 4.17 2018/10/29 10:40:34 kls Exp $
*/ */
#include "dvbdevice.h" #include "dvbdevice.h"
@ -293,6 +293,90 @@ bool cDvbTransponderParameters::Parse(const char *s)
return true; return true;
} }
// --- cDvbFrontend ----------------------------------------------------------
class cDvbFrontend {
private:
int adapter, frontend;
int fd_frontend;
uint32_t subsystemId;
dvb_frontend_info frontendInfo;
cVector<int> deliverySystems;
int numModulations;
bool QueryDeliverySystems(void);
public:
cDvbFrontend(int Adapter, int Frontend);
~cDvbFrontend();
int Open(void);
void Close(void);
const char *FrontendName(void) { return frontendInfo.name; }
bool ProvidesDeliverySystem(int DeliverySystem) const;
bool ProvidesModulation(int System, int StreamId, int Modulation) const;
int NumDeliverySystems(void) const { return deliverySystems.Size(); }
int NumModulations(void) const { return numModulations; }
uint32_t SubsystemId(void) const { return subsystemId; }
};
cDvbFrontend::cDvbFrontend(int Adapter, int Frontend)
{
adapter = Adapter;
frontend = Frontend;
fd_frontend = -1;
subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend);
numModulations = 0;
Open();
QueryDeliverySystems();
Close();
}
cDvbFrontend::~cDvbFrontend()
{
Close();
}
int cDvbFrontend::Open(void)
{
Close();
fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK, true);
return fd_frontend;
}
void cDvbFrontend::Close(void)
{
if (fd_frontend >= 0) {
if (close(fd_frontend) != 0)
esyslog("ERROR: frontend %d/%d", adapter, frontend);
fd_frontend = -1;
}
}
bool cDvbFrontend::ProvidesDeliverySystem(int DeliverySystem) const
{
for (int i = 0; i < deliverySystems.Size(); i++) {
if (deliverySystems[i] == DeliverySystem)
return true;
}
return false;
}
bool cDvbFrontend::ProvidesModulation(int System, int StreamId, int Modulation) const
{
if (StreamId != 0 && !(frontendInfo.caps & FE_CAN_MULTISTREAM))
return false;
if (Modulation == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) ||
Modulation == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) ||
Modulation == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) ||
Modulation == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) ||
Modulation == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) ||
Modulation == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) ||
Modulation == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) ||
Modulation == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) ||
Modulation == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) ||
Modulation == PSK_8 && !(frontendInfo.caps & FE_CAN_TURBO_FEC) && System == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to de
return false;
return true;
}
// --- cDvbTuner ------------------------------------------------------------- // --- cDvbTuner -------------------------------------------------------------
#define TUNER_POLL_TIMEOUT 10 // ms #define TUNER_POLL_TIMEOUT 10 // ms
@ -303,9 +387,13 @@ private:
enum eTunerStatus { tsIdle, tsSet, tsPositioning, tsTuned, tsLocked }; enum eTunerStatus { tsIdle, tsSet, tsPositioning, tsTuned, tsLocked };
int frontendType; int frontendType;
const cDvbDevice *device; const cDvbDevice *device;
int fd_frontend; mutable int fd_frontend;
int adapter, frontend; int adapter;
uint32_t subsystemId; mutable int frontend;
cVector<cDvbFrontend *> dvbFrontends;
mutable cDvbFrontend *dvbFrontend;
int numDeliverySystems;
int numModulations;
int tuneTimeout; int tuneTimeout;
int lockTimeout; int lockTimeout;
time_t lastTimeoutReport; time_t lastTimeoutReport;
@ -320,7 +408,7 @@ private:
const cScr *scr; const cScr *scr;
bool lnbPowerTurnedOn; bool lnbPowerTurnedOn;
eTunerStatus tunerStatus; eTunerStatus tunerStatus;
cMutex mutex; mutable cMutex mutex;
cCondVar locked; cCondVar locked;
cCondVar newSet; cCondVar newSet;
cDvbTuner *bondedTuner; cDvbTuner *bondedTuner;
@ -337,14 +425,19 @@ private:
bool SetFrontend(void); bool SetFrontend(void);
virtual void Action(void); virtual void Action(void);
public: public:
cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend); cDvbTuner(const cDvbDevice *Device, int Adapter, int Frontend);
virtual ~cDvbTuner(); virtual ~cDvbTuner();
bool ProvidesDeliverySystem(int DeliverySystem) const;
bool ProvidesModulation(int System, int StreamId, int Modulation) const;
bool ProvidesFrontend(const cChannel *Channel, bool Activate = false) const;
int FrontendType(void) const { return frontendType; } int FrontendType(void) const { return frontendType; }
const char *FrontendName(void) { return dvbFrontend->FrontendName(); }
int NumProvidedSystems(void) const { return numDeliverySystems + numModulations; }
bool Bond(cDvbTuner *Tuner); bool Bond(cDvbTuner *Tuner);
void UnBond(void); void UnBond(void);
bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const; bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const;
const cChannel *GetTransponder(void) const { return &channel; } const cChannel *GetTransponder(void) const { return &channel; }
uint32_t SubsystemId(void) const { return subsystemId; } uint32_t SubsystemId(void) const { return dvbFrontend->SubsystemId(); }
bool IsTunedTo(const cChannel *Channel) const; bool IsTunedTo(const cChannel *Channel) const;
void SetChannel(const cChannel *Channel); void SetChannel(const cChannel *Channel);
bool Locked(int TimeoutMs = 0); bool Locked(int TimeoutMs = 0);
@ -356,14 +449,14 @@ public:
cMutex cDvbTuner::bondMutex; cMutex cDvbTuner::bondMutex;
cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend) cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Adapter, int Frontend)
{ {
frontendType = SYS_UNDEFINED; frontendType = SYS_UNDEFINED;
device = Device; device = Device;
fd_frontend = Fd_Frontend; fd_frontend = -1;
adapter = Adapter; adapter = Adapter;
frontend = Frontend; frontend = Frontend;
subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); dvbFrontend = NULL;
tuneTimeout = 0; tuneTimeout = 0;
lockTimeout = 0; lockTimeout = 0;
lastTimeoutReport = 0; lastTimeoutReport = 0;
@ -379,7 +472,31 @@ cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int
tunerStatus = tsIdle; tunerStatus = tsIdle;
bondedTuner = NULL; bondedTuner = NULL;
bondedMaster = false; bondedMaster = false;
SetDescription("frontend %d/%d tuner", adapter, frontend); cDvbFrontend *fe = new cDvbFrontend(adapter, frontend);
dvbFrontends.Append(fe);
numDeliverySystems = fe->NumDeliverySystems();
numModulations = fe->NumModulations();
cString FrontendNumbers = cString::sprintf("%d", frontend);
// Check for multiple frontends:
if (frontend == 0) {
for (int i = 1; ; i++) {
if (access(DvbName(DEV_DVB_FRONTEND, adapter, i), F_OK) == 0) {
if (access(DvbName(DEV_DVB_DEMUX, adapter, i), F_OK) != 0) {
fe = new cDvbFrontend(adapter, i);
dvbFrontends.Append(fe);
numDeliverySystems += fe->NumDeliverySystems();
//numModulations += fe->NumModulations(); // looks like in multi frontend devices all frontends report the same modulations
FrontendNumbers = cString::sprintf("%s+%d", *FrontendNumbers, i);
}
}
else
break;
}
}
// Open default frontend:
dvbFrontend = dvbFrontends[0];
fd_frontend = dvbFrontend->Open();
SetDescription("frontend %d/%s tuner", adapter, *FrontendNumbers);
Start(); Start();
} }
@ -396,6 +513,51 @@ cDvbTuner::~cDvbTuner()
ExecuteDiseqc(lastDiseqc, &Frequency); ExecuteDiseqc(lastDiseqc, &Frequency);
} }
*/ */
for (int i = 0; i < dvbFrontends.Size(); i++)
delete dvbFrontends[i];
}
bool cDvbTuner::ProvidesDeliverySystem(int DeliverySystem) const
{
for (int i = 0; i < dvbFrontends.Size(); i++) {
if (dvbFrontends[i]->ProvidesDeliverySystem(DeliverySystem))
return true;
}
return false;
}
bool cDvbTuner::ProvidesModulation(int System, int StreamId, int Modulation) const
{
for (int i = 0; i < dvbFrontends.Size(); i++) {
if (dvbFrontends[i]->ProvidesModulation(System, StreamId, Modulation))
return true;
}
return false;
}
static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp);//TODO
bool cDvbTuner::ProvidesFrontend(const cChannel *Channel, bool Activate) const
{
cDvbTransponderParameters dtp(Channel->Parameters());
int DeliverySystem = GetRequiredDeliverySystem(Channel, &dtp);
for (int i = 0; i < dvbFrontends.Size(); i++) {
if (dvbFrontends[i]->ProvidesDeliverySystem(DeliverySystem) && dvbFrontends[i]->ProvidesModulation(dtp.System(), dtp.StreamId(), dtp.Modulation())) {
if (Activate && dvbFrontend != dvbFrontends[i]) {
cMutexLock MutexLock(&mutex);
dvbFrontend->Close();
dvbFrontend = dvbFrontends[i];
fd_frontend = dvbFrontend->Open();
frontend = i;
dsyslog("using frontend %d/%d", adapter, frontend);
lastUncValue = 0;
lastUncDelta = 0;
lastUncChange = 0;
}
return true;
}
}
return false;
} }
bool cDvbTuner::Bond(cDvbTuner *Tuner) bool cDvbTuner::Bond(cDvbTuner *Tuner)
@ -585,7 +747,7 @@ bool cDvbTuner::GetSignalStats(int &Valid, double *Strength, double *Cnr, double
CmdSeq.props = Props; CmdSeq.props = Props;
Valid = DTV_STAT_VALID_NONE; Valid = DTV_STAT_VALID_NONE;
if (ioctl(fd_frontend, FE_READ_STATUS, &FeStatus) != 0) { if (ioctl(fd_frontend, FE_READ_STATUS, &FeStatus) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return false; return false;
} }
if (Status) { if (Status) {
@ -606,7 +768,7 @@ bool cDvbTuner::GetSignalStats(int &Valid, double *Strength, double *Cnr, double
if (Per) { SETCMD(DTV_STAT_ERROR_BLOCK_COUNT, 0); if (Per) { SETCMD(DTV_STAT_ERROR_BLOCK_COUNT, 0);
SETCMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0); } SETCMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0); }
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) { if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return false; return false;
} }
int i = 0; int i = 0;
@ -939,7 +1101,7 @@ int cDvbTuner::GetSignalStrength(void) const
SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
SETCMD(DTV_INNER_FEC, 0); SETCMD(DTV_INNER_FEC, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) { if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return -1; return -1;
} }
int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1; int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
@ -973,7 +1135,7 @@ int cDvbTuner::GetSignalStrength(void) const
uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range. uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
// Use the subsystemId to identify individual devices in case they need // Use the subsystemId to identify individual devices in case they need
// special treatment to map their Signal value into the range 0...0xFFFF. // special treatment to map their Signal value into the range 0...0xFFFF.
switch (subsystemId) { switch (dvbFrontend->SubsystemId()) {
case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2) case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2) case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
MaxSignal = 670; break; MaxSignal = 670; break;
@ -982,7 +1144,7 @@ int cDvbTuner::GetSignalStrength(void) const
if (s > 100) if (s > 100)
s = 100; s = 100;
#ifdef DEBUG_SIGNALSTRENGTH #ifdef DEBUG_SIGNALSTRENGTH
fprintf(stderr, "FE %d/%d: API3 %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s); fprintf(stderr, "FE %d/%d: API3 %08X S = %04X %04X %3d%%\n", adapter, frontend, dvbFrontend->SubsystemId(), MaxSignal, Signal, s);
#endif #endif
return s; return s;
} }
@ -1005,7 +1167,7 @@ int cDvbTuner::GetSignalQuality(void) const
SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0); SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0);
SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0); SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) { if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return -1; return -1;
} }
int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1; int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
@ -1132,7 +1294,7 @@ int cDvbTuner::GetSignalQuality(void) const
uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range. uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range.
// Use the subsystemId to identify individual devices in case they need // Use the subsystemId to identify individual devices in case they need
// special treatment to map their Snr value into the range 0...0xFFFF. // special treatment to map their Snr value into the range 0...0xFFFF.
switch (subsystemId) { switch (dvbFrontend->SubsystemId()) {
case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2) case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2)
case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2) case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
if (frontendType == SYS_DVBS2) { if (frontendType == SYS_DVBS2) {
@ -1154,7 +1316,7 @@ int cDvbTuner::GetSignalQuality(void) const
if (q > 100) if (q > 100)
q = 100; q = 100;
#ifdef DEBUG_SIGNALQUALITY #ifdef DEBUG_SIGNALQUALITY
fprintf(stderr, "FE %d/%d: API3 %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q); fprintf(stderr, "FE %d/%d: API3 %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, dvbFrontend->SubsystemId(), MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q);
#endif #endif
return q; return q;
} }
@ -1265,7 +1427,7 @@ bool cDvbTuner::SetFrontend(void)
CmdSeq.props = Props; CmdSeq.props = Props;
SETCMD(DTV_CLEAR, 0); SETCMD(DTV_CLEAR, 0);
if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) { if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return false; return false;
} }
CmdSeq.num = 0; CmdSeq.num = 0;
@ -1390,7 +1552,7 @@ bool cDvbTuner::SetFrontend(void)
} }
SETCMD(DTV_TUNE, 0); SETCMD(DTV_TUNE, 0);
if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) { if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
return false; return false;
} }
return true; return true;
@ -1402,11 +1564,11 @@ void cDvbTuner::Action(void)
bool LostLock = false; bool LostLock = false;
fe_status_t Status = (fe_status_t)0; fe_status_t Status = (fe_status_t)0;
while (Running()) { while (Running()) {
int WaitTime = 1000;
fe_status_t NewStatus; fe_status_t NewStatus;
if (GetFrontendStatus(NewStatus)) if (GetFrontendStatus(NewStatus))
Status = NewStatus; Status = NewStatus;
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
int WaitTime = 1000;
switch (tunerStatus) { switch (tunerStatus) {
case tsIdle: case tsIdle:
break; // we want the TimedWait() below! break; // we want the TimedWait() below!
@ -1542,7 +1704,7 @@ int cDvbDevice::setTransferModeForDolbyDigital = 1;
cMutex cDvbDevice::bondMutex; cMutex cDvbDevice::bondMutex;
const char *DeliverySystemNames[] = { const char *DeliverySystemNames[] = {
"", "???",
"DVB-C", "DVB-C",
"DVB-C", "DVB-C",
"DVB-T", "DVB-T",
@ -1580,16 +1742,10 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend)
frontend = Frontend; frontend = Frontend;
ciAdapter = NULL; ciAdapter = NULL;
dvbTuner = NULL; dvbTuner = NULL;
numDeliverySystems = 0;
numModulations = 0;
bondedDevice = NULL; bondedDevice = NULL;
needsDetachBondedReceivers = false; needsDetachBondedReceivers = false;
tsBuffer = NULL; tsBuffer = NULL;
// Devices that are present on all card types:
int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK);
// Common Interface: // Common Interface:
fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR);
@ -1603,12 +1759,7 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend)
// We only check the devices that must be present - the others will be checked before accessing them://XXX // We only check the devices that must be present - the others will be checked before accessing them://XXX
if (fd_frontend >= 0) { dvbTuner = new cDvbTuner(this, adapter, frontend);
if (QueryDeliverySystems(fd_frontend))
dvbTuner = new cDvbTuner(this, fd_frontend, adapter, frontend);
}
else
esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend);
StartSectionHandler(); StartSectionHandler();
} }
@ -1623,12 +1774,12 @@ cDvbDevice::~cDvbDevice()
// caused segfaults. Besides, the program is about to terminate anyway... // caused segfaults. Besides, the program is about to terminate anyway...
} }
cString cDvbDevice::DvbName(const char *Name, int Adapter, int Frontend) cString DvbName(const char *Name, int Adapter, int Frontend)
{ {
return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend); return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend);
} }
int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError) int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError)
{ {
cString FileName = DvbName(Name, Adapter, Frontend); cString FileName = DvbName(Name, Adapter, Frontend);
int fd = open(FileName, Mode); int fd = open(FileName, Mode);
@ -1669,18 +1820,16 @@ bool cDvbDevice::Probe(int Adapter, int Frontend)
cString cDvbDevice::DeviceType(void) const cString cDvbDevice::DeviceType(void) const
{ {
if (dvbTuner) { if (dvbTuner)
if (dvbTuner->FrontendType() != SYS_UNDEFINED)
return GetDeliverySystemName(dvbTuner->FrontendType()); return GetDeliverySystemName(dvbTuner->FrontendType());
if (numDeliverySystems)
return GetDeliverySystemName(deliverySystems[0]); // to have some reasonable default
}
return ""; return "";
} }
cString cDvbDevice::DeviceName(void) const cString cDvbDevice::DeviceName(void) const
{ {
return frontendInfo.name; if (dvbTuner)
return dvbTuner->FrontendName();
return "";
} }
bool cDvbDevice::Initialize(void) bool cDvbDevice::Initialize(void)
@ -1702,6 +1851,7 @@ bool cDvbDevice::Initialize(void)
while ((f = AdapterDir.Next()) != NULL) { while ((f = AdapterDir.Next()) != NULL) {
if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) { if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) {
int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10); int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10);
if (access(DvbName(DEV_DVB_DEMUX, Adapter, Frontend), F_OK) == 0) // we only create devices for actual demuxes
Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend))); Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend)));
} }
} }
@ -1741,9 +1891,11 @@ bool cDvbDevice::Initialize(void)
return Found > 0; return Found > 0;
} }
bool cDvbDevice::QueryDeliverySystems(int fd_frontend) //TODO move this up to cDvbFrontend later (leaving it here for now to keep the diff small)
bool cDvbFrontend::QueryDeliverySystems(void)
{ {
numDeliverySystems = 0; deliverySystems.Clear();
numModulations = 0;
if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) { if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) {
LOG_ERROR; LOG_ERROR;
return false; return false;
@ -1773,11 +1925,9 @@ bool cDvbDevice::QueryDeliverySystems(int fd_frontend)
int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq); int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
if (Result == 0) { if (Result == 0) {
for (uint i = 0; i < Props[0].u.buffer.len; i++) { for (uint i = 0; i < Props[0].u.buffer.len; i++) {
if (numDeliverySystems >= MAXDELIVERYSYSTEMS) { // activate this line to simulate a multi-frontend device if you only have a single-frontend device with DVB-S and DVB-S2:
esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend); //if (frontend == 0 && Props[0].u.buffer.data[i] != SYS_DVBS || frontend == 1 && Props[0].u.buffer.data[i] != SYS_DVBS2)
break; deliverySystems.Append(Props[0].u.buffer.data[i]);
}
deliverySystems[numDeliverySystems++] = Props[0].u.buffer.data[i];
} }
LegacyMode = false; LegacyMode = false;
} }
@ -1788,22 +1938,22 @@ bool cDvbDevice::QueryDeliverySystems(int fd_frontend)
if (LegacyMode) { if (LegacyMode) {
// Legacy mode (DVB-API < 5.5): // Legacy mode (DVB-API < 5.5):
switch (frontendInfo.type) { switch (frontendInfo.type) {
case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS; case FE_QPSK: deliverySystems.Append(SYS_DVBS);
if (frontendInfo.caps & FE_CAN_2G_MODULATION) if (frontendInfo.caps & FE_CAN_2G_MODULATION)
deliverySystems[numDeliverySystems++] = SYS_DVBS2; deliverySystems.Append(SYS_DVBS2);
break; break;
case FE_OFDM: deliverySystems[numDeliverySystems++] = SYS_DVBT; case FE_OFDM: deliverySystems.Append(SYS_DVBT);
if (frontendInfo.caps & FE_CAN_2G_MODULATION) if (frontendInfo.caps & FE_CAN_2G_MODULATION)
deliverySystems[numDeliverySystems++] = SYS_DVBT2; deliverySystems.Append(SYS_DVBT2);
break; break;
case FE_QAM: deliverySystems[numDeliverySystems++] = SYS_DVBC_ANNEX_AC; break; case FE_QAM: deliverySystems.Append(SYS_DVBC_ANNEX_AC); break;
case FE_ATSC: deliverySystems[numDeliverySystems++] = SYS_ATSC; break; case FE_ATSC: deliverySystems.Append(SYS_ATSC); break;
default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend); default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend);
} }
} }
if (numDeliverySystems > 0) { if (deliverySystems.Size() > 0) {
cString ds(""); cString ds("");
for (int i = 0; i < numDeliverySystems; i++) for (int i = 0; i < deliverySystems.Size(); i++)
ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", GetDeliverySystemName(deliverySystems[i])); ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", GetDeliverySystemName(deliverySystems[i]));
cString ms(""); cString ms("");
if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); } if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); }
@ -1999,11 +2149,7 @@ void cDvbDevice::CloseFilter(int Handle)
bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const
{ {
for (int i = 0; i < numDeliverySystems; i++) { return dvbTuner->ProvidesDeliverySystem(DeliverySystem);
if (deliverySystems[i] == DeliverySystem)
return true;
}
return false;
} }
bool cDvbDevice::ProvidesSource(int Source) const bool cDvbDevice::ProvidesSource(int Source) const
@ -2020,20 +2166,9 @@ bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
{ {
if (!ProvidesSource(Channel->Source())) if (!ProvidesSource(Channel->Source()))
return false; // doesn't provide source return false; // doesn't provide source
cDvbTransponderParameters dtp(Channel->Parameters()); if (!dvbTuner->ProvidesFrontend(Channel))
if (!ProvidesDeliverySystem(GetRequiredDeliverySystem(Channel, &dtp)) ||
dtp.StreamId() != 0 && !(frontendInfo.caps & FE_CAN_MULTISTREAM) ||
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
return false; // requires modulation system which frontend doesn't provide return false; // requires modulation system which frontend doesn't provide
cDvbTransponderParameters dtp(Channel->Parameters());
if (!cSource::IsSat(Channel->Source()) || if (!cSource::IsSat(Channel->Source()) ||
(!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL))) (!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)))
return DeviceHooksProvidesTransponder(Channel); return DeviceHooksProvidesTransponder(Channel);
@ -2047,7 +2182,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
bool needsDetachReceivers = false; bool needsDetachReceivers = false;
needsDetachBondedReceivers = false; needsDetachBondedReceivers = false;
if (dvbTuner && ProvidesTransponder(Channel)) { if (ProvidesTransponder(Channel)) {
result = hasPriority; result = hasPriority;
if (Priority > IDLEPRIORITY) { if (Priority > IDLEPRIORITY) {
if (Receiving()) { if (Receiving()) {
@ -2097,7 +2232,7 @@ bool cDvbDevice::ProvidesEIT(void) const
int cDvbDevice::NumProvidedSystems(void) const int cDvbDevice::NumProvidedSystems(void) const
{ {
return numDeliverySystems + numModulations; return dvbTuner ? dvbTuner->NumProvidedSystems() : 0;
} }
const cPositioner *cDvbDevice::Positioner(void) const const cPositioner *cDvbDevice::Positioner(void) const
@ -2137,9 +2272,11 @@ bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{ {
if (dvbTuner) if (dvbTuner->ProvidesFrontend(Channel, true)) {
dvbTuner->SetChannel(Channel); dvbTuner->SetChannel(Channel);
return true; return true;
}
return false;
} }
bool cDvbDevice::HasLock(int TimeoutMs) const bool cDvbDevice::HasLock(int TimeoutMs) const

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbdevice.h 4.4 2017/05/09 11:24:47 kls Exp $ * $Id: dvbdevice.h 4.5 2018/10/20 11:39:11 kls Exp $
*/ */
#ifndef __DVBDEVICE_H #ifndef __DVBDEVICE_H
@ -67,8 +67,6 @@ enum {
// --- End of definitions for older DVB API versions ------------------------- // --- End of definitions for older DVB API versions -------------------------
#define MAXDELIVERYSYSTEMS 8
#define DEV_VIDEO "/dev/video" #define DEV_VIDEO "/dev/video"
#define DEV_DVB_BASE "/dev/dvb" #define DEV_DVB_BASE "/dev/dvb"
#define DEV_DVB_ADAPTER "adapter" #define DEV_DVB_ADAPTER "adapter"
@ -162,12 +160,12 @@ public:
class cDvbTuner; class cDvbTuner;
cString DvbName(const char *Name, int Adapter, int Frontend);
int DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError = false);
/// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API. /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API.
class cDvbDevice : public cDevice { class cDvbDevice : public cDevice {
protected:
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: private:
static bool Exists(int Adapter, int Frontend); static bool Exists(int Adapter, int Frontend);
///< Checks whether the given adapter/frontend exists. ///< Checks whether the given adapter/frontend exists.
@ -182,16 +180,11 @@ public:
protected: protected:
int adapter, frontend; int adapter, frontend;
private: private:
dvb_frontend_info frontendInfo;
int deliverySystems[MAXDELIVERYSYSTEMS];
int numDeliverySystems;
int numModulations;
int fd_dvr, fd_ca; int fd_dvr, fd_ca;
bool checkTsBuffer; bool checkTsBuffer;
static cMutex bondMutex; static cMutex bondMutex;
cDvbDevice *bondedDevice; cDvbDevice *bondedDevice;
mutable bool needsDetachBondedReceivers; mutable bool needsDetachBondedReceivers;
bool QueryDeliverySystems(int fd_frontend);
public: public:
cDvbDevice(int Adapter, int Frontend); cDvbDevice(int Adapter, int Frontend);
virtual ~cDvbDevice(); virtual ~cDvbDevice();