2002-08-04 14:57:29 +02:00
|
|
|
/*
|
2009-12-31 15:38:18 +01:00
|
|
|
* dvbdevice.c: The DVB device tuner interface
|
2002-08-04 14:57:29 +02:00
|
|
|
*
|
|
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
|
|
* how to reach the author.
|
|
|
|
*
|
2009-12-31 15:38:18 +01:00
|
|
|
* $Id: dvbdevice.c 2.23 2009/12/31 15:38:18 kls Exp $
|
2002-08-04 14:57:29 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dvbdevice.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
2002-09-04 17:26:02 +02:00
|
|
|
#include <linux/dvb/dmx.h>
|
2002-08-10 14:58:25 +02:00
|
|
|
#include <linux/dvb/frontend.h>
|
2002-08-04 14:57:29 +02:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mman.h>
|
2002-10-06 10:25:42 +02:00
|
|
|
#include "channels.h"
|
|
|
|
#include "diseqc.h"
|
2007-01-07 14:46:14 +01:00
|
|
|
#include "dvbci.h"
|
2002-08-10 14:58:25 +02:00
|
|
|
|
2006-01-28 15:39:23 +01:00
|
|
|
#define DVBS_TUNE_TIMEOUT 9000 //ms
|
2006-01-03 10:42:47 +01:00
|
|
|
#define DVBS_LOCK_TIMEOUT 2000 //ms
|
2006-01-28 15:39:23 +01:00
|
|
|
#define DVBC_TUNE_TIMEOUT 9000 //ms
|
2006-01-03 10:42:47 +01:00
|
|
|
#define DVBC_LOCK_TIMEOUT 2000 //ms
|
|
|
|
#define DVBT_TUNE_TIMEOUT 9000 //ms
|
|
|
|
#define DVBT_LOCK_TIMEOUT 2000 //ms
|
2006-01-01 12:22:18 +01:00
|
|
|
|
2002-12-08 09:55:26 +01:00
|
|
|
// --- cDvbTuner -------------------------------------------------------------
|
|
|
|
|
|
|
|
class cDvbTuner : public cThread {
|
|
|
|
private:
|
2005-11-26 13:39:47 +01:00
|
|
|
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
|
2002-12-08 09:55:26 +01:00
|
|
|
int fd_frontend;
|
|
|
|
int cardIndex;
|
2006-01-01 12:22:18 +01:00
|
|
|
int tuneTimeout;
|
|
|
|
int lockTimeout;
|
2006-01-03 10:42:47 +01:00
|
|
|
time_t lastTimeoutReport;
|
2008-12-13 12:22:36 +01:00
|
|
|
fe_delivery_system frontendType;
|
2002-12-08 09:55:26 +01:00
|
|
|
cChannel channel;
|
|
|
|
const char *diseqcCommands;
|
|
|
|
eTunerStatus tunerStatus;
|
2004-10-23 10:04:01 +02:00
|
|
|
cMutex mutex;
|
|
|
|
cCondVar locked;
|
2005-08-21 09:25:51 +02:00
|
|
|
cCondVar newSet;
|
2006-01-04 11:48:38 +01:00
|
|
|
bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
|
2002-12-08 09:55:26 +01:00
|
|
|
bool SetFrontend(void);
|
|
|
|
virtual void Action(void);
|
|
|
|
public:
|
2008-12-13 12:22:36 +01:00
|
|
|
cDvbTuner(int Fd_Frontend, int CardIndex, fe_delivery_system FrontendType);
|
2002-12-08 09:55:26 +01:00
|
|
|
virtual ~cDvbTuner();
|
|
|
|
bool IsTunedTo(const cChannel *Channel) const;
|
2009-12-31 15:38:18 +01:00
|
|
|
void Set(const cChannel *Channel);
|
2004-10-23 10:04:01 +02:00
|
|
|
bool Locked(int TimeoutMs = 0);
|
2002-12-08 09:55:26 +01:00
|
|
|
};
|
|
|
|
|
2008-12-13 12:22:36 +01:00
|
|
|
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_delivery_system FrontendType)
|
2002-12-08 09:55:26 +01:00
|
|
|
{
|
|
|
|
fd_frontend = Fd_Frontend;
|
|
|
|
cardIndex = CardIndex;
|
|
|
|
frontendType = FrontendType;
|
2006-01-03 10:42:47 +01:00
|
|
|
tuneTimeout = 0;
|
|
|
|
lockTimeout = 0;
|
|
|
|
lastTimeoutReport = 0;
|
2002-12-08 09:55:26 +01:00
|
|
|
diseqcCommands = NULL;
|
|
|
|
tunerStatus = tsIdle;
|
2008-12-13 12:22:36 +01:00
|
|
|
if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
|
2004-06-05 14:37:26 +02:00
|
|
|
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power
|
2003-10-18 12:29:08 +02:00
|
|
|
SetDescription("tuner on device %d", cardIndex + 1);
|
2002-12-08 09:55:26 +01:00
|
|
|
Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
cDvbTuner::~cDvbTuner()
|
|
|
|
{
|
|
|
|
tunerStatus = tsIdle;
|
2005-08-21 09:25:51 +02:00
|
|
|
newSet.Broadcast();
|
|
|
|
locked.Broadcast();
|
2002-12-08 09:55:26 +01:00
|
|
|
Cancel(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
|
|
|
|
{
|
2008-04-12 13:39:12 +02:00
|
|
|
if (tunerStatus == tsIdle)
|
|
|
|
return false; // not tuned to
|
|
|
|
if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder())
|
|
|
|
return false; // sufficient mismatch
|
|
|
|
char Type = **cSource::ToString(Channel->Source());
|
|
|
|
#define ST(s, p) if (strchr(s, Type)) if (channel.p() != Channel->p()) return false;
|
|
|
|
// Polarization is already checked as part of the Transponder.
|
|
|
|
ST(" T", Bandwidth);
|
|
|
|
ST("CST", CoderateH);
|
|
|
|
ST(" T", CoderateL);
|
|
|
|
ST(" T", Guard);
|
|
|
|
ST("CST", Inversion);
|
|
|
|
ST("CST", Modulation);
|
|
|
|
ST(" S ", RollOff);
|
|
|
|
ST(" S ", System);
|
2008-12-13 12:22:36 +01:00
|
|
|
ST("CS ", Srate);
|
2008-04-12 13:39:12 +02:00
|
|
|
ST(" T", Transmission);
|
|
|
|
ST(" T", Hierarchy);
|
|
|
|
return true;
|
2002-12-08 09:55:26 +01:00
|
|
|
}
|
|
|
|
|
2009-12-31 15:38:18 +01:00
|
|
|
void cDvbTuner::Set(const cChannel *Channel)
|
2002-12-08 09:55:26 +01:00
|
|
|
{
|
2005-08-21 09:25:51 +02:00
|
|
|
cMutexLock MutexLock(&mutex);
|
2009-12-31 15:38:18 +01:00
|
|
|
if (!IsTunedTo(Channel))
|
2003-01-06 14:44:27 +01:00
|
|
|
tunerStatus = tsSet;
|
2003-04-19 14:46:58 +02:00
|
|
|
channel = *Channel;
|
2006-01-03 10:42:47 +01:00
|
|
|
lastTimeoutReport = 0;
|
2005-08-21 09:25:51 +02:00
|
|
|
newSet.Broadcast();
|
2002-12-08 09:55:26 +01:00
|
|
|
}
|
|
|
|
|
2004-10-23 10:04:01 +02:00
|
|
|
bool cDvbTuner::Locked(int TimeoutMs)
|
|
|
|
{
|
2005-08-21 09:25:51 +02:00
|
|
|
bool isLocked = (tunerStatus >= tsLocked);
|
|
|
|
if (isLocked || !TimeoutMs)
|
|
|
|
return isLocked;
|
|
|
|
|
2004-10-23 10:04:01 +02:00
|
|
|
cMutexLock MutexLock(&mutex);
|
|
|
|
if (TimeoutMs && tunerStatus < tsLocked)
|
|
|
|
locked.TimedWait(mutex, TimeoutMs);
|
|
|
|
return tunerStatus >= tsLocked;
|
|
|
|
}
|
|
|
|
|
2006-01-04 11:48:38 +01:00
|
|
|
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
|
2004-10-30 14:21:13 +02:00
|
|
|
{
|
|
|
|
if (TimeoutMs) {
|
2006-01-04 11:48:38 +01:00
|
|
|
cPoller Poller(fd_frontend);
|
|
|
|
if (Poller.Poll(TimeoutMs)) {
|
|
|
|
dvb_frontend_event Event;
|
2006-01-05 15:30:06 +01:00
|
|
|
while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
|
|
|
|
; // just to clear the event queue - we'll read the actual status below
|
2006-01-04 11:48:38 +01:00
|
|
|
}
|
2004-10-30 14:21:13 +02:00
|
|
|
}
|
2007-02-24 11:20:42 +01:00
|
|
|
while (1) {
|
2007-02-25 11:52:09 +01:00
|
|
|
if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
|
2007-02-24 11:20:42 +01:00
|
|
|
return true;
|
2007-02-25 11:52:09 +01:00
|
|
|
if (errno != EINTR)
|
2007-02-24 11:20:42 +01:00
|
|
|
break;
|
2004-10-30 14:21:13 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-12-08 09:55:26 +01:00
|
|
|
static unsigned int FrequencyToHz(unsigned int f)
|
|
|
|
{
|
|
|
|
while (f && f < 1000000)
|
|
|
|
f *= 1000;
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cDvbTuner::SetFrontend(void)
|
|
|
|
{
|
2008-12-13 12:22:36 +01:00
|
|
|
#define MAXFRONTENDCMDS 16
|
|
|
|
#define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
|
|
|
|
Frontend[CmdSeq.num].u.data = (d);\
|
|
|
|
if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
|
|
|
|
esyslog("ERROR: too many tuning commands on frontend %d", cardIndex);\
|
|
|
|
return false;\
|
|
|
|
}\
|
|
|
|
}
|
|
|
|
dtv_property Frontend[MAXFRONTENDCMDS];
|
2002-12-08 09:55:26 +01:00
|
|
|
memset(&Frontend, 0, sizeof(Frontend));
|
2008-12-13 12:22:36 +01:00
|
|
|
dtv_properties CmdSeq;
|
|
|
|
memset(&CmdSeq, 0, sizeof(CmdSeq));
|
|
|
|
CmdSeq.props = Frontend;
|
|
|
|
SETCMD(DTV_CLEAR, 0);
|
|
|
|
if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
|
|
|
|
esyslog("ERROR: frontend %d: %m", cardIndex);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CmdSeq.num = 0;
|
2002-12-08 09:55:26 +01:00
|
|
|
|
2008-12-13 12:22:36 +01:00
|
|
|
if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
|
2008-04-12 13:39:12 +02:00
|
|
|
unsigned int frequency = channel.Frequency();
|
|
|
|
if (Setup.DiSEqC) {
|
|
|
|
cDiseqc *diseqc = Diseqcs.Get(channel.Source(), channel.Frequency(), channel.Polarization());
|
|
|
|
if (diseqc) {
|
|
|
|
if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) {
|
|
|
|
cDiseqc::eDiseqcActions da;
|
|
|
|
for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) {
|
|
|
|
switch (da) {
|
|
|
|
case cDiseqc::daNone: break;
|
|
|
|
case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break;
|
|
|
|
case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break;
|
|
|
|
case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break;
|
|
|
|
case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break;
|
|
|
|
case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break;
|
|
|
|
case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break;
|
|
|
|
case cDiseqc::daCodes: {
|
|
|
|
int n = 0;
|
|
|
|
uchar *codes = diseqc->Codes(n);
|
|
|
|
if (codes) {
|
|
|
|
struct dvb_diseqc_master_cmd cmd;
|
2009-06-01 11:45:52 +02:00
|
|
|
cmd.msg_len = min(n, int(sizeof(cmd.msg)));
|
|
|
|
memcpy(cmd.msg, codes, cmd.msg_len);
|
2008-04-12 13:39:12 +02:00
|
|
|
CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-12-06 12:57:45 +01:00
|
|
|
default: esyslog("ERROR: unknown diseqc command %d", da);
|
2008-04-12 13:39:12 +02:00
|
|
|
}
|
2002-12-08 09:55:26 +01:00
|
|
|
}
|
2008-04-12 13:39:12 +02:00
|
|
|
diseqcCommands = diseqc->Commands();
|
|
|
|
}
|
|
|
|
frequency -= diseqc->Lof();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int tone = SEC_TONE_OFF;
|
|
|
|
if (frequency < (unsigned int)Setup.LnbSLOF) {
|
|
|
|
frequency -= Setup.LnbFrequLo;
|
|
|
|
tone = SEC_TONE_OFF;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
frequency -= Setup.LnbFrequHi;
|
|
|
|
tone = SEC_TONE_ON;
|
|
|
|
}
|
|
|
|
int volt = (channel.Polarization() == 'v' || channel.Polarization() == 'V' || channel.Polarization() == 'r' || channel.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
|
|
|
|
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt));
|
|
|
|
CHECK(ioctl(fd_frontend, FE_SET_TONE, tone));
|
|
|
|
}
|
|
|
|
frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF
|
|
|
|
|
2008-12-13 12:22:36 +01:00
|
|
|
// DVB-S/DVB-S2 (common parts)
|
|
|
|
SETCMD(DTV_DELIVERY_SYSTEM, channel.System());
|
|
|
|
SETCMD(DTV_FREQUENCY, frequency * 1000UL);
|
|
|
|
SETCMD(DTV_MODULATION, channel.Modulation());
|
|
|
|
SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
|
|
|
|
SETCMD(DTV_INNER_FEC, channel.CoderateH());
|
|
|
|
SETCMD(DTV_INVERSION, channel.Inversion());
|
|
|
|
if (channel.System() == SYS_DVBS2) {
|
|
|
|
if (frontendType == SYS_DVBS2) {
|
|
|
|
// DVB-S2
|
|
|
|
SETCMD(DTV_PILOT, PILOT_AUTO);
|
|
|
|
SETCMD(DTV_ROLLOFF, channel.RollOff());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
esyslog("ERROR: frontend %d doesn't provide DVB-S2", cardIndex);
|
|
|
|
return false;
|
|
|
|
}
|
2008-04-12 13:39:12 +02:00
|
|
|
}
|
|
|
|
else {
|
2008-12-13 12:22:36 +01:00
|
|
|
// DVB-S
|
|
|
|
SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35
|
2008-04-12 13:39:12 +02:00
|
|
|
}
|
2008-02-08 13:48:31 +01:00
|
|
|
|
2008-04-12 13:39:12 +02:00
|
|
|
tuneTimeout = DVBS_TUNE_TIMEOUT;
|
|
|
|
lockTimeout = DVBS_LOCK_TIMEOUT;
|
|
|
|
}
|
2008-12-13 12:22:36 +01:00
|
|
|
else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
|
|
|
|
// DVB-C
|
|
|
|
SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
|
|
|
|
SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
|
|
|
|
SETCMD(DTV_INVERSION, channel.Inversion());
|
|
|
|
SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL);
|
|
|
|
SETCMD(DTV_INNER_FEC, channel.CoderateH());
|
|
|
|
SETCMD(DTV_MODULATION, channel.Modulation());
|
2008-04-12 13:39:12 +02:00
|
|
|
|
|
|
|
tuneTimeout = DVBC_TUNE_TIMEOUT;
|
|
|
|
lockTimeout = DVBC_LOCK_TIMEOUT;
|
|
|
|
}
|
2008-12-13 12:22:36 +01:00
|
|
|
else if (frontendType == SYS_DVBT) {
|
|
|
|
// DVB-T
|
|
|
|
SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
|
|
|
|
SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
|
|
|
|
SETCMD(DTV_INVERSION, channel.Inversion());
|
|
|
|
SETCMD(DTV_BANDWIDTH_HZ, channel.Bandwidth());
|
|
|
|
SETCMD(DTV_CODE_RATE_HP, channel.CoderateH());
|
|
|
|
SETCMD(DTV_CODE_RATE_LP, channel.CoderateL());
|
|
|
|
SETCMD(DTV_MODULATION, channel.Modulation());
|
|
|
|
SETCMD(DTV_TRANSMISSION_MODE, channel.Transmission());
|
|
|
|
SETCMD(DTV_GUARD_INTERVAL, channel.Guard());
|
|
|
|
SETCMD(DTV_HIERARCHY, channel.Hierarchy());
|
2008-04-12 13:39:12 +02:00
|
|
|
|
|
|
|
tuneTimeout = DVBT_TUNE_TIMEOUT;
|
|
|
|
lockTimeout = DVBT_LOCK_TIMEOUT;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
|
|
|
|
return false;
|
2008-04-13 13:31:00 +02:00
|
|
|
}
|
2008-12-13 12:22:36 +01:00
|
|
|
SETCMD(DTV_TUNE, 0);
|
|
|
|
if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
|
2002-12-08 09:55:26 +01:00
|
|
|
esyslog("ERROR: frontend %d: %m", cardIndex);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cDvbTuner::Action(void)
|
|
|
|
{
|
2006-01-01 12:22:18 +01:00
|
|
|
cTimeMs Timer;
|
|
|
|
bool LostLock = false;
|
2006-01-04 11:48:38 +01:00
|
|
|
fe_status_t Status = (fe_status_t)0;
|
2005-08-14 11:24:57 +02:00
|
|
|
while (Running()) {
|
2006-01-04 11:48:38 +01:00
|
|
|
fe_status_t NewStatus;
|
|
|
|
if (GetFrontendStatus(NewStatus, 10))
|
|
|
|
Status = NewStatus;
|
2005-08-21 09:25:51 +02:00
|
|
|
cMutexLock MutexLock(&mutex);
|
|
|
|
switch (tunerStatus) {
|
|
|
|
case tsIdle:
|
|
|
|
break;
|
|
|
|
case tsSet:
|
|
|
|
tunerStatus = SetFrontend() ? tsTuned : tsIdle;
|
2006-01-01 12:22:18 +01:00
|
|
|
Timer.Set(tuneTimeout);
|
2005-08-21 09:25:51 +02:00
|
|
|
continue;
|
|
|
|
case tsTuned:
|
2006-01-01 12:22:18 +01:00
|
|
|
if (Timer.TimedOut()) {
|
|
|
|
tunerStatus = tsSet;
|
|
|
|
diseqcCommands = NULL;
|
2006-01-03 10:42:47 +01:00
|
|
|
if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
|
2006-01-14 16:04:36 +01:00
|
|
|
isyslog("frontend %d timed out while tuning to channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
|
2006-01-03 10:42:47 +01:00
|
|
|
lastTimeoutReport = time(NULL);
|
2006-01-01 12:22:18 +01:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2005-08-21 09:25:51 +02:00
|
|
|
case tsLocked:
|
2006-01-04 11:48:38 +01:00
|
|
|
if (Status & FE_REINIT) {
|
|
|
|
tunerStatus = tsSet;
|
|
|
|
diseqcCommands = NULL;
|
2006-01-14 16:04:36 +01:00
|
|
|
isyslog("frontend %d was reinitialized", cardIndex);
|
2006-01-04 11:48:38 +01:00
|
|
|
lastTimeoutReport = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (Status & FE_HAS_LOCK) {
|
|
|
|
if (LostLock) {
|
2006-01-14 16:04:36 +01:00
|
|
|
isyslog("frontend %d regained lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
|
2006-01-04 11:48:38 +01:00
|
|
|
LostLock = false;
|
2006-01-01 12:22:18 +01:00
|
|
|
}
|
2006-01-04 11:48:38 +01:00
|
|
|
tunerStatus = tsLocked;
|
|
|
|
locked.Broadcast();
|
|
|
|
lastTimeoutReport = 0;
|
|
|
|
}
|
|
|
|
else if (tunerStatus == tsLocked) {
|
|
|
|
LostLock = true;
|
2006-01-14 16:04:36 +01:00
|
|
|
isyslog("frontend %d lost lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
|
2006-01-04 11:48:38 +01:00
|
|
|
tunerStatus = tsTuned;
|
|
|
|
Timer.Set(lockTimeout);
|
|
|
|
lastTimeoutReport = 0;
|
2005-08-21 09:25:51 +02:00
|
|
|
continue;
|
|
|
|
}
|
2009-12-06 12:57:45 +01:00
|
|
|
break;
|
|
|
|
default: esyslog("ERROR: unknown tuner status %d", tunerStatus);
|
2005-08-21 09:25:51 +02:00
|
|
|
}
|
|
|
|
|
2004-10-30 14:21:13 +02:00
|
|
|
if (tunerStatus != tsTuned)
|
2005-11-26 13:39:47 +01:00
|
|
|
newSet.TimedWait(mutex, 1000);
|
2002-12-08 09:55:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- cDvbDevice ------------------------------------------------------------
|
|
|
|
|
2006-05-20 10:17:44 +02:00
|
|
|
int cDvbDevice::setTransferModeForDolbyDigital = 1;
|
2003-10-04 12:42:58 +02:00
|
|
|
|
2008-04-12 13:39:12 +02:00
|
|
|
const char *DeliverySystems[] = {
|
2008-12-13 12:22:36 +01:00
|
|
|
"UNDEFINED",
|
|
|
|
"DVB-C",
|
|
|
|
"DVB-C",
|
|
|
|
"DVB-T",
|
2008-04-12 13:39:12 +02:00
|
|
|
"DSS",
|
2008-12-13 12:22:36 +01:00
|
|
|
"DVB-S",
|
|
|
|
"DVB-S2",
|
|
|
|
"DVB-H",
|
|
|
|
"ISDBT",
|
|
|
|
"ISDBS",
|
|
|
|
"ISDBC",
|
2008-04-12 13:39:12 +02:00
|
|
|
"ATSC",
|
2008-12-13 12:22:36 +01:00
|
|
|
"ATSCMH",
|
|
|
|
"DMBTH",
|
|
|
|
"CMMB",
|
|
|
|
"DAB",
|
2008-04-12 13:39:12 +02:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2002-08-04 14:57:29 +02:00
|
|
|
cDvbDevice::cDvbDevice(int n)
|
|
|
|
{
|
2007-01-07 14:46:14 +01:00
|
|
|
ciAdapter = NULL;
|
2002-12-08 09:55:26 +01:00
|
|
|
dvbTuner = NULL;
|
2008-12-13 12:22:36 +01:00
|
|
|
frontendType = SYS_UNDEFINED;
|
2008-04-12 13:39:12 +02:00
|
|
|
numProvidedSystems = 0;
|
2002-08-04 14:57:29 +02:00
|
|
|
|
|
|
|
// Devices that are present on all card types:
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK);
|
2003-10-17 15:36:13 +02:00
|
|
|
|
2007-01-07 14:46:14 +01:00
|
|
|
// Common Interface:
|
|
|
|
|
|
|
|
fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
|
|
|
|
if (fd_ca >= 0)
|
|
|
|
ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca);
|
|
|
|
|
2002-08-04 14:57:29 +02:00
|
|
|
// 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) {
|
2008-12-13 12:22:36 +01:00
|
|
|
if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) >= 0) {
|
|
|
|
switch (frontendInfo.type) {
|
2009-01-06 14:52:54 +01:00
|
|
|
case FE_QPSK: frontendType = (frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
|
2008-12-13 12:22:36 +01:00
|
|
|
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 device %d", frontendInfo.type, CardIndex() + 1);
|
|
|
|
}
|
2002-12-08 09:55:26 +01:00
|
|
|
}
|
2002-08-04 14:57:29 +02:00
|
|
|
else
|
|
|
|
LOG_ERROR;
|
2008-12-13 12:22:36 +01:00
|
|
|
if (frontendType != SYS_UNDEFINED) {
|
|
|
|
numProvidedSystems++;
|
|
|
|
if (frontendType == SYS_DVBS2)
|
|
|
|
numProvidedSystems++;
|
|
|
|
isyslog("device %d provides %s (\"%s\")", CardIndex() + 1, DeliverySystems[frontendType], frontendInfo.name);
|
|
|
|
dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType);
|
|
|
|
}
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
esyslog("ERROR: can't open DVB device %d", n);
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
StartSectionHandler();
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cDvbDevice::~cDvbDevice()
|
|
|
|
{
|
2008-02-09 16:28:15 +01:00
|
|
|
StopSectionHandler();
|
2002-12-08 09:55:26 +01:00
|
|
|
delete dvbTuner;
|
2007-01-07 14:46:14 +01:00
|
|
|
delete ciAdapter;
|
2002-08-04 14:57:29 +02:00
|
|
|
// We're not explicitly closing any device files here, since this sometimes
|
|
|
|
// caused segfaults. Besides, the program is about to terminate anyway...
|
|
|
|
}
|
|
|
|
|
2009-12-31 15:38:18 +01:00
|
|
|
cString cDvbDevice::DvbName(const char *Name, int n)
|
2002-08-04 14:57:29 +02:00
|
|
|
{
|
2009-12-31 15:38:18 +01:00
|
|
|
return cString::sprintf("%s%d/%s%d", DEV_DVB_ADAPTER, n, Name, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int cDvbDevice::DvbOpen(const char *Name, int n, int Mode, bool ReportError)
|
|
|
|
{
|
|
|
|
cString FileName = DvbName(Name, n);
|
|
|
|
int fd = open(FileName, Mode);
|
|
|
|
if (fd < 0 && ReportError)
|
|
|
|
LOG_ERROR_STR(*FileName);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cDvbDevice::Probe(int Adapter)
|
|
|
|
{
|
|
|
|
cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter);
|
2002-08-04 14:57:29 +02:00
|
|
|
if (access(FileName, F_OK) == 0) {
|
2009-12-31 15:38:18 +01:00
|
|
|
dsyslog("probing %s", *FileName);
|
2002-08-04 14:57:29 +02:00
|
|
|
int f = open(FileName, O_RDONLY);
|
|
|
|
if (f >= 0) {
|
|
|
|
close(f);
|
2009-12-31 15:38:18 +01:00
|
|
|
for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) {
|
|
|
|
if (dp->Probe(Adapter))
|
|
|
|
return true; // a plugin has created the actual device
|
|
|
|
}
|
|
|
|
dsyslog("creating cDvbDevice");
|
|
|
|
new cDvbDevice(Adapter); // it's a "budget" device
|
2002-08-04 14:57:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (errno != ENODEV && errno != EINVAL)
|
2009-12-31 15:38:18 +01:00
|
|
|
LOG_ERROR_STR(*FileName);
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
|
|
|
else if (errno != ENOENT)
|
2009-12-31 15:38:18 +01:00
|
|
|
LOG_ERROR_STR(*FileName);
|
2002-08-04 14:57:29 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cDvbDevice::Initialize(void)
|
|
|
|
{
|
|
|
|
int found = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < MAXDVBDEVICES; i++) {
|
|
|
|
if (UseDevice(NextCardIndex())) {
|
2009-12-31 15:38:18 +01:00
|
|
|
if (Probe(i))
|
2002-08-04 14:57:29 +02:00
|
|
|
found++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
NextCardIndex(1); // skips this one
|
|
|
|
}
|
|
|
|
NextCardIndex(MAXDVBDEVICES - i); // skips the rest
|
|
|
|
if (found > 0)
|
|
|
|
isyslog("found %d video device%s", found, found > 1 ? "s" : "");
|
|
|
|
else
|
|
|
|
isyslog("no DVB device found");
|
|
|
|
return found > 0;
|
|
|
|
}
|
|
|
|
|
2005-08-21 08:56:49 +02:00
|
|
|
bool cDvbDevice::Ready(void)
|
|
|
|
{
|
2007-01-07 14:46:14 +01:00
|
|
|
if (ciAdapter)
|
|
|
|
return ciAdapter->Ready();
|
2005-08-21 08:56:49 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-01-13 12:14:51 +01:00
|
|
|
bool cDvbDevice::HasCi(void)
|
|
|
|
{
|
|
|
|
return ciAdapter;
|
|
|
|
}
|
|
|
|
|
2002-08-04 14:57:29 +02:00
|
|
|
bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
|
|
|
|
{
|
|
|
|
if (Handle->pid) {
|
2002-10-11 13:23:44 +02:00
|
|
|
dmx_pes_filter_params pesFilterParams;
|
2002-09-14 11:51:51 +02:00
|
|
|
memset(&pesFilterParams, 0, sizeof(pesFilterParams));
|
2002-08-04 14:57:29 +02:00
|
|
|
if (On) {
|
|
|
|
if (Handle->handle < 0) {
|
2002-08-10 14:58:25 +02:00
|
|
|
Handle->handle = DvbOpen(DEV_DVB_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true);
|
2005-06-05 13:37:37 +02:00
|
|
|
if (Handle->handle < 0) {
|
|
|
|
LOG_ERROR;
|
2002-08-04 14:57:29 +02:00
|
|
|
return false;
|
2005-06-05 13:37:37 +02:00
|
|
|
}
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
2002-09-14 11:51:51 +02:00
|
|
|
pesFilterParams.pid = Handle->pid;
|
|
|
|
pesFilterParams.input = DMX_IN_FRONTEND;
|
2009-12-31 15:38:18 +01:00
|
|
|
pesFilterParams.output = DMX_OUT_TS_TAP;
|
|
|
|
pesFilterParams.pes_type= DMX_PES_OTHER;
|
2002-09-14 11:51:51 +02:00
|
|
|
pesFilterParams.flags = DMX_IMMEDIATE_START;
|
|
|
|
if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
|
|
|
|
LOG_ERROR;
|
|
|
|
return false;
|
|
|
|
}
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
2002-09-14 13:26:16 +02:00
|
|
|
else if (!Handle->used) {
|
2002-08-04 14:57:29 +02:00
|
|
|
CHECK(ioctl(Handle->handle, DMX_STOP));
|
2002-09-14 13:26:16 +02:00
|
|
|
if (Type <= ptTeletext) {
|
2002-09-04 17:26:02 +02:00
|
|
|
pesFilterParams.pid = 0x1FFF;
|
|
|
|
pesFilterParams.input = DMX_IN_FRONTEND;
|
|
|
|
pesFilterParams.output = DMX_OUT_DECODER;
|
2009-12-31 15:38:18 +01:00
|
|
|
pesFilterParams.pes_type= DMX_PES_OTHER;
|
2002-09-04 17:26:02 +02:00
|
|
|
pesFilterParams.flags = DMX_IMMEDIATE_START;
|
|
|
|
CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams));
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
2002-11-15 14:04:11 +01:00
|
|
|
close(Handle->handle);
|
|
|
|
Handle->handle = -1;
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask)
|
|
|
|
{
|
2009-12-31 15:38:18 +01:00
|
|
|
cString FileName = DvbName(DEV_DVB_DEMUX, CardIndex());
|
2003-12-22 13:29:24 +01:00
|
|
|
int f = open(FileName, O_RDWR | O_NONBLOCK);
|
|
|
|
if (f >= 0) {
|
|
|
|
dmx_sct_filter_params sctFilterParams;
|
|
|
|
memset(&sctFilterParams, 0, sizeof(sctFilterParams));
|
|
|
|
sctFilterParams.pid = Pid;
|
|
|
|
sctFilterParams.timeout = 0;
|
|
|
|
sctFilterParams.flags = DMX_IMMEDIATE_START;
|
|
|
|
sctFilterParams.filter.filter[0] = Tid;
|
|
|
|
sctFilterParams.filter.mask[0] = Mask;
|
|
|
|
if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
|
|
|
|
return f;
|
|
|
|
else {
|
2004-01-10 12:21:41 +01:00
|
|
|
esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
|
2003-12-22 13:29:24 +01:00
|
|
|
close(f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2009-12-31 15:38:18 +01:00
|
|
|
esyslog("ERROR: can't open filter handle on '%s'", *FileName);
|
2003-12-22 13:29:24 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-10-14 13:11:23 +02:00
|
|
|
void cDvbDevice::CloseFilter(int Handle)
|
|
|
|
{
|
|
|
|
close(Handle);
|
|
|
|
}
|
|
|
|
|
2002-10-06 10:25:42 +02:00
|
|
|
bool cDvbDevice::ProvidesSource(int Source) const
|
|
|
|
{
|
|
|
|
int type = Source & cSource::st_Mask;
|
|
|
|
return type == cSource::stNone
|
2008-12-13 12:22:36 +01:00
|
|
|
|| 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);
|
2002-10-06 10:25:42 +02:00
|
|
|
}
|
|
|
|
|
2004-01-04 12:30:00 +01:00
|
|
|
bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
|
|
|
|
{
|
2008-04-12 13:39:12 +02:00
|
|
|
if (!ProvidesSource(Channel->Source()))
|
|
|
|
return false; // doesn't provide source
|
|
|
|
if (!cSource::IsSat(Channel->Source()))
|
|
|
|
return true; // source is sufficient for non sat
|
2008-12-13 12:22:36 +01:00
|
|
|
if (frontendType == SYS_DVBS && Channel->System() == SYS_DVBS2)
|
2008-04-12 13:39:12 +02:00
|
|
|
return false; // requires modulation system which frontend doesn't provide
|
|
|
|
return !Setup.DiSEqC || Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
|
2004-01-04 12:30:00 +01:00
|
|
|
}
|
|
|
|
|
2002-09-29 13:40:45 +02:00
|
|
|
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
|
2002-08-04 14:57:29 +02:00
|
|
|
{
|
2002-09-04 17:26:02 +02:00
|
|
|
bool result = false;
|
|
|
|
bool hasPriority = Priority < 0 || Priority > this->Priority();
|
2003-08-24 14:49:53 +02:00
|
|
|
bool needsDetachReceivers = false;
|
2002-09-04 17:26:02 +02:00
|
|
|
|
2008-04-12 13:39:12 +02:00
|
|
|
if (ProvidesTransponder(Channel)) {
|
2002-12-13 15:35:00 +01:00
|
|
|
result = hasPriority;
|
2004-05-22 15:36:09 +02:00
|
|
|
if (Priority >= 0 && Receiving(true)) {
|
2002-12-08 09:55:26 +01:00
|
|
|
if (dvbTuner->IsTunedTo(Channel)) {
|
2004-12-17 14:55:49 +01:00
|
|
|
if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
|
2007-01-07 14:46:14 +01:00
|
|
|
if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
|
|
|
if (CamSlot()->CanDecrypt(Channel))
|
|
|
|
result = true;
|
|
|
|
else
|
|
|
|
needsDetachReceivers = true;
|
|
|
|
}
|
|
|
|
else if (!IsPrimaryDevice())
|
2002-09-14 11:51:51 +02:00
|
|
|
result = true;
|
2002-11-03 12:31:51 +01:00
|
|
|
else
|
|
|
|
result = Priority >= Setup.PrimaryLimit;
|
2002-09-04 17:26:02 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
result = !IsPrimaryDevice() || Priority >= Setup.PrimaryLimit;
|
|
|
|
}
|
2003-08-24 14:49:53 +02:00
|
|
|
else
|
|
|
|
needsDetachReceivers = true;
|
2002-09-04 17:26:02 +02:00
|
|
|
}
|
|
|
|
}
|
2002-09-06 14:10:17 +02:00
|
|
|
if (NeedsDetachReceivers)
|
|
|
|
*NeedsDetachReceivers = needsDetachReceivers;
|
2002-09-04 17:26:02 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-04-12 13:39:12 +02:00
|
|
|
int cDvbDevice::NumProvidedSystems(void) const
|
|
|
|
{
|
|
|
|
return numProvidedSystems;
|
|
|
|
}
|
|
|
|
|
2006-04-09 09:12:47 +02:00
|
|
|
bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
|
|
|
|
{
|
|
|
|
return dvbTuner->IsTunedTo(Channel);
|
|
|
|
}
|
|
|
|
|
2002-09-04 17:26:02 +02:00
|
|
|
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
|
|
|
{
|
2009-12-31 15:38:18 +01:00
|
|
|
dvbTuner->Set(Channel);
|
2002-08-04 14:57:29 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2004-10-30 15:10:50 +02:00
|
|
|
bool cDvbDevice::HasLock(int TimeoutMs)
|
2004-01-04 12:30:00 +01:00
|
|
|
{
|
2004-10-30 15:10:50 +02:00
|
|
|
return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
|
2004-01-04 12:30:00 +01:00
|
|
|
}
|
|
|
|
|
2006-05-20 10:17:44 +02:00
|
|
|
void cDvbDevice::SetTransferModeForDolbyDigital(int Mode)
|
|
|
|
{
|
|
|
|
setTransferModeForDolbyDigital = Mode;
|
2005-02-13 14:26:37 +01:00
|
|
|
}
|
|
|
|
|
2002-08-04 14:57:29 +02:00
|
|
|
bool cDvbDevice::OpenDvr(void)
|
|
|
|
{
|
|
|
|
CloseDvr();
|
2002-08-10 14:58:25 +02:00
|
|
|
fd_dvr = DvbOpen(DEV_DVB_DVR, CardIndex(), O_RDONLY | O_NONBLOCK, true);
|
2002-09-08 09:03:10 +02:00
|
|
|
if (fd_dvr >= 0)
|
|
|
|
tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1);
|
2002-08-04 14:57:29 +02:00
|
|
|
return fd_dvr >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cDvbDevice::CloseDvr(void)
|
|
|
|
{
|
|
|
|
if (fd_dvr >= 0) {
|
2002-09-08 09:03:10 +02:00
|
|
|
delete tsBuffer;
|
|
|
|
tsBuffer = NULL;
|
2004-10-16 09:36:28 +02:00
|
|
|
close(fd_dvr);
|
|
|
|
fd_dvr = -1;
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-08 09:03:10 +02:00
|
|
|
bool cDvbDevice::GetTSPacket(uchar *&Data)
|
2002-08-04 14:57:29 +02:00
|
|
|
{
|
2002-09-08 09:03:10 +02:00
|
|
|
if (tsBuffer) {
|
2004-10-16 09:36:28 +02:00
|
|
|
Data = tsBuffer->Get();
|
2002-09-08 09:03:10 +02:00
|
|
|
return true;
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
2002-09-08 09:03:10 +02:00
|
|
|
return false;
|
2002-08-04 14:57:29 +02:00
|
|
|
}
|
2009-12-31 15:38:18 +01:00
|
|
|
|
|
|
|
// --- cDvbDeviceProbe -------------------------------------------------------
|
|
|
|
|
|
|
|
cList<cDvbDeviceProbe> DvbDeviceProbes;
|
|
|
|
|
|
|
|
cDvbDeviceProbe::cDvbDeviceProbe(void)
|
|
|
|
{
|
|
|
|
DvbDeviceProbes.Add(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
cDvbDeviceProbe::~cDvbDeviceProbe()
|
|
|
|
{
|
|
|
|
DvbDeviceProbes.Del(this, false);
|
|
|
|
}
|