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 .
*
2013-03-07 09:51:06 +01:00
* $ Id : dvbdevice . c 2.84 2013 / 03 / 07 09 : 42 : 29 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
2012-10-04 12:44:13 +02:00
# if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) < 0x0508
# define DTV_STREAM_ID DTV_DVBT2_PLP_ID
2013-03-07 09:51:06 +01:00
# define FE_CAN_MULTISTREAM 0x4000000
2012-10-04 12:44:13 +02:00
# endif
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 [ ] = {
2012-01-11 13:22:51 +01:00
{ 5 , 5000000 , " 5 MHz " } ,
{ 6 , 6000000 , " 6 MHz " } ,
{ 7 , 7000000 , " 7 MHz " } ,
{ 8 , 8000000 , " 8 MHz " } ,
{ 10 , 10000000 , " 10 MHz " } ,
{ 1712 , 1712000 , " 1.712 MHz " } ,
2010-02-28 12:19:50 +01:00
{ - 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 " } ,
2012-01-11 13:22:51 +01:00
{ 7 , APSK_32 , " 32APSK " } ,
2010-02-28 12:19:50 +01:00
{ 10 , VSB_8 , " VSB8 " } ,
{ 11 , VSB_16 , " VSB16 " } ,
2012-01-11 13:22:51 +01:00
{ 12 , DQPSK , " DQPSK " } ,
{ 999 , QAM_AUTO , trNOOP ( " auto " ) } ,
2010-02-28 12:19:50 +01:00
{ - 1 , 0 , NULL }
} ;
2012-01-08 09:27:59 +01:00
# define DVB_SYSTEM_1 0 // see also nit.c
# define DVB_SYSTEM_2 1
const tDvbParameterMap SystemValuesSat [ ] = {
{ 0 , DVB_SYSTEM_1 , " DVB-S " } ,
{ 1 , DVB_SYSTEM_2 , " DVB-S2 " } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap SystemValuesTerr [ ] = {
{ 0 , DVB_SYSTEM_1 , " DVB-T " } ,
{ 1 , DVB_SYSTEM_2 , " DVB-T2 " } ,
2010-02-28 12:19:50 +01:00
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap TransmissionValues [ ] = {
2012-01-11 13:22:51 +01:00
{ 1 , TRANSMISSION_MODE_1K , " 1K " } ,
2010-02-28 12:19:50 +01:00
{ 2 , TRANSMISSION_MODE_2K , " 2K " } ,
2012-01-11 13:22:51 +01:00
{ 4 , TRANSMISSION_MODE_4K , " 4K " } ,
2010-02-28 12:19:50 +01:00
{ 8 , TRANSMISSION_MODE_8K , " 8K " } ,
2012-01-11 13:22:51 +01:00
{ 16 , TRANSMISSION_MODE_16K , " 16K " } ,
{ 32 , TRANSMISSION_MODE_32K , " 32K " } ,
2010-02-28 12:19:50 +01:00
{ 999 , TRANSMISSION_MODE_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
const tDvbParameterMap GuardValues [ ] = {
2012-01-11 13:22:51 +01:00
{ 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 " } ,
{ 128 , GUARD_INTERVAL_1_128 , " 1/128 " } ,
{ 19128 , GUARD_INTERVAL_19_128 , " 19/128 " } ,
{ 19256 , GUARD_INTERVAL_19_256 , " 19/256 " } ,
{ 999 , GUARD_INTERVAL_AUTO , trNOOP ( " auto " ) } ,
2010-02-28 12:19:50 +01:00
{ - 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 ;
2012-01-08 09:27:59 +01:00
system = DVB_SYSTEM_1 ;
2010-02-28 12:19:50 +01:00
transmission = TRANSMISSION_MODE_AUTO ;
guard = GUARD_INTERVAL_AUTO ;
hierarchy = HIERARCHY_AUTO ;
rollOff = ROLLOFF_AUTO ;
2013-03-07 09:51:06 +01:00
streamId = 0 ;
2010-02-28 12:19:50 +01:00
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
{
2012-01-13 12:27:18 +01:00
# define ST(s) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*')))
2010-02-28 12:19:50 +01:00
char buffer [ 64 ] ;
char * q = buffer ;
* q = 0 ;
2012-01-13 12:27:18 +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 2 " ) q + = PrintParameter ( q , ' O ' , MapToUser ( rollOff , RollOffValues ) ) ;
2013-03-07 09:51:06 +01:00
ST ( " ST2 " ) q + = PrintParameter ( q , ' P ' , streamId ) ;
2012-01-13 12:27:18 +01:00
ST ( " ST* " ) q + = PrintParameter ( q , ' S ' , MapToUser ( system , SystemValuesSat ) ) ; // we only need the numerical value, so Sat or Terr doesn't matter
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 ) {
2012-01-11 13:22:51 +01:00
Value = Map ? MapToDriver ( n , Map ) : n ;
2010-02-28 12:19:50 +01:00
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 ;
2012-12-03 14:13:12 +01:00
case ' H ' : polarization = ' H ' ; s + + ; break ;
2010-02-28 12:19:50 +01:00
case ' I ' : s = ParseParameter ( s , inversion , InversionValues ) ; break ;
2012-12-03 14:13:12 +01:00
case ' L ' : polarization = ' L ' ; s + + ; break ;
2010-02-28 12:19:50 +01:00
case ' M ' : s = ParseParameter ( s , modulation , ModulationValues ) ; break ;
case ' O ' : s = ParseParameter ( s , rollOff , RollOffValues ) ; break ;
2013-03-07 09:51:06 +01:00
case ' P ' : s = ParseParameter ( s , streamId ) ; break ;
2012-12-03 14:13:12 +01:00
case ' R ' : polarization = ' R ' ; s + + ; break ;
2012-01-08 09:27:59 +01:00
case ' S ' : s = ParseParameter ( s , system , SystemValuesSat ) ; break ; // we only need the numerical value, so Sat or Terr doesn't matter
2010-02-28 12:19:50 +01:00
case ' T ' : s = ParseParameter ( s , transmission , TransmissionValues ) ; break ;
2012-12-03 14:13:12 +01:00
case ' V ' : polarization = ' V ' ; s + + ; break ;
2010-02-28 12:19:50 +01:00
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 :
2011-12-04 12:45:26 +01:00
static cMutex bondMutex ;
2005-11-26 13:39:47 +01:00
enum eTunerStatus { tsIdle , tsSet , tsTuned , tsLocked } ;
2012-04-04 09:52:15 +02:00
int frontendType ;
2011-12-04 12:45:26 +01:00
const cDvbDevice * 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 ;
2002-12-08 09:55:26 +01:00
cChannel channel ;
2011-09-11 14:09:03 +02:00
const cDiseqc * lastDiseqc ;
const cScr * scr ;
2012-01-08 09:27:59 +01:00
bool lnbPowerTurnedOn ;
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-12-04 12:45:26 +01:00
cDvbTuner * bondedTuner ;
bool bondedMaster ;
2012-01-08 09:27:59 +01:00
bool SetFrontendType ( const cChannel * Channel ) ;
2011-12-04 12:45:26 +01:00
cString GetBondingParams ( const cChannel * Channel = NULL ) const ;
2013-02-19 10:24:39 +01:00
cDvbTuner * GetBondedMaster ( void ) ;
2013-02-20 09:12:12 +01:00
bool IsBondedMaster ( void ) const { return ! bondedTuner | | bondedMaster ; }
2011-06-02 13:28:42 +02:00
void ClearEventQueue ( void ) const ;
bool GetFrontendStatus ( fe_status_t & Status ) const ;
2011-12-04 12:45:26 +01:00
void ExecuteDiseqc ( const cDiseqc * Diseqc , unsigned int * Frequency ) ;
void ResetToneAndVoltage ( void ) ;
2002-12-08 09:55:26 +01:00
bool SetFrontend ( void ) ;
virtual void Action ( void ) ;
public :
2012-01-08 09:27:59 +01:00
cDvbTuner ( const cDvbDevice * Device , int Fd_Frontend , int Adapter , int Frontend ) ;
2002-12-08 09:55:26 +01:00
virtual ~ cDvbTuner ( ) ;
2012-04-04 09:52:15 +02:00
int FrontendType ( void ) const { return frontendType ; }
2011-12-04 12:45:26 +01:00
bool Bond ( cDvbTuner * Tuner ) ;
void UnBond ( void ) ;
bool BondingOk ( const cChannel * Channel , bool ConsiderOccupied = false ) const ;
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 ;
2011-12-04 12:45:26 +01:00
void SetChannel ( 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
} ;
2011-12-04 12:45:26 +01:00
cMutex cDvbTuner : : bondMutex ;
2012-01-08 09:27:59 +01:00
cDvbTuner : : cDvbTuner ( const cDvbDevice * Device , int Fd_Frontend , int Adapter , int Frontend )
2002-12-08 09:55:26 +01:00
{
2012-04-04 09:52:15 +02:00
frontendType = SYS_UNDEFINED ;
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 ;
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 ;
2012-01-08 09:27:59 +01:00
lnbPowerTurnedOn = false ;
2002-12-08 09:55:26 +01:00
tunerStatus = tsIdle ;
2011-12-04 12:45:26 +01:00
bondedTuner = NULL ;
bondedMaster = false ;
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-12-04 12:45:26 +01:00
UnBond ( ) ;
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
}
2011-12-04 12:45:26 +01:00
bool cDvbTuner : : Bond ( cDvbTuner * Tuner )
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( ! bondedTuner ) {
2012-01-08 09:27:59 +01:00
ResetToneAndVoltage ( ) ;
bondedMaster = false ; // makes sure we don't disturb an existing master
bondedTuner = Tuner - > bondedTuner ? Tuner - > bondedTuner : Tuner ;
Tuner - > bondedTuner = this ;
dsyslog ( " tuner %d/%d bonded with tuner %d/%d " , adapter , frontend , bondedTuner - > adapter , bondedTuner - > frontend ) ;
return true ;
2011-12-04 12:45:26 +01:00
}
else
esyslog ( " ERROR: tuner %d/%d already bonded with tuner %d/%d, can't bond with tuner %d/%d " , adapter , frontend , bondedTuner - > adapter , bondedTuner - > frontend , Tuner - > adapter , Tuner - > frontend ) ;
return false ;
}
void cDvbTuner : : UnBond ( void )
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( cDvbTuner * t = bondedTuner ) {
dsyslog ( " tuner %d/%d unbonded from tuner %d/%d " , adapter , frontend , bondedTuner - > adapter , bondedTuner - > frontend ) ;
while ( t - > bondedTuner ! = this )
t = t - > bondedTuner ;
if ( t = = bondedTuner )
t - > bondedTuner = NULL ;
else
t - > bondedTuner = bondedTuner ;
bondedMaster = false ; // another one will automatically become master whenever necessary
bondedTuner = NULL ;
}
}
cString cDvbTuner : : GetBondingParams ( const cChannel * Channel ) const
{
if ( ! Channel )
Channel = & channel ;
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
if ( Setup . DiSEqC ) {
if ( const cDiseqc * diseqc = Diseqcs . Get ( device - > CardIndex ( ) + 1 , Channel - > Source ( ) , Channel - > Frequency ( ) , dtp . Polarization ( ) , NULL ) )
return diseqc - > Commands ( ) ;
}
else {
2012-10-07 11:13:18 +02:00
bool ToneOff = Channel - > Frequency ( ) < Setup . LnbSLOF ;
2011-12-04 12:45:26 +01:00
bool VoltOff = dtp . Polarization ( ) = = ' V ' | | dtp . Polarization ( ) = = ' R ' ;
return cString : : sprintf ( " %c %c " , ToneOff ? ' t ' : ' T ' , VoltOff ? ' v ' : ' V ' ) ;
}
return " " ;
}
bool cDvbTuner : : BondingOk ( const cChannel * Channel , bool ConsiderOccupied ) const
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( cDvbTuner * t = bondedTuner ) {
cString BondingParams = GetBondingParams ( Channel ) ;
do {
2012-03-07 13:46:13 +01:00
if ( t - > device - > Priority ( ) > IDLEPRIORITY | | ConsiderOccupied & & t - > device - > Occupied ( ) ) {
2011-12-04 12:45:26 +01:00
if ( strcmp ( BondingParams , t - > GetBondingParams ( ) ) ! = 0 )
return false ;
}
t = t - > bondedTuner ;
} while ( t ! = bondedTuner ) ;
}
return true ;
}
cDvbTuner * cDvbTuner : : GetBondedMaster ( void )
{
if ( ! bondedTuner )
return this ; // an unbonded tuner is always "master"
cMutexLock MutexLock ( & bondMutex ) ;
2012-05-09 08:33:59 +02:00
if ( bondedMaster )
return this ;
2011-12-04 12:45:26 +01:00
// This tuner is bonded, but it's not the master, so let's see if there is a master at all:
if ( cDvbTuner * t = bondedTuner ) {
while ( t ! = this ) {
if ( t - > bondedMaster )
return t ;
t = t - > bondedTuner ;
}
}
// None of the other bonded tuners is master, so make this one the master:
2012-05-09 08:33:59 +02:00
bondedMaster = true ;
dsyslog ( " tuner %d/%d is now bonded master " , adapter , frontend ) ;
return this ;
2011-12-04 12:45:26 +01: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
}
2011-12-04 12:45:26 +01:00
void cDvbTuner : : SetChannel ( const cChannel * Channel )
2002-12-08 09:55:26 +01:00
{
2011-12-04 12:45:26 +01:00
if ( Channel ) {
if ( bondedTuner ) {
cMutexLock MutexLock ( & bondMutex ) ;
cDvbTuner * BondedMaster = GetBondedMaster ( ) ;
if ( BondedMaster = = this ) {
if ( strcmp ( GetBondingParams ( Channel ) , GetBondingParams ( ) ) ! = 0 ) {
// switching to a completely different band, so set all others to idle:
for ( cDvbTuner * t = bondedTuner ; t & & t ! = this ; t = t - > bondedTuner )
t - > SetChannel ( NULL ) ;
}
}
2012-01-15 14:31:47 +01:00
else if ( strcmp ( GetBondingParams ( Channel ) , BondedMaster - > GetBondingParams ( ) ) ! = 0 )
2011-12-04 12:45:26 +01:00
BondedMaster - > SetChannel ( Channel ) ;
}
cMutexLock MutexLock ( & mutex ) ;
if ( ! IsTunedTo ( Channel ) )
tunerStatus = tsSet ;
channel = * Channel ;
lastTimeoutReport = 0 ;
newSet . Broadcast ( ) ;
}
else {
cMutexLock MutexLock ( & mutex ) ;
tunerStatus = tsIdle ;
ResetToneAndVoltage ( ) ;
}
2012-01-17 15:37:52 +01:00
if ( bondedTuner & & device - > IsPrimaryDevice ( ) )
cDevice : : PrimaryDevice ( ) - > DelLivePids ( ) ; // 'device' is const, so we must do it this way
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 ;
}
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
bool HasSnr = true ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
uint16_t Snr ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_SNR , & Snr ) ! = - 1 )
break ;
2013-02-14 13:21:34 +01:00
if ( errno ! = EINTR ) {
2011-06-02 13:28:42 +02:00
Snr = 0xFFFF ;
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
HasSnr = false ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
break ;
}
}
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
bool HasBer = true ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
uint32_t Ber ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_BER , & Ber ) ! = - 1 )
break ;
2013-02-14 13:21:34 +01:00
if ( errno ! = EINTR ) {
2011-06-02 13:28:42 +02:00
Ber = 0 ;
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
HasBer = false ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
break ;
}
}
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
bool HasUnc = true ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
uint32_t Unc ;
while ( 1 ) {
if ( ioctl ( fd_frontend , FE_READ_UNCORRECTED_BLOCKS , & Unc ) ! = - 1 )
break ;
2013-02-14 13:21:34 +01:00
if ( errno ! = EINTR ) {
2011-06-02 13:28:42 +02:00
Unc = 0 ;
2012-10-07 11:13:18 +02:00
# ifdef DEBUG_SIGNALQUALITY
2011-06-02 13:28:42 +02:00
HasUnc = false ;
2012-10-07 11:13:18 +02:00
# endif
2011-06-02 13:28:42 +02:00
break ;
}
}
2013-02-19 12:16:42 +01:00
uint16_t MinSnr = 0x0000 ;
2011-06-02 13:28:42 +02:00
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 ) {
2013-02-19 12:16:42 +01:00
case 0x13C21019 : if ( frontendType = = SYS_DVBS2 ) { // TT-budget S2-3200 (DVB-S/DVB-S2)
MinSnr = 10 ;
MaxSnr = 70 ;
}
else
MaxSnr = 200 ;
break ;
2012-12-30 13:10:44 +01:00
case 0x20130245 : // PCTV Systems PCTV 73ESE
case 0x2013024F : MaxSnr = 255 ; break ; // PCTV Systems nanoStick T2 290e
2011-06-05 16:22:51 +02:00
}
2013-02-19 12:16:42 +01:00
int a = int ( constrain ( Snr , MinSnr , MaxSnr ) ) * 100 / ( MaxSnr - MinSnr ) ;
2011-06-02 13:28:42 +02:00
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
2011-12-10 15:01:23 +01:00
fprintf ( stderr , " FE %d/%d: %08X Q = %04X %04X %d %5d %5d %3d%% \n " , adapter , frontend , subsystemId , MaxSnr , Snr , HasSnr , HasBer ? int ( Ber ) : - 1 , HasUnc ? int ( Unc ) : - 1 , q ) ;
2011-06-02 13:28:42 +02:00
# 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-12-04 12:45:26 +01:00
void cDvbTuner : : ExecuteDiseqc ( const cDiseqc * Diseqc , unsigned int * Frequency )
2011-09-11 14:09:03 +02:00
{
2012-01-08 09:27:59 +01:00
if ( ! lnbPowerTurnedOn ) {
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ; // must explicitly turn on LNB power
lnbPowerTurnedOn = true ;
}
2012-01-08 14:32:39 +01:00
static cMutex Mutex ;
if ( Diseqc - > IsScr ( ) )
2013-02-17 13:19:36 +01:00
Mutex . Lock ( ) ;
2011-09-11 14:09:03 +02:00
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 )
2011-12-04 12:45:26 +01:00
ResetToneAndVoltage ( ) ; // makes sure we don't block the bus!
2012-01-08 14:32:39 +01:00
if ( Diseqc - > IsScr ( ) )
2013-02-17 13:19:36 +01:00
Mutex . Unlock ( ) ;
2011-12-04 12:45:26 +01:00
}
void cDvbTuner : : ResetToneAndVoltage ( void )
{
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ;
CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_OFF ) ) ;
2011-09-11 14:09:03 +02:00
}
2012-01-08 09:27:59 +01:00
static int GetRequiredDeliverySystem ( const cChannel * Channel , const cDvbTransponderParameters * Dtp )
{
int ds = SYS_UNDEFINED ;
if ( Channel - > IsAtsc ( ) )
ds = SYS_ATSC ;
else if ( Channel - > IsCable ( ) )
ds = SYS_DVBC_ANNEX_AC ;
else if ( Channel - > IsSat ( ) )
ds = Dtp - > System ( ) = = DVB_SYSTEM_1 ? SYS_DVBS : SYS_DVBS2 ;
else if ( Channel - > IsTerr ( ) )
ds = Dtp - > System ( ) = = DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2 ;
else
esyslog ( " ERROR: can't determine frontend type for channel %d " , Channel - > Number ( ) ) ;
return ds ;
}
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 ( ) ) ;
2012-01-08 09:27:59 +01:00
// Determine the required frontend type:
2012-04-04 09:52:15 +02:00
frontendType = GetRequiredDeliverySystem ( & channel , & dtp ) ;
2012-01-08 09:27:59 +01:00
if ( frontendType = = SYS_UNDEFINED )
return false ;
SETCMD ( DTV_DELIVERY_SYSTEM , frontendType ) ;
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-12-04 12:45:26 +01:00
if ( const cDiseqc * diseqc = Diseqcs . Get ( device - > CardIndex ( ) + 1 , 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 ( ) ) {
2013-02-20 09:12:12 +01:00
if ( IsBondedMaster ( ) ) {
2011-12-04 12:45:26 +01:00
ExecuteDiseqc ( diseqc , & frequency ) ;
if ( frequency = = 0 )
return false ;
}
else
ResetToneAndVoltage ( ) ;
2011-09-11 14:09:03 +02:00
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 ;
}
2011-12-04 12:45:26 +01:00
int volt = ( dtp . Polarization ( ) = = ' V ' | | dtp . Polarization ( ) = = ' R ' ) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18 ;
2013-02-20 09:12:12 +01:00
if ( ! IsBondedMaster ( ) ) {
2011-12-04 12:45:26 +01:00
tone = SEC_TONE_OFF ;
volt = SEC_VOLTAGE_13 ;
}
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)
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 ( ) ) ;
2012-01-08 09:27:59 +01:00
if ( frontendType = = SYS_DVBS2 ) {
// DVB-S2
SETCMD ( DTV_PILOT , PILOT_AUTO ) ;
SETCMD ( DTV_ROLLOFF , dtp . RollOff ( ) ) ;
2013-03-07 09:51:06 +01:00
SETCMD ( DTV_STREAM_ID , dtp . StreamId ( ) ) ;
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_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 ;
}
2012-01-11 13:22:51 +01:00
else if ( frontendType = = SYS_DVBT | | frontendType = = SYS_DVBT2 ) {
// DVB-T/DVB-T2 (common parts)
2008-12-13 12:22:36 +01:00
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 ( ) ) ;
2012-01-11 13:22:51 +01:00
if ( frontendType = = SYS_DVBT2 ) {
// DVB-T2
2013-03-07 09:51:06 +01:00
SETCMD ( DTV_STREAM_ID , dtp . StreamId ( ) ) ;
2012-01-11 13:22:51 +01:00
}
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_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 ) ;
2012-01-16 12:43:33 +01:00
int WaitTime = 1000 ;
2005-08-21 09:25:51 +02:00
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 ;
}
2012-01-16 12:43:33 +01:00
WaitTime = 100 ; // allows for a quick change from tsTuned to tsLocked
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
}
2012-01-16 12:43:33 +01:00
newSet . TimedWait ( mutex , WaitTime ) ;
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 ( ) ;
2012-01-08 09:27:59 +01:00
const tDvbParameterMap * SystemValues = type = = ' S ' ? SystemValuesSat : SystemValuesTerr ;
2010-02-28 12:19:50 +01:00
# 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 ( ) ;
2012-01-11 13:22:51 +01:00
case 1 : ST ( " ST " ) 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 ( ) ;
2013-03-07 09:51:06 +01:00
case 12 : ST ( " ST " ) return new cMenuEditIntItem ( tr ( " StreamId " ) , & dtp . streamId , 0 , 255 ) ; 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 ;
2011-12-04 12:45:26 +01:00
cMutex cDvbDevice : : bondMutex ;
2003-10-04 12:42:58 +02:00
2012-01-08 09:27:59 +01:00
const char * DeliverySystemNames [ ] = {
2012-04-04 09:52:15 +02:00
" " ,
2008-12-13 12:22:36 +01:00
" 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 " ,
2012-01-08 09:27:59 +01:00
" DVB-T2 " ,
" TURBO " ,
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 ;
2012-01-08 09:27:59 +01:00
numDeliverySystems = 0 ;
numModulations = 0 ;
2011-12-04 12:45:26 +01:00
bondedDevice = NULL ;
needsDetachBondedReceivers = false ;
2012-02-12 15:45:59 +01:00
tsBuffer = NULL ;
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 ) {
2012-01-08 09:27:59 +01:00
if ( QueryDeliverySystems ( fd_frontend ) )
dvbTuner = new cDvbTuner ( this , fd_frontend , adapter , frontend ) ;
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 ;
2011-12-04 12:45:26 +01:00
UnBond ( ) ;
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
{
2012-02-17 12:38:57 +01:00
return cString : : sprintf ( " %s/%s%d/%s%d " , DEV_DVB_BASE , 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 ;
}
2012-04-04 09:52:15 +02:00
cString cDvbDevice : : DeviceType ( void ) const
{
if ( dvbTuner ) {
if ( dvbTuner - > FrontendType ( ) ! = SYS_UNDEFINED )
return DeliverySystemNames [ dvbTuner - > FrontendType ( ) ] ;
if ( numDeliverySystems )
return DeliverySystemNames [ deliverySystems [ 0 ] ] ; // to have some reasonable default
}
return " " ;
}
2012-03-13 10:20:42 +01:00
cString cDvbDevice : : DeviceName ( void ) const
{
return frontendInfo . name ;
}
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 " ) ;
2012-02-17 12:38:57 +01:00
cStringList Nodes ;
cReadDir DvbDir ( DEV_DVB_BASE ) ;
if ( DvbDir . Ok ( ) ) {
struct dirent * a ;
while ( ( a = DvbDir . Next ( ) ) ! = NULL ) {
if ( strstr ( a - > d_name , DEV_DVB_ADAPTER ) = = a - > d_name ) {
int Adapter = strtol ( a - > d_name + strlen ( DEV_DVB_ADAPTER ) , NULL , 10 ) ;
cReadDir AdapterDir ( AddDirectory ( DEV_DVB_BASE , a - > d_name ) ) ;
if ( AdapterDir . Ok ( ) ) {
struct dirent * f ;
while ( ( f = AdapterDir . Next ( ) ) ! = NULL ) {
if ( strstr ( f - > d_name , DEV_DVB_FRONTEND ) = = f - > d_name ) {
int Frontend = strtol ( f - > d_name + strlen ( DEV_DVB_FRONTEND ) , NULL , 10 ) ;
Nodes . Append ( strdup ( cString : : sprintf ( " %2d %2d " , Adapter , Frontend ) ) ) ;
}
}
}
}
}
}
2010-01-04 14:16:11 +01:00
int Checked = 0 ;
int Found = 0 ;
2012-02-17 12:38:57 +01:00
if ( Nodes . Size ( ) > 0 ) {
Nodes . Sort ( ) ;
for ( int i = 0 ; i < Nodes . Size ( ) ; i + + ) {
int Adapter ;
int Frontend ;
if ( 2 = = sscanf ( Nodes [ i ] , " %d %d " , & Adapter , & Frontend ) ) {
if ( Exists ( Adapter , Frontend ) ) {
if ( Checked + + < MAXDVBDEVICES ) {
if ( UseDevice ( NextCardIndex ( ) ) ) {
if ( Probe ( Adapter , Frontend ) )
Found + + ;
}
else
NextCardIndex ( 1 ) ; // skips this one
}
}
}
}
}
2010-01-04 14:16:11 +01:00
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
}
2012-01-08 09:27:59 +01:00
bool cDvbDevice : : QueryDeliverySystems ( int fd_frontend )
{
numDeliverySystems = 0 ;
if ( ioctl ( fd_frontend , FE_GET_INFO , & frontendInfo ) < 0 ) {
LOG_ERROR ;
return false ;
}
2012-01-16 12:53:38 +01:00
# if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) >= 0x0505
2012-01-08 09:27:59 +01:00
dtv_property Frontend [ 1 ] ;
memset ( & Frontend , 0 , sizeof ( Frontend ) ) ;
dtv_properties CmdSeq ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Frontend ;
SETCMD ( DTV_ENUM_DELSYS , 0 ) ;
int Result = ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ;
if ( Result = = 0 ) {
for ( uint i = 0 ; i < Frontend [ 0 ] . u . buffer . len ; i + + ) {
if ( numDeliverySystems > = MAXDELIVERYSYSTEMS ) {
esyslog ( " ERROR: too many delivery systems on frontend %d/%d " , adapter , frontend ) ;
break ;
}
deliverySystems [ numDeliverySystems + + ] = Frontend [ 0 ] . u . buffer . data [ i ] ;
}
}
else {
esyslog ( " ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode " , adapter , frontend ) ;
# else
{
# endif
// Legacy mode (DVB-API < 5.5):
switch ( frontendInfo . type ) {
case FE_QPSK : deliverySystems [ numDeliverySystems + + ] = SYS_DVBS ;
if ( frontendInfo . caps & FE_CAN_2G_MODULATION )
deliverySystems [ numDeliverySystems + + ] = SYS_DVBS2 ;
break ;
2012-01-11 13:22:51 +01:00
case FE_OFDM : deliverySystems [ numDeliverySystems + + ] = SYS_DVBT ;
if ( frontendInfo . caps & FE_CAN_2G_MODULATION )
deliverySystems [ numDeliverySystems + + ] = SYS_DVBT2 ;
break ;
2012-01-08 09:27:59 +01:00
case FE_QAM : deliverySystems [ numDeliverySystems + + ] = SYS_DVBC_ANNEX_AC ; break ;
case FE_ATSC : deliverySystems [ numDeliverySystems + + ] = SYS_ATSC ; break ;
default : esyslog ( " ERROR: unknown frontend type %d on frontend %d/%d " , frontendInfo . type , adapter , frontend ) ;
}
}
if ( numDeliverySystems > 0 ) {
cString ds ( " " ) ;
for ( int i = 0 ; i < numDeliverySystems ; i + + )
ds = cString : : sprintf ( " %s%s%s " , * ds , i ? " , " : " " , DeliverySystemNames [ deliverySystems [ i ] ] ) ;
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_QAM_16 ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( QAM_16 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_QAM_32 ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( QAM_32 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_QAM_64 ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( QAM_64 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_QAM_128 ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( QAM_128 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_QAM_256 ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( QAM_256 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_8VSB ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( VSB_8 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_16VSB ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , MapToUserString ( VSB_16 , ModulationValues ) ) ; }
if ( frontendInfo . caps & FE_CAN_TURBO_FEC ) { numModulations + + ; ms = cString : : sprintf ( " %s%s%s " , * ms , * * ms ? " , " : " " , " TURBO_FEC " ) ; }
if ( ! * * ms )
ms = " unknown modulations " ;
isyslog ( " frontend %d/%d provides %s with %s ( \" %s \" ) " , adapter , frontend , * ds , * ms , frontendInfo . name ) ;
return true ;
}
else
esyslog ( " ERROR: frontend %d/%d doesn't provide any delivery systems " , adapter , frontend ) ;
return false ;
}
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 ;
}
2011-12-04 12:45:26 +01:00
bool cDvbDevice : : BondDevices ( const char * Bondings )
{
UnBondDevices ( ) ;
if ( Bondings ) {
cSatCableNumbers SatCableNumbers ( MAXDEVICES , Bondings ) ;
for ( int i = 0 ; i < cDevice : : NumDevices ( ) ; i + + ) {
int d = SatCableNumbers . FirstDeviceIndex ( i ) ;
if ( d > = 0 ) {
int ErrorDevice = 0 ;
if ( cDevice * Device1 = cDevice : : GetDevice ( i ) ) {
if ( cDevice * Device2 = cDevice : : GetDevice ( d ) ) {
if ( cDvbDevice * DvbDevice1 = dynamic_cast < cDvbDevice * > ( Device1 ) ) {
if ( cDvbDevice * DvbDevice2 = dynamic_cast < cDvbDevice * > ( Device2 ) ) {
2011-12-10 13:49:04 +01:00
if ( ! DvbDevice1 - > Bond ( DvbDevice2 ) )
2011-12-04 12:45:26 +01:00
return false ; // Bond() has already logged the error
}
else
ErrorDevice = d + 1 ;
}
else
ErrorDevice = i + 1 ;
if ( ErrorDevice ) {
esyslog ( " ERROR: device '%d' in device bondings '%s' is not a cDvbDevice " , ErrorDevice , Bondings ) ;
return false ;
}
}
else
ErrorDevice = d + 1 ;
}
else
ErrorDevice = i + 1 ;
if ( ErrorDevice ) {
esyslog ( " ERROR: unknown device '%d' in device bondings '%s' " , ErrorDevice , Bondings ) ;
return false ;
}
}
}
}
return true ;
}
void cDvbDevice : : UnBondDevices ( void )
{
for ( int i = 0 ; i < cDevice : : NumDevices ( ) ; i + + ) {
if ( cDvbDevice * d = dynamic_cast < cDvbDevice * > ( cDevice : : GetDevice ( i ) ) )
d - > UnBond ( ) ;
}
}
bool cDvbDevice : : Bond ( cDvbDevice * Device )
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( ! bondedDevice ) {
if ( Device ! = this ) {
2012-01-08 09:27:59 +01:00
if ( ( ProvidesDeliverySystem ( SYS_DVBS ) | | ProvidesDeliverySystem ( SYS_DVBS2 ) ) & & ( Device - > ProvidesDeliverySystem ( SYS_DVBS ) | | Device - > ProvidesDeliverySystem ( SYS_DVBS2 ) ) ) {
2011-12-04 12:45:26 +01:00
if ( dvbTuner & & Device - > dvbTuner & & dvbTuner - > Bond ( Device - > dvbTuner ) ) {
bondedDevice = Device - > bondedDevice ? Device - > bondedDevice : Device ;
Device - > bondedDevice = this ;
dsyslog ( " device %d bonded with device %d " , CardIndex ( ) + 1 , bondedDevice - > CardIndex ( ) + 1 ) ;
return true ;
}
}
else
esyslog ( " ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded) " , CardIndex ( ) + 1 , Device - > CardIndex ( ) + 1 ) ;
}
else
esyslog ( " ERROR: can't bond device %d with itself " , CardIndex ( ) + 1 ) ;
}
else
esyslog ( " ERROR: device %d already bonded with device %d, can't bond with device %d " , CardIndex ( ) + 1 , bondedDevice - > CardIndex ( ) + 1 , Device - > CardIndex ( ) + 1 ) ;
return false ;
}
void cDvbDevice : : UnBond ( void )
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( cDvbDevice * d = bondedDevice ) {
if ( dvbTuner )
dvbTuner - > UnBond ( ) ;
dsyslog ( " device %d unbonded from device %d " , CardIndex ( ) + 1 , bondedDevice - > CardIndex ( ) + 1 ) ;
while ( d - > bondedDevice ! = this )
d = d - > bondedDevice ;
if ( d = = bondedDevice )
d - > bondedDevice = NULL ;
else
d - > bondedDevice = bondedDevice ;
bondedDevice = NULL ;
}
}
bool cDvbDevice : : BondingOk ( const cChannel * Channel , bool ConsiderOccupied ) const
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( bondedDevice )
return dvbTuner & & dvbTuner - > BondingOk ( Channel , ConsiderOccupied ) ;
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 ) ;
}
2012-01-08 09:27:59 +01:00
bool cDvbDevice : : ProvidesDeliverySystem ( int DeliverySystem ) const
{
for ( int i = 0 ; i < numDeliverySystems ; i + + ) {
if ( deliverySystems [ i ] = = DeliverySystem )
return true ;
}
return false ;
}
2002-10-06 10:25:42 +02:00
bool cDvbDevice : : ProvidesSource ( int Source ) const
{
int type = Source & cSource : : st_Mask ;
return type = = cSource : : stNone
2012-01-08 09:27:59 +01:00
| | type = = cSource : : stAtsc & & ProvidesDeliverySystem ( SYS_ATSC )
| | type = = cSource : : stCable & & ( ProvidesDeliverySystem ( SYS_DVBC_ANNEX_AC ) | | ProvidesDeliverySystem ( SYS_DVBC_ANNEX_B ) )
| | type = = cSource : : stSat & & ( ProvidesDeliverySystem ( SYS_DVBS ) | | ProvidesDeliverySystem ( SYS_DVBS2 ) )
2012-01-11 13:22:51 +01:00
| | type = = cSource : : stTerr & & ( ProvidesDeliverySystem ( SYS_DVBT ) | | ProvidesDeliverySystem ( SYS_DVBT2 ) ) ;
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 ( ) ) ;
2012-01-08 09:27:59 +01:00
if ( ! ProvidesDeliverySystem ( GetRequiredDeliverySystem ( Channel , & dtp ) ) | |
2013-03-07 09:51:06 +01:00
dtp . StreamId ( ) ! = 0 & & ! ( frontendInfo . caps & FE_CAN_MULTISTREAM ) | |
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-12-04 12:45:26 +01: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 ;
2012-03-02 10:19:00 +01:00
bool hasPriority = Priority = = IDLEPRIORITY | | Priority > this - > Priority ( ) ;
2003-08-24 14:49:53 +02:00
bool needsDetachReceivers = false ;
2011-12-04 12:45:26 +01:00
needsDetachBondedReceivers = 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 ;
2012-03-25 10:44:04 +02:00
if ( Priority > IDLEPRIORITY ) {
2012-03-02 10:19:00 +01:00
if ( Receiving ( ) ) {
2011-12-04 12:45:26 +01:00
if ( dvbTuner - > IsTunedTo ( Channel ) ) {
2012-03-08 10:04:16 +01:00
if ( Channel - > Vpid ( ) & & ! HasPid ( Channel - > Vpid ( ) ) | | Channel - > Apid ( 0 ) & & ! HasPid ( Channel - > Apid ( 0 ) ) | | Channel - > Dpid ( 0 ) & & ! HasPid ( Channel - > Dpid ( 0 ) ) ) {
2011-12-04 12:45:26 +01:00
if ( CamSlot ( ) & & Channel - > Ca ( ) > = CA_ENCRYPTED_MIN ) {
if ( CamSlot ( ) - > CanDecrypt ( Channel ) )
result = true ;
else
needsDetachReceivers = true ;
}
2007-01-07 14:46:14 +01:00
else
2012-02-25 14:44:43 +01:00
result = true ;
2007-01-07 14:46:14 +01:00
}
2002-11-03 12:31:51 +01:00
else
2012-02-25 14:44:43 +01:00
result = true ;
2002-09-04 17:26:02 +02:00
}
else
2012-03-02 10:19:00 +01:00
needsDetachReceivers = Receiving ( ) ;
2011-12-04 12:45:26 +01:00
}
if ( result ) {
if ( ! BondingOk ( Channel ) ) {
// This device is bonded, so we need to check the priorities of the others:
for ( cDvbDevice * d = bondedDevice ; d & & d ! = this ; d = d - > bondedDevice ) {
if ( d - > Priority ( ) > = Priority ) {
result = false ;
break ;
}
}
needsDetachBondedReceivers = true ;
2012-03-02 10:19:00 +01:00
needsDetachReceivers = Receiving ( ) ;
2011-12-04 12:45:26 +01:00
}
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
{
2012-01-08 09:27:59 +01:00
return numDeliverySystems + numModulations ;
2008-04-12 13:39:12 +02:00
}
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
}
2011-12-04 12:45:26 +01:00
bool cDvbDevice : : IsTunedToTransponder ( const cChannel * Channel ) const
2006-04-09 09:12:47 +02:00
{
2011-06-02 13:28:42 +02:00
return dvbTuner ? dvbTuner - > IsTunedTo ( Channel ) : false ;
2006-04-09 09:12:47 +02:00
}
2011-12-04 12:45:26 +01:00
bool cDvbDevice : : MaySwitchTransponder ( const cChannel * Channel ) const
{
return BondingOk ( Channel , true ) & & cDevice : : MaySwitchTransponder ( Channel ) ;
}
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 )
2011-12-04 12:45:26 +01:00
dvbTuner - > SetChannel ( Channel ) ;
2002-08-04 14:57:29 +02:00
return true ;
}
2013-02-16 13:06:16 +01:00
bool cDvbDevice : : HasLock ( int TimeoutMs ) const
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 )
2012-09-22 11:55:26 +02:00
tsBuffer = new cTSBuffer ( fd_dvr , MEGABYTE ( 5 ) , 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
2011-12-04 12:45:26 +01:00
void cDvbDevice : : DetachAllReceivers ( void )
{
cMutexLock MutexLock ( & bondMutex ) ;
cDvbDevice * d = this ;
do {
d - > cDevice : : DetachAllReceivers ( ) ;
d = d - > bondedDevice ;
} while ( d & & d ! = this & & needsDetachBondedReceivers ) ;
needsDetachBondedReceivers = false ;
}
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 )
{
uint32_t SubsystemId = 0 ;
2012-02-20 12:54:01 +01:00
cString FileName = cString : : sprintf ( " /dev/dvb/adapter%d/frontend%d " , Adapter , Frontend ) ;
struct stat st ;
if ( stat ( FileName , & st ) = = 0 ) {
cReadDir d ( " /sys/class/dvb " ) ;
if ( d . Ok ( ) ) {
struct dirent * e ;
while ( ( e = d . Next ( ) ) ! = NULL ) {
if ( strstr ( e - > d_name , " frontend " ) ) {
FileName = cString : : sprintf ( " /sys/class/dvb/%s/dev " , e - > d_name ) ;
if ( FILE * f = fopen ( FileName , " r " ) ) {
cReadLine ReadLine ;
char * s = ReadLine . Read ( f ) ;
fclose ( f ) ;
unsigned Major ;
unsigned Minor ;
if ( s & & 2 = = sscanf ( s , " %u:%u " , & Major , & Minor ) ) {
if ( ( ( Major < < 8 ) | Minor ) = = st . st_rdev ) {
FileName = cString : : sprintf ( " /sys/class/dvb/%s/device/subsystem_vendor " , e - > d_name ) ;
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/%s/device/subsystem_device " , e - > d_name ) ;
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId | = strtoul ( s , NULL , 0 ) ;
fclose ( f ) ;
}
break ;
}
}
}
}
}
}
2011-06-02 13:28:42 +02:00
}
return SubsystemId ;
}