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 .
*
2011-09-17 12:53:46 +02:00
* $ Id : dvbdevice . c 2.47 2011 / 09 / 17 12 : 53 : 46 kls Exp $
2002-08-04 14:57:29 +02:00
*/
# include "dvbdevice.h"
2010-02-28 12:19:50 +01:00
# include <ctype.h>
2002-08-04 14:57:29 +02:00
# 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"
2010-02-28 12:19:50 +01:00
# include "menuitems.h"
# include "sourceparams.h"
2002-08-10 14:58:25 +02:00
2010-04-25 13:05:37 +02:00
# define FE_CAN_TURBO_FEC 0x8000000 // TODO: remove this once it is defined in the driver
2010-04-10 12:17:34 +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
2010-03-06 12:01:17 +01:00
# define ATSC_TUNE_TIMEOUT 9000 //ms
# define ATSC_LOCK_TIMEOUT 2000 //ms
2006-01-01 12:22:18 +01:00
2011-09-17 12:53:46 +02:00
# define SCR_RANDOM_TIMEOUT 500 // ms (add random value up to this when tuning SCR device to avoid lockups)
2010-02-28 12:19:50 +01:00
// --- DVB Parameter Maps ----------------------------------------------------
const tDvbParameterMap InversionValues [ ] = {
{ 0 , INVERSION_OFF , trNOOP ( " off " ) } ,
{ 1 , INVERSION_ON , trNOOP ( " on " ) } ,
{ 999 , INVERSION_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap BandwidthValues [ ] = {
{ 6 , 6000000 , " 6 MHz " } ,
{ 7 , 7000000 , " 7 MHz " } ,
{ 8 , 8000000 , " 8 MHz " } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap CoderateValues [ ] = {
{ 0 , FEC_NONE , trNOOP ( " none " ) } ,
{ 12 , FEC_1_2 , " 1/2 " } ,
{ 23 , FEC_2_3 , " 2/3 " } ,
{ 34 , FEC_3_4 , " 3/4 " } ,
{ 35 , FEC_3_5 , " 3/5 " } ,
{ 45 , FEC_4_5 , " 4/5 " } ,
{ 56 , FEC_5_6 , " 5/6 " } ,
{ 67 , FEC_6_7 , " 6/7 " } ,
{ 78 , FEC_7_8 , " 7/8 " } ,
{ 89 , FEC_8_9 , " 8/9 " } ,
{ 910 , FEC_9_10 , " 9/10 " } ,
{ 999 , FEC_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap ModulationValues [ ] = {
{ 16 , QAM_16 , " QAM16 " } ,
{ 32 , QAM_32 , " QAM32 " } ,
{ 64 , QAM_64 , " QAM64 " } ,
{ 128 , QAM_128 , " QAM128 " } ,
{ 256 , QAM_256 , " QAM256 " } ,
{ 2 , QPSK , " QPSK " } ,
{ 5 , PSK_8 , " 8PSK " } ,
{ 6 , APSK_16 , " 16APSK " } ,
{ 10 , VSB_8 , " VSB8 " } ,
{ 11 , VSB_16 , " VSB16 " } ,
{ 998 , QAM_AUTO , " QAMAUTO " } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap SystemValues [ ] = {
{ 0 , SYS_DVBS , " DVB-S " } ,
{ 1 , SYS_DVBS2 , " DVB-S2 " } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap TransmissionValues [ ] = {
{ 2 , TRANSMISSION_MODE_2K , " 2K " } ,
{ 8 , TRANSMISSION_MODE_8K , " 8K " } ,
{ 999 , TRANSMISSION_MODE_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap GuardValues [ ] = {
{ 4 , GUARD_INTERVAL_1_4 , " 1/4 " } ,
{ 8 , GUARD_INTERVAL_1_8 , " 1/8 " } ,
{ 16 , GUARD_INTERVAL_1_16 , " 1/16 " } ,
{ 32 , GUARD_INTERVAL_1_32 , " 1/32 " } ,
{ 999 , GUARD_INTERVAL_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap HierarchyValues [ ] = {
{ 0 , HIERARCHY_NONE , trNOOP ( " none " ) } ,
{ 1 , HIERARCHY_1 , " 1 " } ,
{ 2 , HIERARCHY_2 , " 2 " } ,
{ 4 , HIERARCHY_4 , " 4 " } ,
{ 999 , HIERARCHY_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap RollOffValues [ ] = {
{ 0 , ROLLOFF_AUTO , trNOOP ( " auto " ) } ,
{ 20 , ROLLOFF_20 , " 0.20 " } ,
{ 25 , ROLLOFF_25 , " 0.25 " } ,
{ 35 , ROLLOFF_35 , " 0.35 " } ,
{ - 1 , 0 , NULL }
} ;
int UserIndex ( int Value , const tDvbParameterMap * Map )
{
const tDvbParameterMap * map = Map ;
while ( map & & map - > userValue ! = - 1 ) {
if ( map - > userValue = = Value )
return map - Map ;
map + + ;
}
return - 1 ;
}
int DriverIndex ( int Value , const tDvbParameterMap * Map )
{
const tDvbParameterMap * map = Map ;
while ( map & & map - > userValue ! = - 1 ) {
if ( map - > driverValue = = Value )
return map - Map ;
map + + ;
}
return - 1 ;
}
int MapToUser ( int Value , const tDvbParameterMap * Map , const char * * String )
{
int n = DriverIndex ( Value , Map ) ;
if ( n > = 0 ) {
if ( String )
* String = tr ( Map [ n ] . userString ) ;
return Map [ n ] . userValue ;
}
return - 1 ;
}
2010-04-11 13:02:42 +02:00
const char * MapToUserString ( int Value , const tDvbParameterMap * Map )
{
int n = DriverIndex ( Value , Map ) ;
if ( n > = 0 )
return Map [ n ] . userString ;
return " ??? " ;
}
2010-02-28 12:19:50 +01:00
int MapToDriver ( int Value , const tDvbParameterMap * Map )
{
int n = UserIndex ( Value , Map ) ;
if ( n > = 0 )
return Map [ n ] . driverValue ;
return - 1 ;
}
// --- cDvbTransponderParameters ---------------------------------------------
cDvbTransponderParameters : : cDvbTransponderParameters ( const char * Parameters )
{
polarization = 0 ;
inversion = INVERSION_AUTO ;
bandwidth = 8000000 ;
coderateH = FEC_AUTO ;
coderateL = FEC_AUTO ;
modulation = QPSK ;
system = SYS_DVBS ;
transmission = TRANSMISSION_MODE_AUTO ;
guard = GUARD_INTERVAL_AUTO ;
hierarchy = HIERARCHY_AUTO ;
rollOff = ROLLOFF_AUTO ;
Parse ( Parameters ) ;
}
int cDvbTransponderParameters : : PrintParameter ( char * p , char Name , int Value ) const
{
return Value > = 0 & & Value ! = 999 ? sprintf ( p , " %c%d " , Name , Value ) : 0 ;
}
cString cDvbTransponderParameters : : ToString ( char Type ) const
{
# define ST(s) if (strchr(s, Type))
char buffer [ 64 ] ;
char * q = buffer ;
* q = 0 ;
2010-03-06 12:01:17 +01:00
ST ( " S " ) q + = sprintf ( q , " %c " , polarization ) ;
ST ( " T " ) q + = PrintParameter ( q , ' B ' , MapToUser ( bandwidth , BandwidthValues ) ) ;
ST ( " CST " ) q + = PrintParameter ( q , ' C ' , MapToUser ( coderateH , CoderateValues ) ) ;
ST ( " T " ) q + = PrintParameter ( q , ' D ' , MapToUser ( coderateL , CoderateValues ) ) ;
ST ( " T " ) q + = PrintParameter ( q , ' G ' , MapToUser ( guard , GuardValues ) ) ;
ST ( " ACST " ) q + = PrintParameter ( q , ' I ' , MapToUser ( inversion , InversionValues ) ) ;
ST ( " ACST " ) q + = PrintParameter ( q , ' M ' , MapToUser ( modulation , ModulationValues ) ) ;
ST ( " S " ) q + = PrintParameter ( q , ' O ' , MapToUser ( rollOff , RollOffValues ) ) ;
ST ( " S " ) q + = PrintParameter ( q , ' S ' , MapToUser ( system , SystemValues ) ) ;
ST ( " T " ) q + = PrintParameter ( q , ' T ' , MapToUser ( transmission , TransmissionValues ) ) ;
ST ( " T " ) q + = PrintParameter ( q , ' Y ' , MapToUser ( hierarchy , HierarchyValues ) ) ;
2010-02-28 12:19:50 +01:00
return buffer ;
}
const char * cDvbTransponderParameters : : ParseParameter ( const char * s , int & Value , const tDvbParameterMap * Map )
{
if ( * + + s ) {
char * p = NULL ;
errno = 0 ;
int n = strtol ( s , & p , 10 ) ;
if ( ! errno & & p ! = s ) {
Value = MapToDriver ( n , Map ) ;
if ( Value > = 0 )
return p ;
}
}
esyslog ( " ERROR: invalid value for parameter '%c' " , * ( s - 1 ) ) ;
return NULL ;
}
bool cDvbTransponderParameters : : Parse ( const char * s )
{
while ( s & & * s ) {
switch ( toupper ( * s ) ) {
case ' B ' : s = ParseParameter ( s , bandwidth , BandwidthValues ) ; break ;
case ' C ' : s = ParseParameter ( s , coderateH , CoderateValues ) ; break ;
case ' D ' : s = ParseParameter ( s , coderateL , CoderateValues ) ; break ;
case ' G ' : s = ParseParameter ( s , guard , GuardValues ) ; break ;
case ' H ' : polarization = * s + + ; break ;
case ' I ' : s = ParseParameter ( s , inversion , InversionValues ) ; break ;
case ' L ' : polarization = * s + + ; break ;
case ' M ' : s = ParseParameter ( s , modulation , ModulationValues ) ; break ;
case ' O ' : s = ParseParameter ( s , rollOff , RollOffValues ) ; break ;
case ' R ' : polarization = * s + + ; break ;
case ' S ' : s = ParseParameter ( s , system , SystemValues ) ; break ;
case ' T ' : s = ParseParameter ( s , transmission , TransmissionValues ) ; break ;
case ' V ' : polarization = * s + + ; break ;
case ' Y ' : s = ParseParameter ( s , hierarchy , HierarchyValues ) ; break ;
default : esyslog ( " ERROR: unknown parameter key '%c' " , * s ) ;
return false ;
}
}
return true ;
}
2002-12-08 09:55:26 +01:00
// --- cDvbTuner -------------------------------------------------------------
2011-06-02 13:28:42 +02:00
# define TUNER_POLL_TIMEOUT 10 // ms
2002-12-08 09:55:26 +01:00
class cDvbTuner : public cThread {
private :
2005-11-26 13:39:47 +01:00
enum eTunerStatus { tsIdle , tsSet , tsTuned , tsLocked } ;
2010-02-06 15:56:01 +01:00
int device ;
2002-12-08 09:55:26 +01:00
int fd_frontend ;
2010-01-04 14:16:11 +01:00
int adapter , frontend ;
2011-06-02 13:28:42 +02:00
uint32_t subsystemId ;
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 ;
2011-09-11 14:09:03 +02:00
const cDiseqc * lastDiseqc ;
const cScr * scr ;
2002-12-08 09:55:26 +01:00
eTunerStatus tunerStatus ;
2004-10-23 10:04:01 +02:00
cMutex mutex ;
cCondVar locked ;
2005-08-21 09:25:51 +02:00
cCondVar newSet ;
2011-06-02 13:28:42 +02:00
void ClearEventQueue ( void ) const ;
bool GetFrontendStatus ( fe_status_t & Status ) const ;
2011-09-11 14:09:03 +02:00
void ExecuteDiseqc ( const cDiseqc * Diseqc , unsigned int * Frequency ) const ;
2002-12-08 09:55:26 +01:00
bool SetFrontend ( void ) ;
virtual void Action ( void ) ;
public :
2010-02-06 15:56:01 +01:00
cDvbTuner ( int Device , int Fd_Frontend , int Adapter , int Frontend , fe_delivery_system FrontendType ) ;
2002-12-08 09:55:26 +01:00
virtual ~ cDvbTuner ( ) ;
2010-02-06 14:43:42 +01:00
const cChannel * GetTransponder ( void ) const { return & channel ; }
2011-06-02 13:28:42 +02:00
uint32_t SubsystemId ( void ) const { return subsystemId ; }
2002-12-08 09:55:26 +01:00
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 ) ;
2011-06-02 13:28:42 +02:00
int GetSignalStrength ( void ) const ;
int GetSignalQuality ( void ) const ;
2002-12-08 09:55:26 +01:00
} ;
2010-02-06 15:56:01 +01:00
cDvbTuner : : cDvbTuner ( int Device , int Fd_Frontend , int Adapter , int Frontend , fe_delivery_system FrontendType )
2002-12-08 09:55:26 +01:00
{
2010-02-06 15:56:01 +01:00
device = Device ;
2002-12-08 09:55:26 +01:00
fd_frontend = Fd_Frontend ;
2010-01-04 14:16:11 +01:00
adapter = Adapter ;
frontend = Frontend ;
2002-12-08 09:55:26 +01:00
frontendType = FrontendType ;
2011-06-02 13:28:42 +02:00
subsystemId = cDvbDeviceProbe : : GetSubsystemId ( adapter , frontend ) ;
2006-01-03 10:42:47 +01:00
tuneTimeout = 0 ;
lockTimeout = 0 ;
lastTimeoutReport = 0 ;
2011-09-11 14:09:03 +02:00
lastDiseqc = NULL ;
scr = NULL ;
2002-12-08 09:55:26 +01:00
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
2010-01-04 14:16:11 +01:00
SetDescription ( " tuner on frontend %d/%d " , adapter , frontend ) ;
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 ) ;
2011-09-17 11:41:01 +02:00
/* looks like this irritates the SCR switch, so let's leave it out for now
2011-09-11 14:09:03 +02:00
if ( lastDiseqc & & lastDiseqc - > IsScr ( ) ) {
unsigned int Frequency = 0 ;
ExecuteDiseqc ( lastDiseqc , & Frequency ) ;
}
2011-09-17 11:41:01 +02:00
*/
2002-12-08 09:55:26 +01:00
}
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
// Polarization is already checked as part of the Transponder.
2010-02-28 12:19:50 +01:00
return strcmp ( channel . Parameters ( ) , Channel - > Parameters ( ) ) = = 0 ;
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 ;
}
2011-06-02 13:28:42 +02:00
void cDvbTuner : : ClearEventQueue ( void ) const
2004-10-30 14:21:13 +02:00
{
2011-06-02 13:28:42 +02:00
cPoller Poller ( fd_frontend ) ;
if ( Poller . Poll ( TUNER_POLL_TIMEOUT ) ) {
dvb_frontend_event Event ;
while ( ioctl ( fd_frontend , FE_GET_EVENT , & Event ) = = 0 )
; // just to clear the event queue - we'll read the actual status below
2004-10-30 14:21:13 +02:00
}
2011-06-02 13:28:42 +02:00
}
bool cDvbTuner : : GetFrontendStatus ( fe_status_t & Status ) const
{
ClearEventQueue ( ) ;
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 ;
}
2011-06-02 13:28:42 +02:00
//#define DEBUG_SIGNALSTRENGTH
//#define DEBUG_SIGNALQUALITY
int cDvbTuner : : GetSignalStrength ( void ) const
{
ClearEventQueue ( ) ;
uint16_t Signal ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_SIGNAL_STRENGTH , & Signal ) ! = - 1 )
break ;
if ( errno ! = EINTR )
return - 1 ;
}
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
// special treatment to map their Signal value into the range 0...0xFFFF.
2011-06-05 16:22:51 +02:00
switch ( subsystemId ) {
2011-06-11 14:34:24 +02:00
case 0x13C21019 : MaxSignal = 670 ; break ; // TT-budget S2-3200 (DVB-S/DVB-S2)
2011-06-05 16:22:51 +02:00
}
2011-06-02 13:28:42 +02:00
int s = int ( Signal ) * 100 / MaxSignal ;
if ( s > 100 )
s = 100 ;
# ifdef DEBUG_SIGNALSTRENGTH
fprintf ( stderr , " FE %d/%d: %08X S = %04X %04X %3d%% \n " , adapter , frontend , subsystemId , MaxSignal , Signal , s ) ;
# endif
return s ;
}
2011-06-05 16:22:51 +02:00
# define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
2011-06-02 13:28:42 +02:00
int cDvbTuner : : GetSignalQuality ( void ) const
{
fe_status_t Status ;
if ( GetFrontendStatus ( Status ) ) {
2011-06-05 16:22:51 +02:00
// 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...
if ( ( Status & FE_HAS_LOCK ) = = 0 ) {
if ( ( Status & FE_HAS_SIGNAL ) = = 0 )
return 0 ;
if ( ( Status & FE_HAS_CARRIER ) = = 0 )
return 1 ;
if ( ( Status & FE_HAS_VITERBI ) = = 0 )
return 2 ;
if ( ( Status & FE_HAS_SYNC ) = = 0 )
return 3 ;
return 4 ;
}
2011-06-02 13:28:42 +02:00
bool HasSnr = true ;
uint16_t Snr ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_SNR , & Snr ) ! = - 1 )
break ;
if ( errno = = EOPNOTSUPP ) {
Snr = 0xFFFF ;
HasSnr = false ;
break ;
}
if ( errno ! = EINTR )
return - 1 ;
}
bool HasBer = true ;
uint32_t Ber ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_BER , & Ber ) ! = - 1 )
break ;
if ( errno = = EOPNOTSUPP ) {
Ber = 0 ;
HasBer = false ;
break ;
}
if ( errno ! = EINTR )
return - 1 ;
}
bool HasUnc = true ;
uint32_t Unc ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_UNCORRECTED_BLOCKS , & Unc ) ! = - 1 )
break ;
if ( errno = = EOPNOTSUPP ) {
Unc = 0 ;
HasUnc = false ;
break ;
}
if ( errno ! = EINTR )
return - 1 ;
}
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
// special treatment to map their Snr value into the range 0...0xFFFF.
2011-06-05 16:22:51 +02:00
switch ( subsystemId ) {
case 0x13C21019 : MaxSnr = 200 ; break ; // TT-budget S2-3200 (DVB-S/DVB-S2)
}
2011-06-02 13:28:42 +02:00
int a = int ( Snr ) * 100 / MaxSnr ;
int b = 100 - ( Unc * 10 + ( Ber / 256 ) * 5 ) ;
if ( b < 0 )
b = 0 ;
int q = LOCK_THRESHOLD + a * b * ( 100 - LOCK_THRESHOLD ) / 100 / 100 ;
if ( q > 100 )
q = 100 ;
# ifdef DEBUG_SIGNALQUALITY
fprintf ( stderr , " FE %d/%d: %08X Q = %04X %04X %5d %5d %3d%% \n " , adapter , frontend , subsystemId , MaxSnr , Snr , HasBer ? int ( Ber ) : - 1 , HasUnc ? int ( Unc ) : - 1 , q ) ;
# endif
return q ;
}
return - 1 ;
}
2002-12-08 09:55:26 +01:00
static unsigned int FrequencyToHz ( unsigned int f )
{
while ( f & & f < 1000000 )
f * = 1000 ;
return f ;
}
2011-09-11 14:09:03 +02:00
void cDvbTuner : : ExecuteDiseqc ( const cDiseqc * Diseqc , unsigned int * Frequency ) const
{
struct dvb_diseqc_master_cmd cmd ;
const char * CurrentAction = NULL ;
for ( ; ; ) {
cmd . msg_len = sizeof ( cmd . msg ) ;
cDiseqc : : eDiseqcActions da = Diseqc - > Execute ( & CurrentAction , cmd . msg , & cmd . msg_len , scr , Frequency ) ;
if ( da = = cDiseqc : : daNone )
break ;
switch ( da ) {
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 : CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_MASTER_CMD , & cmd ) ) ; break ;
default : esyslog ( " ERROR: unknown diseqc command %d " , da ) ;
}
}
if ( scr )
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ; // makes sure we don't block the bus!
}
2002-12-08 09:55:26 +01:00
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 ) { \
2010-01-04 14:16:11 +01:00
esyslog ( " ERROR: too many tuning commands on frontend %d/%d " , adapter , frontend ) ; \
2008-12-13 12:22:36 +01:00
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 ) {
2010-01-04 14:16:11 +01:00
esyslog ( " ERROR: frontend %d/%d: %m " , adapter , frontend ) ;
2008-12-13 12:22:36 +01:00
return false ;
}
CmdSeq . num = 0 ;
2002-12-08 09:55:26 +01:00
2010-02-28 12:19:50 +01:00
cDvbTransponderParameters dtp ( channel . Parameters ( ) ) ;
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 ) {
2011-09-11 14:09:03 +02:00
if ( const cDiseqc * diseqc = Diseqcs . Get ( device , channel . Source ( ) , frequency , dtp . Polarization ( ) , & scr ) ) {
2008-04-12 13:39:12 +02:00
frequency - = diseqc - > Lof ( ) ;
2011-09-11 14:09:03 +02:00
if ( diseqc ! = lastDiseqc | | diseqc - > IsScr ( ) ) {
ExecuteDiseqc ( diseqc , & frequency ) ;
if ( frequency = = 0 )
return false ;
lastDiseqc = diseqc ;
}
2008-04-12 13:39:12 +02:00
}
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 ;
}
2010-02-28 12:19:50 +01:00
int volt = ( dtp . Polarization ( ) = = ' v ' | | dtp . Polarization ( ) = = ' V ' | | dtp . Polarization ( ) = = ' r ' | | dtp . Polarization ( ) = = ' R ' ) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18 ;
2008-04-12 13:39:12 +02:00
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)
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_DELIVERY_SYSTEM , dtp . System ( ) ) ;
2008-12-13 12:22:36 +01:00
SETCMD ( DTV_FREQUENCY , frequency * 1000UL ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_MODULATION , dtp . Modulation ( ) ) ;
2008-12-13 12:22:36 +01:00
SETCMD ( DTV_SYMBOL_RATE , channel . Srate ( ) * 1000UL ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_INNER_FEC , dtp . CoderateH ( ) ) ;
SETCMD ( DTV_INVERSION , dtp . Inversion ( ) ) ;
if ( dtp . System ( ) = = SYS_DVBS2 ) {
2008-12-13 12:22:36 +01:00
if ( frontendType = = SYS_DVBS2 ) {
// DVB-S2
SETCMD ( DTV_PILOT , PILOT_AUTO ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_ROLLOFF , dtp . RollOff ( ) ) ;
2008-12-13 12:22:36 +01:00
}
else {
2010-01-04 14:16:11 +01:00
esyslog ( " ERROR: frontend %d/%d doesn't provide DVB-S2 " , adapter , frontend ) ;
2008-12-13 12:22:36 +01:00
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 ( ) ) ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_INVERSION , dtp . Inversion ( ) ) ;
2008-12-13 12:22:36 +01:00
SETCMD ( DTV_SYMBOL_RATE , channel . Srate ( ) * 1000UL ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_INNER_FEC , dtp . CoderateH ( ) ) ;
SETCMD ( DTV_MODULATION , dtp . 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 ( ) ) ) ;
2010-02-28 12:19:50 +01:00
SETCMD ( DTV_INVERSION , dtp . Inversion ( ) ) ;
SETCMD ( DTV_BANDWIDTH_HZ , dtp . Bandwidth ( ) ) ;
SETCMD ( DTV_CODE_RATE_HP , dtp . CoderateH ( ) ) ;
SETCMD ( DTV_CODE_RATE_LP , dtp . CoderateL ( ) ) ;
SETCMD ( DTV_MODULATION , dtp . Modulation ( ) ) ;
SETCMD ( DTV_TRANSMISSION_MODE , dtp . Transmission ( ) ) ;
SETCMD ( DTV_GUARD_INTERVAL , dtp . Guard ( ) ) ;
SETCMD ( DTV_HIERARCHY , dtp . Hierarchy ( ) ) ;
2008-04-12 13:39:12 +02:00
tuneTimeout = DVBT_TUNE_TIMEOUT ;
lockTimeout = DVBT_LOCK_TIMEOUT ;
}
2010-03-06 12:01:17 +01:00
else if ( frontendType = = SYS_ATSC ) {
// ATSC
SETCMD ( DTV_DELIVERY_SYSTEM , frontendType ) ;
SETCMD ( DTV_FREQUENCY , FrequencyToHz ( channel . Frequency ( ) ) ) ;
SETCMD ( DTV_INVERSION , dtp . Inversion ( ) ) ;
SETCMD ( DTV_MODULATION , dtp . Modulation ( ) ) ;
2011-06-02 13:28:42 +02:00
2010-03-06 12:01:17 +01:00
tuneTimeout = ATSC_TUNE_TIMEOUT ;
2011-06-02 13:28:42 +02:00
lockTimeout = ATSC_LOCK_TIMEOUT ;
2010-03-06 12:01:17 +01:00
}
2008-04-12 13:39:12 +02:00
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 ) {
2010-01-04 14:16:11 +01:00
esyslog ( " ERROR: frontend %d/%d: %m " , adapter , frontend ) ;
2002-12-08 09:55:26 +01:00
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 ;
2011-06-02 13:28:42 +02:00
if ( GetFrontendStatus ( NewStatus ) )
2006-01-04 11:48:38 +01:00
Status = NewStatus ;
2005-08-21 09:25:51 +02:00
cMutexLock MutexLock ( & mutex ) ;
switch ( tunerStatus ) {
case tsIdle :
break ;
case tsSet :
tunerStatus = SetFrontend ( ) ? tsTuned : tsIdle ;
2011-09-17 12:53:46 +02:00
Timer . Set ( tuneTimeout + ( scr ? rand ( ) % SCR_RANDOM_TIMEOUT : 0 ) ) ;
2005-08-21 09:25:51 +02:00
continue ;
case tsTuned :
2006-01-01 12:22:18 +01:00
if ( Timer . TimedOut ( ) ) {
tunerStatus = tsSet ;
2011-09-11 14:09:03 +02:00
lastDiseqc = NULL ;
2006-01-03 10:42:47 +01:00
if ( time ( NULL ) - lastTimeoutReport > 60 ) { // let's not get too many of these
2010-01-04 14:16:11 +01:00
isyslog ( " frontend %d/%d timed out while tuning to channel %d, tp %d " , adapter , frontend , 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 ;
2011-09-11 14:09:03 +02:00
lastDiseqc = NULL ;
2010-01-04 14:16:11 +01:00
isyslog ( " frontend %d/%d was reinitialized " , adapter , frontend ) ;
2006-01-04 11:48:38 +01:00
lastTimeoutReport = 0 ;
continue ;
}
else if ( Status & FE_HAS_LOCK ) {
if ( LostLock ) {
2010-01-04 14:16:11 +01:00
isyslog ( " frontend %d/%d regained lock on channel %d, tp %d " , adapter , frontend , 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 ;
2010-01-04 14:16:11 +01:00
isyslog ( " frontend %d/%d lost lock on channel %d, tp %d " , adapter , frontend , 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
}
}
2010-02-28 12:19:50 +01:00
// --- cDvbSourceParam -------------------------------------------------------
class cDvbSourceParam : public cSourceParam {
private :
int param ;
2010-03-06 15:19:17 +01:00
int srate ;
2010-02-28 12:19:50 +01:00
cDvbTransponderParameters dtp ;
public :
cDvbSourceParam ( char Source , const char * Description ) ;
virtual void SetData ( cChannel * Channel ) ;
virtual void GetData ( cChannel * Channel ) ;
virtual cOsdItem * GetOsdItem ( void ) ;
} ;
cDvbSourceParam : : cDvbSourceParam ( char Source , const char * Description )
: cSourceParam ( Source , Description )
{
param = 0 ;
2010-03-06 15:19:17 +01:00
srate = 0 ;
2010-02-28 12:19:50 +01:00
}
void cDvbSourceParam : : SetData ( cChannel * Channel )
{
2010-03-06 15:19:17 +01:00
srate = Channel - > Srate ( ) ;
dtp . Parse ( Channel - > Parameters ( ) ) ;
2010-02-28 12:19:50 +01:00
param = 0 ;
}
void cDvbSourceParam : : GetData ( cChannel * Channel )
{
2010-03-06 15:19:17 +01:00
Channel - > SetTransponderData ( Channel - > Source ( ) , Channel - > Frequency ( ) , srate , dtp . ToString ( Source ( ) ) , true ) ;
2010-02-28 12:19:50 +01:00
}
cOsdItem * cDvbSourceParam : : GetOsdItem ( void )
{
char type = Source ( ) ;
# undef ST
# define ST(s) if (strchr(s, type))
switch ( param + + ) {
2010-03-07 13:59:11 +01:00
case 0 : ST ( " S " ) return new cMenuEditChrItem ( tr ( " Polarization " ) , & dtp . polarization , " HVLR " ) ; else return GetOsdItem ( ) ;
2010-03-06 12:01:17 +01:00
case 1 : ST ( " S " ) return new cMenuEditMapItem ( tr ( " System " ) , & dtp . system , SystemValues ) ; else return GetOsdItem ( ) ;
2010-03-06 15:19:17 +01:00
case 2 : ST ( " CS " ) return new cMenuEditIntItem ( tr ( " Srate " ) , & srate ) ; else return GetOsdItem ( ) ;
2010-03-06 12:01:17 +01:00
case 3 : ST ( " ACST " ) return new cMenuEditMapItem ( tr ( " Inversion " ) , & dtp . inversion , InversionValues ) ; else return GetOsdItem ( ) ;
case 4 : ST ( " CST " ) return new cMenuEditMapItem ( tr ( " CoderateH " ) , & dtp . coderateH , CoderateValues ) ; else return GetOsdItem ( ) ;
case 5 : ST ( " T " ) return new cMenuEditMapItem ( tr ( " CoderateL " ) , & dtp . coderateL , CoderateValues ) ; else return GetOsdItem ( ) ;
case 6 : ST ( " ACST " ) return new cMenuEditMapItem ( tr ( " Modulation " ) , & dtp . modulation , ModulationValues ) ; else return GetOsdItem ( ) ;
case 7 : ST ( " T " ) return new cMenuEditMapItem ( tr ( " Bandwidth " ) , & dtp . bandwidth , BandwidthValues ) ; else return GetOsdItem ( ) ;
case 8 : ST ( " T " ) return new cMenuEditMapItem ( tr ( " Transmission " ) , & dtp . transmission , TransmissionValues ) ; else return GetOsdItem ( ) ;
case 9 : ST ( " T " ) return new cMenuEditMapItem ( tr ( " Guard " ) , & dtp . guard , GuardValues ) ; else return GetOsdItem ( ) ;
case 10 : ST ( " T " ) return new cMenuEditMapItem ( tr ( " Hierarchy " ) , & dtp . hierarchy , HierarchyValues ) ; else return GetOsdItem ( ) ;
case 11 : ST ( " S " ) return new cMenuEditMapItem ( tr ( " Rolloff " ) , & dtp . rollOff , RollOffValues ) ; else return GetOsdItem ( ) ;
2010-02-28 12:19:50 +01:00
default : return NULL ;
}
return NULL ;
}
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
} ;
2010-01-04 14:16:11 +01:00
cDvbDevice : : cDvbDevice ( int Adapter , int Frontend )
2002-08-04 14:57:29 +02:00
{
2010-01-04 14:16:11 +01:00
adapter = Adapter ;
frontend = Frontend ;
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:
2010-01-04 14:16:11 +01:00
int fd_frontend = DvbOpen ( DEV_DVB_FRONTEND , adapter , frontend , O_RDWR | O_NONBLOCK ) ;
2003-10-17 15:36:13 +02:00
2007-01-07 14:46:14 +01:00
// Common Interface:
2010-01-04 14:16:11 +01:00
fd_ca = DvbOpen ( DEV_DVB_CA , adapter , frontend , O_RDWR ) ;
2007-01-07 14:46:14 +01:00
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 ;
2010-01-04 14:16:11 +01:00
default : esyslog ( " ERROR: unknown frontend type %d on frontend %d/%d " , frontendInfo . type , adapter , frontend ) ;
2008-12-13 12:22:36 +01:00
}
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 + + ;
2010-04-11 13:02:42 +02:00
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 ) ) ; }
2010-05-01 09:47:13 +02:00
if ( frontendInfo . caps & FE_CAN_TURBO_FEC ) { numProvidedSystems + + ; p + = sprintf ( p , " ,%s " , " TURBO_FEC " ) ; }
2010-04-11 13:02:42 +02:00
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 ) ;
2010-02-07 13:21:05 +01:00
dvbTuner = new cDvbTuner ( CardIndex ( ) + 1 , fd_frontend , adapter , frontend , frontendType ) ;
2008-12-13 12:22:36 +01:00
}
2002-08-04 14:57:29 +02:00
}
else
2010-01-04 14:16:11 +01:00
esyslog ( " ERROR: can't open DVB device %d/%d " , adapter , frontend ) ;
2002-08-04 14:57:29 +02:00
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...
}
2010-01-04 14:16:11 +01:00
cString cDvbDevice : : DvbName ( const char * Name , int Adapter , int Frontend )
2002-08-04 14:57:29 +02:00
{
2010-01-04 14:16:11 +01:00
return cString : : sprintf ( " %s%d/%s%d " , DEV_DVB_ADAPTER , Adapter , Name , Frontend ) ;
2009-12-31 15:38:18 +01:00
}
2010-01-04 14:16:11 +01:00
int cDvbDevice : : DvbOpen ( const char * Name , int Adapter , int Frontend , int Mode , bool ReportError )
2009-12-31 15:38:18 +01:00
{
2010-01-04 14:16:11 +01:00
cString FileName = DvbName ( Name , Adapter , Frontend ) ;
2009-12-31 15:38:18 +01:00
int fd = open ( FileName , Mode ) ;
if ( fd < 0 & & ReportError )
LOG_ERROR_STR ( * FileName ) ;
return fd ;
}
2010-01-04 14:16:11 +01:00
bool cDvbDevice : : Exists ( int Adapter , int Frontend )
2009-12-31 15:38:18 +01:00
{
2010-01-04 14:16:11 +01:00
cString FileName = DvbName ( DEV_DVB_FRONTEND , Adapter , Frontend ) ;
2002-08-04 14:57:29 +02:00
if ( access ( FileName , F_OK ) = = 0 ) {
int f = open ( FileName , O_RDONLY ) ;
if ( f > = 0 ) {
close ( f ) ;
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 ;
}
2010-01-04 14:16:11 +01:00
bool cDvbDevice : : Probe ( int Adapter , int Frontend )
{
cString FileName = DvbName ( DEV_DVB_FRONTEND , Adapter , Frontend ) ;
dsyslog ( " probing %s " , * FileName ) ;
for ( cDvbDeviceProbe * dp = DvbDeviceProbes . First ( ) ; dp ; dp = DvbDeviceProbes . Next ( dp ) ) {
if ( dp - > Probe ( Adapter , Frontend ) )
return true ; // a plugin has created the actual device
}
dsyslog ( " creating cDvbDevice " ) ;
new cDvbDevice ( Adapter , Frontend ) ; // it's a "budget" device
return true ;
}
2002-08-04 14:57:29 +02:00
bool cDvbDevice : : Initialize ( void )
{
2010-03-06 12:01:17 +01:00
new cDvbSourceParam ( ' A ' , " ATSC " ) ;
2010-02-28 12:19:50 +01:00
new cDvbSourceParam ( ' C ' , " DVB-C " ) ;
new cDvbSourceParam ( ' S ' , " DVB-S " ) ;
new cDvbSourceParam ( ' T ' , " DVB-T " ) ;
2010-01-04 14:16:11 +01:00
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 : ;
2002-08-04 14:57:29 +02:00
}
2010-01-04 14:16:11 +01:00
LastAdapter :
NextCardIndex ( MAXDVBDEVICES - Checked ) ; // skips the rest
if ( Found > 0 )
isyslog ( " found %d DVB device%s " , Found , Found > 1 ? " s " : " " ) ;
2002-08-04 14:57:29 +02:00
else
isyslog ( " no DVB device found " ) ;
2010-01-04 14:16:11 +01:00
return Found > 0 ;
2002-08-04 14:57:29 +02:00
}
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 ) {
2010-01-04 14:16:11 +01:00
Handle - > handle = DvbOpen ( DEV_DVB_DEMUX , adapter , frontend , 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 )
{
2010-01-04 14:16:11 +01:00
cString FileName = DvbName ( DEV_DVB_DEMUX , adapter , frontend ) ;
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
2010-03-06 12:01:17 +01:00
| | type = = cSource : : stAtsc & & ( frontendType = = SYS_ATSC )
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
2010-02-28 12:19:50 +01:00
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
2010-04-04 11:37:10 +02:00
if ( dtp . System ( ) = = SYS_DVBS2 & & frontendType = = SYS_DVBS | |
2010-05-01 09:47:13 +02:00
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 ) | |
2010-04-04 11:37:10 +02:00
dtp . Modulation ( ) = = QAM_AUTO & & ! ( frontendInfo . caps & FE_CAN_QAM_AUTO ) | |
2010-05-01 09:47:13 +02:00
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
2008-04-12 13:39:12 +02:00
return false ; // requires modulation system which frontend doesn't provide
2010-04-05 20:13:05 +02:00
if ( ! cSource : : IsSat ( Channel - > Source ( ) ) | |
2011-09-11 14:09:03 +02:00
! Setup . DiSEqC | | Diseqcs . Get ( CardIndex ( ) + 1 , Channel - > Source ( ) , Channel - > Frequency ( ) , dtp . Polarization ( ) , NULL ) )
2010-02-06 14:23:03 +01:00
return DeviceHooksProvidesTransponder ( Channel ) ;
return false ;
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
2011-06-02 13:28:42 +02:00
if ( dvbTuner & & 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 ;
}
2011-08-26 13:03:14 +02:00
bool cDvbDevice : : ProvidesEIT ( void ) const
{
return dvbTuner ! = NULL ;
}
2008-04-12 13:39:12 +02:00
int cDvbDevice : : NumProvidedSystems ( void ) const
{
return numProvidedSystems ;
}
2011-06-02 13:28:42 +02:00
int cDvbDevice : : SignalStrength ( void ) const
{
return dvbTuner ? dvbTuner - > GetSignalStrength ( ) : - 1 ;
}
int cDvbDevice : : SignalQuality ( void ) const
{
return dvbTuner ? dvbTuner - > GetSignalQuality ( ) : - 1 ;
}
2010-02-06 14:43:42 +01:00
const cChannel * cDvbDevice : : GetCurrentlyTunedTransponder ( void ) const
{
2011-06-02 13:28:42 +02:00
return dvbTuner ? dvbTuner - > GetTransponder ( ) : NULL ;
2010-02-06 14:43:42 +01:00
}
2006-04-09 09:12:47 +02:00
bool cDvbDevice : : IsTunedToTransponder ( const cChannel * Channel )
{
2011-06-02 13:28:42 +02:00
return dvbTuner ? dvbTuner - > IsTunedTo ( Channel ) : false ;
2006-04-09 09:12:47 +02:00
}
2002-09-04 17:26:02 +02:00
bool cDvbDevice : : SetChannelDevice ( const cChannel * Channel , bool LiveView )
{
2011-06-02 13:28:42 +02:00
if ( dvbTuner )
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 ( ) ;
2010-01-04 14:16:11 +01:00
fd_dvr = DvbOpen ( DEV_DVB_DVR , adapter , frontend , 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 ) ;
}
2011-06-02 13:28:42 +02:00
uint32_t cDvbDeviceProbe : : GetSubsystemId ( int Adapter , int Frontend )
{
cString FileName ;
cReadLine ReadLine ;
FILE * f = NULL ;
uint32_t SubsystemId = 0 ;
FileName = cString : : sprintf ( " /sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor " , Adapter , Frontend ) ;
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId = strtoul ( s , NULL , 0 ) < < 16 ;
fclose ( f ) ;
}
FileName = cString : : sprintf ( " /sys/class/dvb/dvb%d.frontend%d/device/subsystem_device " , Adapter , Frontend ) ;
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId | = strtoul ( s , NULL , 0 ) ;
fclose ( f ) ;
}
return SubsystemId ;
}