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 .
*
2020-06-10 14:52:43 +02:00
* $ Id : dvbdevice . c 4.23 2020 / 06 / 10 14 : 52 : 43 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
2013-04-09 09:53:00 +02:00
static int DvbApiVersion = 0x0000 ; // the version of the DVB driver actually in use (will be determined by the first device created)
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 ----------------------------------------------------
2014-03-16 10:48:30 +01:00
const tDvbParameterMap PilotValues [ ] = {
{ 0 , PILOT_OFF , trNOOP ( " off " ) } ,
{ 1 , PILOT_ON , trNOOP ( " on " ) } ,
{ 999 , PILOT_AUTO , trNOOP ( " auto " ) } ,
{ - 1 , 0 , NULL }
} ;
2010-02-28 12:19:50 +01:00
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 )
{
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 ) ) ;
2014-03-16 10:48:30 +01:00
ST ( " S 2 " ) q + = PrintParameter ( q , ' N ' , MapToUser ( pilot , PilotValues ) ) ;
2012-01-13 12:27:18 +01:00
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 ) ;
2014-03-16 10:48:30 +01:00
ST ( " T2 " ) q + = PrintParameter ( q , ' Q ' , t2systemId ) ;
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 ) ) ;
2014-03-16 10:48:30 +01:00
ST ( " T2 " ) q + = PrintParameter ( q , ' X ' , sisoMiso ) ;
2012-01-13 12:27:18 +01:00
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 )
{
2016-11-07 14:33:01 +01:00
polarization = 0 ;
inversion = INVERSION_AUTO ;
bandwidth = 8000000 ;
coderateH = FEC_AUTO ;
coderateL = FEC_AUTO ;
modulation = QPSK ;
system = DVB_SYSTEM_1 ;
transmission = TRANSMISSION_MODE_AUTO ;
guard = GUARD_INTERVAL_AUTO ;
hierarchy = HIERARCHY_AUTO ;
rollOff = ROLLOFF_AUTO ;
streamId = 0 ;
t2systemId = 0 ;
sisoMiso = 0 ;
pilot = PILOT_AUTO ;
2010-02-28 12:19:50 +01:00
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 ;
2014-03-16 10:48:30 +01:00
case ' N ' : s = ParseParameter ( s , pilot , PilotValues ) ; break ;
2010-02-28 12:19:50 +01:00
case ' O ' : s = ParseParameter ( s , rollOff , RollOffValues ) ; break ;
2013-03-07 09:51:06 +01:00
case ' P ' : s = ParseParameter ( s , streamId ) ; break ;
2014-03-16 10:48:30 +01:00
case ' Q ' : s = ParseParameter ( s , t2systemId ) ; 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 ;
2014-03-16 10:48:30 +01:00
case ' X ' : s = ParseParameter ( s , sisoMiso ) ; 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 ;
}
2018-10-29 12:16:35 +01:00
// --- cDvbFrontend ----------------------------------------------------------
2018-10-29 12:22:11 +01:00
const char * DeliverySystemNames [ ] = {
" ??? " ,
" DVB-C " ,
" DVB-C " ,
" DVB-T " ,
" DSS " ,
" DVB-S " ,
" DVB-S2 " ,
" DVB-H " ,
" ISDBT " ,
" ISDBS " ,
" ISDBC " ,
" ATSC " ,
" ATSCMH " ,
" DTMB " ,
" CMMB " ,
" DAB " ,
" DVB-T2 " ,
" TURBO " ,
" DVB-C " ,
" DVB-C2 " ,
NULL
} ;
static const int DeliverySystemNamesMax = sizeof ( DeliverySystemNames ) / sizeof ( DeliverySystemNames [ 0 ] ) - 2 ; // -1 to get the maximum allowed index & -1 for the NULL => -2
static const char * GetDeliverySystemName ( int Index )
{
if ( Index > DeliverySystemNamesMax )
Index = 0 ;
return DeliverySystemNames [ Index ] ;
} ;
# define MAXFRONTENDCMDS 16
# define SETCMD(c, d) { Props[CmdSeq.num].cmd = (c);\
Props [ CmdSeq . num ] . u . data = ( d ) ; \
if ( CmdSeq . num + + > MAXFRONTENDCMDS ) { \
esyslog ( " ERROR: too many tuning commands on frontend %d/%d " , adapter , frontend ) ; \
return false ; \
} \
}
2018-10-29 12:16:35 +01:00
class cDvbFrontend {
private :
int adapter , frontend ;
int fd_frontend ;
uint32_t subsystemId ;
dvb_frontend_info frontendInfo ;
cVector < int > deliverySystems ;
int numModulations ;
bool QueryDeliverySystems ( void ) ;
public :
cDvbFrontend ( int Adapter , int Frontend ) ;
~ cDvbFrontend ( ) ;
int Open ( void ) ;
void Close ( void ) ;
const char * FrontendName ( void ) { return frontendInfo . name ; }
bool ProvidesDeliverySystem ( int DeliverySystem ) const ;
bool ProvidesModulation ( int System , int StreamId , int Modulation ) const ;
int NumDeliverySystems ( void ) const { return deliverySystems . Size ( ) ; }
int NumModulations ( void ) const { return numModulations ; }
uint32_t SubsystemId ( void ) const { return subsystemId ; }
} ;
cDvbFrontend : : cDvbFrontend ( int Adapter , int Frontend )
{
adapter = Adapter ;
frontend = Frontend ;
fd_frontend = - 1 ;
subsystemId = cDvbDeviceProbe : : GetSubsystemId ( adapter , frontend ) ;
numModulations = 0 ;
Open ( ) ;
QueryDeliverySystems ( ) ;
Close ( ) ;
}
cDvbFrontend : : ~ cDvbFrontend ( )
{
Close ( ) ;
}
int cDvbFrontend : : Open ( void )
{
Close ( ) ;
fd_frontend = DvbOpen ( DEV_DVB_FRONTEND , adapter , frontend , O_RDWR | O_NONBLOCK , true ) ;
return fd_frontend ;
}
void cDvbFrontend : : Close ( void )
{
if ( fd_frontend > = 0 ) {
if ( close ( fd_frontend ) ! = 0 )
esyslog ( " ERROR: frontend %d/%d " , adapter , frontend ) ;
fd_frontend = - 1 ;
}
}
bool cDvbFrontend : : ProvidesDeliverySystem ( int DeliverySystem ) const
{
for ( int i = 0 ; i < deliverySystems . Size ( ) ; i + + ) {
if ( deliverySystems [ i ] = = DeliverySystem )
return true ;
}
return false ;
}
bool cDvbFrontend : : ProvidesModulation ( int System , int StreamId , int Modulation ) const
{
if ( StreamId ! = 0 & & ! ( frontendInfo . caps & FE_CAN_MULTISTREAM ) )
return false ;
if ( Modulation = = QPSK & & ! ( frontendInfo . caps & FE_CAN_QPSK ) | |
Modulation = = QAM_16 & & ! ( frontendInfo . caps & FE_CAN_QAM_16 ) | |
Modulation = = QAM_32 & & ! ( frontendInfo . caps & FE_CAN_QAM_32 ) | |
Modulation = = QAM_64 & & ! ( frontendInfo . caps & FE_CAN_QAM_64 ) | |
Modulation = = QAM_128 & & ! ( frontendInfo . caps & FE_CAN_QAM_128 ) | |
Modulation = = QAM_256 & & ! ( frontendInfo . caps & FE_CAN_QAM_256 ) | |
Modulation = = QAM_AUTO & & ! ( frontendInfo . caps & FE_CAN_QAM_AUTO ) | |
Modulation = = VSB_8 & & ! ( frontendInfo . caps & FE_CAN_8VSB ) | |
Modulation = = VSB_16 & & ! ( frontendInfo . caps & FE_CAN_16VSB ) | |
Modulation = = PSK_8 & & ! ( frontendInfo . caps & FE_CAN_TURBO_FEC ) & & System = = SYS_DVBS ) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to de
return false ;
return true ;
}
2018-10-29 12:22:11 +01:00
bool cDvbFrontend : : QueryDeliverySystems ( void )
{
deliverySystems . Clear ( ) ;
numModulations = 0 ;
if ( ioctl ( fd_frontend , FE_GET_INFO , & frontendInfo ) < 0 ) {
LOG_ERROR ;
return false ;
}
dtv_property Props [ 1 ] ;
dtv_properties CmdSeq ;
// Determine the version of the running DVB API:
if ( ! DvbApiVersion ) {
memset ( & Props , 0 , sizeof ( Props ) ) ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Props ;
SETCMD ( DTV_API_VERSION , 0 ) ;
if ( ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ! = 0 ) {
LOG_ERROR ;
return false ;
}
DvbApiVersion = Props [ 0 ] . u . data ;
isyslog ( " DVB API version is 0x%04X (VDR was built with 0x%04X) " , DvbApiVersion , DVBAPIVERSION ) ;
}
// Determine the types of delivery systems this device provides:
bool LegacyMode = true ;
if ( DvbApiVersion > = 0x0505 ) {
memset ( & Props , 0 , sizeof ( Props ) ) ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Props ;
SETCMD ( DTV_ENUM_DELSYS , 0 ) ;
int Result = ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ;
if ( Result = = 0 ) {
for ( uint i = 0 ; i < Props [ 0 ] . u . buffer . len ; i + + ) {
// activate this line to simulate a multi-frontend device if you only have a single-frontend device with DVB-S and DVB-S2:
//if (frontend == 0 && Props[0].u.buffer.data[i] != SYS_DVBS || frontend == 1 && Props[0].u.buffer.data[i] != SYS_DVBS2)
deliverySystems . Append ( Props [ 0 ] . u . buffer . data [ i ] ) ;
}
LegacyMode = false ;
}
else {
esyslog ( " ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode " , adapter , frontend ) ;
}
}
if ( LegacyMode ) {
// Legacy mode (DVB-API < 5.5):
switch ( frontendInfo . type ) {
case FE_QPSK : deliverySystems . Append ( SYS_DVBS ) ;
if ( frontendInfo . caps & FE_CAN_2G_MODULATION )
deliverySystems . Append ( SYS_DVBS2 ) ;
break ;
case FE_OFDM : deliverySystems . Append ( SYS_DVBT ) ;
if ( frontendInfo . caps & FE_CAN_2G_MODULATION )
deliverySystems . Append ( SYS_DVBT2 ) ;
break ;
case FE_QAM : deliverySystems . Append ( SYS_DVBC_ANNEX_AC ) ; break ;
case FE_ATSC : deliverySystems . Append ( SYS_ATSC ) ; break ;
default : esyslog ( " ERROR: unknown frontend type %d on frontend %d/%d " , frontendInfo . type , adapter , frontend ) ;
}
}
if ( deliverySystems . Size ( ) > 0 ) {
cString ds ( " " ) ;
for ( int i = 0 ; i < deliverySystems . Size ( ) ; i + + )
ds = cString : : sprintf ( " %s%s%s " , * ds , i ? " , " : " " , GetDeliverySystemName ( 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 ;
}
2002-12-08 09:55:26 +01:00
// --- cDvbTuner -------------------------------------------------------------
2011-06-02 13:28:42 +02:00
# define TUNER_POLL_TIMEOUT 10 // ms
2018-10-29 12:22:11 +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 (%s) " , Channel - > Number ( ) , Channel - > Name ( ) ) ;
return ds ;
}
2002-12-08 09:55:26 +01:00
class cDvbTuner : public cThread {
private :
2011-12-04 12:45:26 +01:00
static cMutex bondMutex ;
2013-08-21 11:02:52 +02:00
enum eTunerStatus { tsIdle , tsSet , tsPositioning , tsTuned , tsLocked } ;
2012-04-04 09:52:15 +02:00
int frontendType ;
2011-12-04 12:45:26 +01:00
const cDvbDevice * device ;
2018-10-29 12:16:35 +01:00
mutable int fd_frontend ;
int adapter ;
mutable int frontend ;
cVector < cDvbFrontend * > dvbFrontends ;
mutable cDvbFrontend * dvbFrontend ;
int numDeliverySystems ;
int numModulations ;
2006-01-01 12:22:18 +01:00
int tuneTimeout ;
int lockTimeout ;
2006-01-03 10:42:47 +01:00
time_t lastTimeoutReport ;
2017-04-20 14:43:54 +02:00
mutable uint32_t lastUncValue ;
mutable uint32_t lastUncDelta ;
mutable time_t lastUncChange ;
2002-12-08 09:55:26 +01:00
cChannel channel ;
2011-09-11 14:09:03 +02:00
const cDiseqc * lastDiseqc ;
2013-08-21 11:02:52 +02:00
int diseqcOffset ;
int lastSource ;
cPositioner * positioner ;
2011-09-11 14:09:03 +02:00
const cScr * scr ;
2012-01-08 09:27:59 +01:00
bool lnbPowerTurnedOn ;
2002-12-08 09:55:26 +01:00
eTunerStatus tunerStatus ;
2018-10-29 12:16:35 +01:00
mutable cMutex mutex ;
2004-10-23 10:04:01 +02:00
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 ;
2013-08-21 11:02:52 +02:00
cPositioner * GetPositioner ( void ) ;
2017-01-09 15:21:13 +01:00
void ExecuteDiseqc ( const cDiseqc * Diseqc , int * Frequency ) ;
2011-12-04 12:45:26 +01:00
void ResetToneAndVoltage ( void ) ;
2002-12-08 09:55:26 +01:00
bool SetFrontend ( void ) ;
virtual void Action ( void ) ;
public :
2018-10-29 12:16:35 +01:00
cDvbTuner ( const cDvbDevice * Device , int Adapter , int Frontend ) ;
2002-12-08 09:55:26 +01:00
virtual ~ cDvbTuner ( ) ;
2018-10-29 12:16:35 +01:00
bool ProvidesDeliverySystem ( int DeliverySystem ) const ;
bool ProvidesModulation ( int System , int StreamId , int Modulation ) const ;
bool ProvidesFrontend ( const cChannel * Channel , bool Activate = false ) const ;
2019-03-10 12:10:01 +01:00
int Frontend ( void ) const { return frontend ; }
2012-04-04 09:52:15 +02:00
int FrontendType ( void ) const { return frontendType ; }
2018-10-29 12:16:35 +01:00
const char * FrontendName ( void ) { return dvbFrontend - > FrontendName ( ) ; }
int NumProvidedSystems ( void ) const { return numDeliverySystems + numModulations ; }
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 ; }
2018-10-29 12:16:35 +01:00
uint32_t SubsystemId ( void ) const { return dvbFrontend - > 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 ) ;
2013-08-21 11:02:52 +02:00
const cPositioner * Positioner ( void ) const { return positioner ; }
2017-05-09 11:53:41 +02:00
bool GetSignalStats ( int & Valid , double * Strength = NULL , double * Cnr = NULL , double * BerPre = NULL , double * BerPost = NULL , double * Per = NULL , int * Status = NULL ) const ;
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 ;
2018-10-29 12:16:35 +01:00
cDvbTuner : : cDvbTuner ( const cDvbDevice * Device , 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 ;
2018-10-29 12:16:35 +01:00
fd_frontend = - 1 ;
2010-01-04 14:16:11 +01:00
adapter = Adapter ;
frontend = Frontend ;
2018-10-29 12:16:35 +01:00
dvbFrontend = NULL ;
2006-01-03 10:42:47 +01:00
tuneTimeout = 0 ;
lockTimeout = 0 ;
lastTimeoutReport = 0 ;
2017-04-20 14:43:54 +02:00
lastUncValue = 0 ;
lastUncDelta = 0 ;
lastUncChange = 0 ;
2011-09-11 14:09:03 +02:00
lastDiseqc = NULL ;
2013-08-21 11:02:52 +02:00
diseqcOffset = 0 ;
lastSource = 0 ;
positioner = NULL ;
2011-09-11 14:09:03 +02:00
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 ;
2018-10-29 12:16:35 +01:00
cDvbFrontend * fe = new cDvbFrontend ( adapter , frontend ) ;
dvbFrontends . Append ( fe ) ;
numDeliverySystems = fe - > NumDeliverySystems ( ) ;
numModulations = fe - > NumModulations ( ) ;
cString FrontendNumbers = cString : : sprintf ( " %d " , frontend ) ;
// Check for multiple frontends:
if ( frontend = = 0 ) {
for ( int i = 1 ; ; i + + ) {
if ( access ( DvbName ( DEV_DVB_FRONTEND , adapter , i ) , F_OK ) = = 0 ) {
if ( access ( DvbName ( DEV_DVB_DEMUX , adapter , i ) , F_OK ) ! = 0 ) {
fe = new cDvbFrontend ( adapter , i ) ;
dvbFrontends . Append ( fe ) ;
numDeliverySystems + = fe - > NumDeliverySystems ( ) ;
//numModulations += fe->NumModulations(); // looks like in multi frontend devices all frontends report the same modulations
FrontendNumbers = cString : : sprintf ( " %s+%d " , * FrontendNumbers , i ) ;
}
}
else
break ;
}
}
// Open default frontend:
dvbFrontend = dvbFrontends [ 0 ] ;
fd_frontend = dvbFrontend - > Open ( ) ;
SetDescription ( " frontend %d/%s tuner " , adapter , * FrontendNumbers ) ;
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
*/
2018-10-29 12:16:35 +01:00
for ( int i = 0 ; i < dvbFrontends . Size ( ) ; i + + )
delete dvbFrontends [ i ] ;
}
bool cDvbTuner : : ProvidesDeliverySystem ( int DeliverySystem ) const
{
for ( int i = 0 ; i < dvbFrontends . Size ( ) ; i + + ) {
if ( dvbFrontends [ i ] - > ProvidesDeliverySystem ( DeliverySystem ) )
return true ;
}
return false ;
}
bool cDvbTuner : : ProvidesModulation ( int System , int StreamId , int Modulation ) const
{
for ( int i = 0 ; i < dvbFrontends . Size ( ) ; i + + ) {
if ( dvbFrontends [ i ] - > ProvidesModulation ( System , StreamId , Modulation ) )
return true ;
}
return false ;
}
bool cDvbTuner : : ProvidesFrontend ( const cChannel * Channel , bool Activate ) const
{
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
int DeliverySystem = GetRequiredDeliverySystem ( Channel , & dtp ) ;
for ( int i = 0 ; i < dvbFrontends . Size ( ) ; i + + ) {
if ( dvbFrontends [ i ] - > ProvidesDeliverySystem ( DeliverySystem ) & & dvbFrontends [ i ] - > ProvidesModulation ( dtp . System ( ) , dtp . StreamId ( ) , dtp . Modulation ( ) ) ) {
if ( Activate & & dvbFrontend ! = dvbFrontends [ i ] ) {
cMutexLock MutexLock ( & mutex ) ;
dvbFrontend - > Close ( ) ;
dvbFrontend = dvbFrontends [ i ] ;
fd_frontend = dvbFrontend - > Open ( ) ;
frontend = i ;
dsyslog ( " using frontend %d/%d " , adapter , frontend ) ;
lastUncValue = 0 ;
lastUncDelta = 0 ;
lastUncChange = 0 ;
}
return true ;
}
}
return false ;
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 ) {
2019-05-28 15:54:22 +02:00
if ( const cDiseqc * diseqc = Diseqcs . Get ( device - > DeviceNumber ( ) + 1 , Channel - > Source ( ) , Channel - > Frequency ( ) , dtp . Polarization ( ) , NULL ) )
2011-12-04 12:45:26 +01:00
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 ( ) ) {
2013-03-12 10:18:55 +01:00
if ( strcmp ( BondingParams , t - > GetBondedMaster ( ) - > GetBondingParams ( ) ) ! = 0 )
2011-12-04 12:45:26 +01:00
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 ;
2013-08-21 11:02:52 +02:00
diseqcOffset = 0 ;
2011-12-04 12:45:26 +01:00
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 ;
}
2017-04-17 15:02:44 +02:00
//#define DEBUG_SIGNALSTATS
2011-06-02 13:28:42 +02:00
//#define DEBUG_SIGNALSTRENGTH
//#define DEBUG_SIGNALQUALITY
2017-05-09 11:53:41 +02:00
bool cDvbTuner : : GetSignalStats ( int & Valid , double * Strength , double * Cnr , double * BerPre , double * BerPost , double * Per , int * Status ) const
2017-04-17 15:02:44 +02:00
{
ClearEventQueue ( ) ;
2017-05-09 11:53:41 +02:00
fe_status_t FeStatus ;
2017-04-17 15:02:44 +02:00
dtv_property Props [ MAXFRONTENDCMDS ] ;
dtv_properties CmdSeq ;
memset ( & Props , 0 , sizeof ( Props ) ) ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Props ;
2017-05-09 11:53:41 +02:00
Valid = DTV_STAT_VALID_NONE ;
if ( ioctl ( fd_frontend , FE_READ_STATUS , & FeStatus ) ! = 0 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
2017-05-09 11:53:41 +02:00
return false ;
}
if ( Status ) {
* Status = DTV_STAT_HAS_NONE ;
if ( FeStatus & FE_HAS_SIGNAL ) * Status | = DTV_STAT_HAS_SIGNAL ;
if ( FeStatus & FE_HAS_CARRIER ) * Status | = DTV_STAT_HAS_CARRIER ;
if ( FeStatus & FE_HAS_VITERBI ) * Status | = DTV_STAT_HAS_VITERBI ;
if ( FeStatus & FE_HAS_SYNC ) * Status | = DTV_STAT_HAS_SYNC ;
if ( FeStatus & FE_HAS_LOCK ) * Status | = DTV_STAT_HAS_LOCK ;
Valid | = DTV_STAT_VALID_STATUS ;
}
2017-04-17 15:02:44 +02:00
if ( Strength ) SETCMD ( DTV_STAT_SIGNAL_STRENGTH , 0 ) ;
if ( Cnr ) SETCMD ( DTV_STAT_CNR , 0 ) ;
if ( BerPre ) { SETCMD ( DTV_STAT_PRE_ERROR_BIT_COUNT , 0 ) ;
SETCMD ( DTV_STAT_PRE_TOTAL_BIT_COUNT , 0 ) ; }
if ( BerPost ) { SETCMD ( DTV_STAT_POST_ERROR_BIT_COUNT , 0 ) ;
SETCMD ( DTV_STAT_POST_TOTAL_BIT_COUNT , 0 ) ; }
if ( Per ) { SETCMD ( DTV_STAT_ERROR_BLOCK_COUNT , 0 ) ;
SETCMD ( DTV_STAT_TOTAL_BLOCK_COUNT , 0 ) ; }
if ( ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ! = 0 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
2017-04-17 15:02:44 +02:00
return false ;
}
int i = 0 ;
if ( Strength ) {
if ( Props [ i ] . u . st . len > 0 ) {
switch ( Props [ i ] . u . st . stat [ 0 ] . scale ) {
case FE_SCALE_DECIBEL : * Strength = double ( Props [ i ] . u . st . stat [ 0 ] . svalue ) / 1000 ;
Valid | = DTV_STAT_VALID_STRENGTH ;
break ;
default : ;
}
}
i + + ;
}
if ( Cnr ) {
if ( Props [ i ] . u . st . len > 0 ) {
switch ( Props [ i ] . u . st . stat [ 0 ] . scale ) {
case FE_SCALE_DECIBEL : * Cnr = double ( Props [ i ] . u . st . stat [ 0 ] . svalue ) / 1000 ;
Valid | = DTV_STAT_VALID_CNR ;
break ;
default : ;
}
}
i + + ;
}
if ( BerPre ) {
if ( Props [ i ] . u . st . len > 0 & & Props [ i + 1 ] . u . st . len > 0 ) {
if ( Props [ i ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER & & Props [ i + 1 ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER ) {
uint64_t ebc = Props [ i ] . u . st . stat [ 0 ] . uvalue ; // error bit count
uint64_t tbc = Props [ i + 1 ] . u . st . stat [ 0 ] . uvalue ; // total bit count
if ( tbc > 0 ) {
* BerPre = double ( ebc ) / tbc ;
Valid | = DTV_STAT_VALID_BERPRE ;
}
}
}
i + = 2 ;
}
if ( BerPost ) {
if ( Props [ i ] . u . st . len > 0 & & Props [ i + 1 ] . u . st . len > 0 ) {
if ( Props [ i ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER & & Props [ i + 1 ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER ) {
uint64_t ebc = Props [ i ] . u . st . stat [ 0 ] . uvalue ; // error bit count
uint64_t tbc = Props [ i + 1 ] . u . st . stat [ 0 ] . uvalue ; // total bit count
if ( tbc > 0 ) {
* BerPost = double ( ebc ) / tbc ;
Valid | = DTV_STAT_VALID_BERPOST ;
}
}
}
i + = 2 ;
}
if ( Per ) {
if ( Props [ i ] . u . st . len > 0 & & Props [ i + 1 ] . u . st . len > 0 ) {
if ( Props [ i ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER & & Props [ i + 1 ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER ) {
uint64_t ebc = Props [ i ] . u . st . stat [ 0 ] . uvalue ; // error block count
uint64_t tbc = Props [ i + 1 ] . u . st . stat [ 0 ] . uvalue ; // total block count
if ( tbc > 0 ) {
* Per = double ( ebc ) / tbc ;
Valid | = DTV_STAT_VALID_PER ;
}
}
}
i + = 2 ;
}
# ifdef DEBUG_SIGNALSTATS
fprintf ( stderr , " FE %d/%d: API5 %04X " , adapter , frontend , Valid ) ;
2017-05-09 11:53:41 +02:00
if ( ( Valid & DTV_STAT_VALID_STATUS ) ! = 0 ) fprintf ( stderr , " STAT=%04X " , * Status ) ;
2017-04-17 15:02:44 +02:00
if ( ( Valid & DTV_STAT_VALID_STRENGTH ) ! = 0 ) fprintf ( stderr , " STR=%1.1fdBm " , * Strength ) ;
if ( ( Valid & DTV_STAT_VALID_CNR ) ! = 0 ) fprintf ( stderr , " CNR=%1.1fdB " , * Cnr ) ;
if ( ( Valid & DTV_STAT_VALID_BERPRE ) ! = 0 ) fprintf ( stderr , " BERPRE=%1.1e " , * BerPre ) ;
if ( ( Valid & DTV_STAT_VALID_BERPOST ) ! = 0 ) fprintf ( stderr , " BERPOST=%1.1e " , * BerPost ) ;
if ( ( Valid & DTV_STAT_VALID_PER ) ! = 0 ) fprintf ( stderr , " PER=%1.1e " , * Per ) ;
fprintf ( stderr , " \n " ) ;
# endif
return Valid ! = DTV_STAT_VALID_NONE ;
}
2017-04-06 17:12:59 +02:00
int dB1000toPercent ( int dB1000 , int Low , int High )
{
// Convert the given value, which is in 1/1000 dBm, to a percentage in the
// range 0..100. Anything below Low is considered 0%, and anything above
// High counts as 100%.
if ( dB1000 < Low )
return 0 ;
if ( dB1000 > High )
return 100 ;
// return 100 - 100 * (High - dB1000) / (High - Low); // linear conversion
// return 100 - 100 * sqr(dB1000 - High) / sqr(Low - High); // quadratic conversion, see https://www.adriangranados.com/blog/dbm-to-percent-conversion
double v = 10.0 * ( dB1000 - High ) / ( Low - High ) ; // avoids the sqr() function
return 100 - v * v ;
}
2018-02-12 14:05:22 +01:00
# define REF_S1(q1) (mod == QPSK) ? q1 : 0
# define REF_S2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == PSK_8) ? q2 : (mod == APSK_16) ? q3 : (mod == APSK_32) ? q4 : 0
# define REF_T1(q1, q2, q3) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : 0
# define REF_T2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_256) ? q4 : 0
# define REF_C1(q1, q2, q3, q4, q5) (mod == QAM_16) ? q1 : (mod == QAM_32) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_128) ? q4 : (mod == QAM_256) ? q5: 0
int StrengthToSSI ( const cChannel * Channel , int Strength , int FeModulation , int FeCoderateH , int FeFec )
{
// Strength in 0.001dBm (dBm x 1000)
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
int ssi = 0 ; // 0-100
int mod = ( FeModulation > = 0 ) ? FeModulation : dtp . Modulation ( ) ;
int cod = ( FeCoderateH > = 0 ) ? FeCoderateH : dtp . CoderateH ( ) ; // DVB-T
int fec = ( FeFec > = 0 ) ? FeFec : dtp . CoderateH ( ) ;
if ( Channel - > IsTerr ( ) ) {
int pref = 0 ;
// NorDig Unified Ver. 2.6 - 3.4.4.6 Page 43 ff.
// reference values : pref-15dBm = 0%, pref+35dBm = 100%
if ( dtp . System ( ) = = DVB_SYSTEM_1 ) { // DVB-T
fec = cod ; // adjustment for DVB-T
if ( mod = = QAM_AUTO ) mod = QPSK ;
switch ( fec ) { // dBm: Q4 Q16 Q64
case FEC_1_2 : pref = REF_T1 ( - 93 , - 87 , - 82 ) ; break ;
default :
case FEC_2_3 : pref = REF_T1 ( - 91 , - 85 , - 80 ) ; break ;
case FEC_3_4 : pref = REF_T1 ( - 90 , - 84 , - 78 ) ; break ;
case FEC_5_6 : pref = REF_T1 ( - 89 , - 83 , - 77 ) ; break ;
case FEC_7_8 : pref = REF_T1 ( - 88 , - 82 , - 76 ) ; break ;
}
}
else { // DVB-T2
if ( mod = = QAM_AUTO ) mod = QAM_64 ;
switch ( fec ) { // dBm: Q4 Q16 Q64 Q256
case FEC_1_2 : pref = REF_T2 ( - 96 , - 91 , - 86 , - 82 ) ; break ;
default :
case FEC_3_5 : pref = REF_T2 ( - 95 , - 89 , - 85 , - 80 ) ; break ;
case FEC_2_3 : pref = REF_T2 ( - 94 , - 88 , - 83 , - 78 ) ; break ;
case FEC_3_4 : pref = REF_T2 ( - 93 , - 87 , - 82 , - 76 ) ; break ;
case FEC_4_5 : pref = REF_T2 ( - 92 , - 86 , - 81 , - 75 ) ; break ;
case FEC_5_6 : pref = REF_T2 ( - 92 , - 86 , - 80 , - 74 ) ; break ;
}
}
if ( pref ) {
int prel = ( Strength / 1000 ) - pref ;
ssi = ( prel < - 15 ) ? 0 :
( prel < 0 ) ? ( prel + 15 ) * 2 / 3 : // 0% - 10%
( prel < 20 ) ? prel * 4 + 10 : // 10% - 90%
( prel < 35 ) ? ( prel - 20 ) * 2 / 3 + 90 : // 90% - 100%
100 ;
# ifdef DEBUG_SIGNALSTRENGTH
2018-02-15 15:37:01 +01:00
fprintf ( stderr , " SSI-T: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(sys:%d, mod:%d, fec:%d) \n " , Strength , pref , prel , ssi , dtp . System ( ) , mod , fec ) ;
# endif
}
}
else if ( Channel - > IsCable ( ) ) { // ! COMPLETELY UNTESTED !
// Formula: pref(dB) = -174.0 + NoiseFigure + SymRef + CnRef
// NoiseFigure = 6.5 dB; -> Tuner specific - range: 3.5 .. 9.0 dB
// SymRef = 10*log(6900000) = 68.5 dB; -> for Symbolrate of 6900 kSym/sec (TV: 6900, 6750 or 6111 kSym/sec)
// ==> pref(dB) = -174.0 + 6.5 + 68.5 + CnRef[modulation]{20,23,26,29,32}; (+/- 3 dB tuner specific)
if ( mod = = QAM_AUTO ) mod = QAM_256 ;
// Q16 Q32 Q64 Q128 Q256
int pref = REF_C1 ( - 79 , - 76 , - 73 , - 70 , - 67 ) ;
if ( pref ) {
int prel = ( Strength / 1000 ) - pref ;
ssi = ( prel < - 15 ) ? 0 :
( prel < 0 ) ? ( prel + 15 ) * 2 / 3 : // 0% - 10%
( prel < 20 ) ? prel * 4 + 10 : // 10% - 90%
( prel < 35 ) ? ( prel - 20 ) * 2 / 3 + 90 : // 90% - 100%
100 ;
# ifdef DEBUG_SIGNALSTRENGTH
fprintf ( stderr , " SSI-C: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(mod:%d) \n " , Strength , pref , prel , ssi , mod ) ;
2018-02-12 14:05:22 +01:00
# endif
}
}
else if ( Channel - > IsSat ( ) )
ssi = dB1000toPercent ( Strength , - 95000 , - 20000 ) ; // defaults
return ssi ;
}
2018-02-15 15:37:01 +01:00
// Due to missing values or the different meanings of the reported error rate, ber_sqi is currently not taken into account
# define IGNORE_BER 1
2018-02-12 14:05:22 +01:00
# define BER_ERROR_FREE (1000*1000*1000) // 1/10^-9
int SignalToSQI ( const cChannel * Channel , int Signal , int Ber , int FeModulation , int FeCoderateH , int FeFec )
{
# if IGNORE_BER
Ber = BER_ERROR_FREE ; // assume/pretend to be biterror free
# endif
// Signal in 0.001dB (dB x 1000)
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
int sqi = 0 ; // 0-100
int mod = ( FeModulation > = 0 ) ? FeModulation : dtp . Modulation ( ) ;
int cod = ( FeCoderateH > = 0 ) ? FeCoderateH : dtp . CoderateH ( ) ; // DVB-T
int fec = ( FeFec > = 0 ) ? FeFec : dtp . CoderateH ( ) ;
if ( Channel - > IsTerr ( ) ) { // QEF: BER 10^-6
int cnref = 0 ;
// NorDig Unified Ver. 2.6 - 3.4.4.7 Page 45 ff.
// reference values for QEF (BER 10^-11 at MPEG2 demux input)
if ( dtp . System ( ) = = DVB_SYSTEM_1 ) { // DVB-T
fec = cod ; // adjustment for DVB-T
if ( mod = = QAM_AUTO ) mod = QPSK ;
switch ( fec ) { // 0.1 dB Q4 Q16 Q64 (Hierarchy=None)
case FEC_1_2 : cnref = REF_T1 ( 51 , 108 , 165 ) ; break ;
default :
case FEC_2_3 : cnref = REF_T1 ( 69 , 131 , 187 ) ; break ;
case FEC_3_4 : cnref = REF_T1 ( 79 , 146 , 202 ) ; break ;
case FEC_5_6 : cnref = REF_T1 ( 89 , 156 , 216 ) ; break ;
case FEC_7_8 : cnref = REF_T1 ( 97 , 160 , 225 ) ; break ;
}
}
else { // DVB-T2
if ( mod = = QAM_AUTO ) mod = QAM_64 ;
switch ( fec ) { // 0.1 dB Q4 Q16 Q64 Q256
case FEC_1_2 : cnref = REF_T2 ( 35 , 87 , 130 , 170 ) ; break ;
default :
case FEC_3_5 : cnref = REF_T2 ( 47 , 101 , 148 , 194 ) ; break ;
case FEC_2_3 : cnref = REF_T2 ( 56 , 114 , 162 , 208 ) ; break ;
case FEC_3_4 : cnref = REF_T2 ( 66 , 125 , 177 , 229 ) ; break ;
case FEC_4_5 : cnref = REF_T2 ( 72 , 133 , 187 , 243 ) ; break ;
case FEC_5_6 : cnref = REF_T2 ( 77 , 138 , 194 , 251 ) ; break ;
}
}
if ( cnref ) {
int cnrel = ( Signal / 100 ) - cnref ; // 0.1 dB
int ber_sqi = 100 ; // 100%
int cnr_sqi = 0 ; // 0%
if ( dtp . System ( ) = = DVB_SYSTEM_1 ) { // DVB-T
ber_sqi = ( Ber < 1000 ) ? 0 : // > 10^-3
( Ber > = 10000000 ) ? 100 : // <= 10^-7
( int ) ( 20 * log10 ( Ber ) ) - 40 ; // 20*log10(1/BER)-40 -> 20% .. 100%
// scale: -7dB/+3dB to reference-value
cnr_sqi = ( cnrel < - 70 ) ? 0 :
( cnrel < + 30 ) ? ( 100 + ( cnrel - 30 ) ) :
100 ;
sqi = ( cnr_sqi * ber_sqi ) / 100 ;
// alternative: stretched scale: cnref-7dB = 0%, 30dB = 100%
// sqi = dB1000toPercent(Signal, (100*cnref)-7000, 30000);
}
else { // DVB-T2
ber_sqi = ( Ber < 10000 ) ? 0 : // > 10^-4
( Ber > = 10000000 ) ? 100 * 100 / 6 : // <= 10^-7 : 16.67% -> SQI 0% .. 100%
( 100 * 100 / 15 ) ; // 6.67% -> SQI 0% .. 40% || 100%
// scale: -3dB/+3dB to reference-value
sqi = ( cnrel < - 30 ) ? 0 :
( cnrel < = + 30 ) ? ( cnrel + 30 ) * ber_sqi / 1000 : // (0 .. 6) * 16,67 || 6.67
100 ;
// alternative: stretched scale: cnref-3dB = 0%, 32dB = 100%
// sqi = dB1000toPercent(Signal, (100*cnref)-3000, 32000);
}
# ifdef DEBUG_SIGNALQUALITY
fprintf ( stderr , " SQI-T: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d) \n " , Signal , Ber , cnref , cnrel , ber_sqi , sqi , dtp . System ( ) , mod , fec ) ;
# endif
}
}
else if ( Channel - > IsCable ( ) ) { // ! COMPLETELY UNTESTED !
2018-02-15 15:37:01 +01:00
if ( mod = = QAM_AUTO ) mod = QAM_256 ;
2018-02-12 14:05:22 +01:00
// 0.1 dB Q16 Q32 Q64 Q128 Q256
int cnref = REF_C1 ( 200 , 230 , 260 , 290 , 320 ) ; // minimum for BER<10^-4
if ( cnref ) {
int cnrel = ( Signal / 100 ) - cnref ; // 0.1 dB
int ber_sqi = ( Ber < 1000 ) ? 0 : // > 10^-3
( Ber > = 10000000 ) ? 100 : // <= 10^-7
( int ) ( 20 * log10 ( Ber ) ) - 40 ; // 20*log10(1/BER)-40 -> 20% .. 100%
// scale: -7dB/+3dB to reference-value
int cnr_sqi = ( cnrel < - 70 ) ? 0 :
( cnrel < + 30 ) ? ( 100 + ( cnrel - 30 ) ) :
100 ;
sqi = ( cnr_sqi * ber_sqi ) / 100 ;
// alternative: stretched scale: cnref-7dB = 0%, 40dB = 100%
// sqi = dB1000toPercent(Signal, (100*cnref)-7000, 40000);
# ifdef DEBUG_SIGNALQUALITY
dsyslog ( " SQI-C: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d) \n " , Signal , Ber , cnref , cnrel , ber_sqi , sqi , dtp . System ( ) , mod , fec ) ;
# endif
}
}
else if ( Channel - > IsSat ( ) ) {
int cnref = 0 ;
if ( dtp . System ( ) = = DVB_SYSTEM_1 ) { // DVB-S
if ( mod = = QAM_AUTO ) mod = QPSK ;
switch ( fec ) { // 0.1 dB: Q4 : 10^-7
case FEC_1_2 : cnref = REF_S1 ( 38 ) ; break ;
default :
case FEC_2_3 : cnref = REF_S1 ( 56 ) ; break ;
case FEC_3_4 : cnref = REF_S1 ( 67 ) ; break ;
case FEC_5_6 : cnref = REF_S1 ( 77 ) ; break ;
case FEC_7_8 : cnref = REF_S1 ( 84 ) ; break ;
}
if ( cnref ) {
//cnrel = (Signal/100) - cnref; // 0.1 dB
// scale: cnref-4dB = 0%, 15dB = 100%
sqi = dB1000toPercent ( Signal , ( 100 * cnref ) - 4000 , 15000 ) ;
# ifdef DEBUG_SIGNALQUALITY
dsyslog ( " SQI-S1: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d) \n " , Signal , Ber , cnref , sqi , mod , fec ) ;
# endif
}
}
else { // DVB-S2
if ( mod = = QAM_AUTO ) mod = QAM_64 ;
switch ( fec ) { // 0.1 dB Q4 Q8 16A* 32A*
//case FEC_1_4: cnref = REF_S2(-14, 65, 90, 126); break;
//case FEC_1_3: cnref = REF_S2( -2, 65, 90, 126); break;
case FEC_2_5 : cnref = REF_S2 ( 7 , 65 , 90 , 126 ) ; break ;
case FEC_1_2 : cnref = REF_S2 ( 20 , 65 , 90 , 126 ) ; break ;
case FEC_3_5 : cnref = REF_S2 ( 32 , 65 , 90 , 126 ) ; break ;
default :
case FEC_2_3 : cnref = REF_S2 ( 41 , 76 , 90 , 126 ) ; break ;
case FEC_3_4 : cnref = REF_S2 ( 50 , 66 , 102 , 126 ) ; break ;
case FEC_4_5 : cnref = REF_S2 ( 57 , 89 , 110 , 136 ) ; break ;
case FEC_5_6 : cnref = REF_S2 ( 62 , 104 , 116 , 143 ) ; break ;
case FEC_8_9 : cnref = REF_S2 ( 72 , 117 , 129 , 157 ) ; break ;
case FEC_9_10 : cnref = REF_S2 ( 74 , 120 , 131 , 161 ) ; break ;
}
if ( cnref ) {
// cnrel = (Signal/100) - cnref; // 0.1 dB
// scale: cnref-4dB = 0%, 20dB = 100%
sqi = dB1000toPercent ( Signal , ( 100 * cnref ) - 4000 , 20000 ) ;
# ifdef DEBUG_SIGNALQUALITY
dsyslog ( " SQI-S2: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d) \n " , Signal , Ber , cnref , sqi , mod , fec ) ;
# endif
}
}
}
return sqi ;
}
2011-06-02 13:28:42 +02:00
int cDvbTuner : : GetSignalStrength ( void ) const
{
ClearEventQueue ( ) ;
2017-04-06 17:12:59 +02:00
// Try DVB API 5:
for ( int i = 0 ; i < 1 ; i + + ) { // just a trick to break out with 'continue' ;-)
dtv_property Props [ MAXFRONTENDCMDS ] ;
dtv_properties CmdSeq ;
memset ( & Props , 0 , sizeof ( Props ) ) ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Props ;
SETCMD ( DTV_STAT_SIGNAL_STRENGTH , 0 ) ;
2018-02-12 14:05:22 +01:00
SETCMD ( DTV_MODULATION , 0 ) ;
SETCMD ( DTV_CODE_RATE_HP , 0 ) ; // DVB-T only
SETCMD ( DTV_INNER_FEC , 0 ) ;
2017-04-06 17:12:59 +02:00
if ( ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ! = 0 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
2017-04-06 17:12:59 +02:00
return - 1 ;
}
2018-02-12 14:05:22 +01:00
int FeMod = ( Props [ 1 ] . u . st . len > 0 ) ? ( int ) Props [ 1 ] . u . data : - 1 ;
int FeCod = ( Props [ 2 ] . u . st . len > 0 ) ? ( int ) Props [ 2 ] . u . data : - 1 ;
int FeFec = ( Props [ 3 ] . u . st . len > 0 ) ? ( int ) Props [ 3 ] . u . data : - 1 ;
2018-01-25 15:09:23 +01:00
int Signal = 0 ;
2017-04-06 17:12:59 +02:00
if ( Props [ 0 ] . u . st . len > 0 ) {
switch ( Props [ 0 ] . u . st . stat [ 0 ] . scale ) {
2018-02-12 14:05:22 +01:00
case FE_SCALE_DECIBEL : Signal = StrengthToSSI ( & channel , Props [ 0 ] . u . st . stat [ 0 ] . svalue , FeMod , FeCod , FeFec ) ;
2017-04-06 17:12:59 +02:00
break ;
case FE_SCALE_RELATIVE : Signal = 100 * Props [ 0 ] . u . st . stat [ 0 ] . uvalue / 0xFFFF ;
break ;
default : ;
}
# ifdef DEBUG_SIGNALSTRENGTH
fprintf ( stderr , " FE %d/%d: API5 %d %08X %.1f S = %d \n " , adapter , frontend , Props [ 0 ] . u . st . stat [ 0 ] . scale , int ( Props [ 0 ] . u . st . stat [ 0 ] . svalue ) , int ( Props [ 0 ] . u . st . stat [ 0 ] . svalue ) / 1000.0 , Signal ) ;
# endif
}
else
continue ;
return Signal ;
}
// Fall back to DVB API 3:
2011-06-02 13:28:42 +02:00
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.
2018-10-29 12:16:35 +01:00
switch ( dvbFrontend - > SubsystemId ( ) ) {
2013-03-16 15:30:12 +01:00
case 0x13C21019 : // TT-budget S2-3200 (DVB-S/DVB-S2)
case 0x1AE40001 : // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
MaxSignal = 670 ; break ;
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
2018-10-29 12:16:35 +01:00
fprintf ( stderr , " FE %d/%d: API3 %08X S = %04X %04X %3d%% \n " , adapter , frontend , dvbFrontend - > SubsystemId ( ) , MaxSignal , Signal , s ) ;
2011-06-02 13:28:42 +02:00
# 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
{
2017-04-06 17:12:59 +02:00
// Try DVB API 5:
for ( int i = 0 ; i < 1 ; i + + ) { // just a trick to break out with 'continue' ;-)
dtv_property Props [ MAXFRONTENDCMDS ] ;
dtv_properties CmdSeq ;
memset ( & Props , 0 , sizeof ( Props ) ) ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
CmdSeq . props = Props ;
SETCMD ( DTV_STAT_CNR , 0 ) ;
2018-02-12 14:05:22 +01:00
SETCMD ( DTV_MODULATION , 0 ) ;
SETCMD ( DTV_CODE_RATE_HP , 0 ) ; // DVB-T only
SETCMD ( DTV_INNER_FEC , 0 ) ;
SETCMD ( DTV_STAT_POST_ERROR_BIT_COUNT , 0 ) ;
SETCMD ( DTV_STAT_POST_TOTAL_BIT_COUNT , 0 ) ;
2017-04-06 17:12:59 +02:00
if ( ioctl ( fd_frontend , FE_GET_PROPERTY , & CmdSeq ) ! = 0 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
2017-04-06 17:12:59 +02:00
return - 1 ;
}
2018-02-12 14:05:22 +01:00
int FeMod = ( Props [ 1 ] . u . st . len > 0 ) ? ( int ) Props [ 1 ] . u . data : - 1 ;
int FeCod = ( Props [ 2 ] . u . st . len > 0 ) ? ( int ) Props [ 2 ] . u . data : - 1 ;
int FeFec = ( Props [ 3 ] . u . st . len > 0 ) ? ( int ) Props [ 3 ] . u . data : - 1 ;
int Ber = BER_ERROR_FREE ; // 1/10^-9
if ( Props [ 4 ] . u . st . len > 0 & & Props [ 4 ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER & & Props [ 5 ] . u . st . len > 0 & & Props [ 5 ] . u . st . stat [ 0 ] . scale = = FE_SCALE_COUNTER ) {
uint64_t ebc = Props [ 4 ] . u . st . stat [ 0 ] . uvalue ; // error bit count
uint64_t tbc = Props [ 5 ] . u . st . stat [ 0 ] . uvalue ; // total bit count
if ( ebc > 0 ) {
uint64_t BerRev = tbc / ebc ; // reversed, for integer arithmetic
if ( BerRev < BER_ERROR_FREE )
Ber = ( int ) BerRev ;
}
}
2018-01-25 15:09:23 +01:00
int Cnr = 0 ;
2017-04-06 17:12:59 +02:00
if ( Props [ 0 ] . u . st . len > 0 ) {
switch ( Props [ 0 ] . u . st . stat [ 0 ] . scale ) {
2018-02-12 14:05:22 +01:00
case FE_SCALE_DECIBEL : Cnr = SignalToSQI ( & channel , Props [ 0 ] . u . st . stat [ 0 ] . svalue , Ber , FeMod , FeCod , FeFec ) ;
2017-04-06 17:12:59 +02:00
break ;
case FE_SCALE_RELATIVE : Cnr = 100 * Props [ 0 ] . u . st . stat [ 0 ] . uvalue / 0xFFFF ;
break ;
default : ;
}
# ifdef DEBUG_SIGNALQUALITY
fprintf ( stderr , " FE %d/%d: API5 %d %08X %.1f Q = %d \n " , adapter , frontend , Props [ 0 ] . u . st . stat [ 0 ] . scale , int ( Props [ 0 ] . u . st . stat [ 0 ] . svalue ) , int ( Props [ 0 ] . u . st . stat [ 0 ] . svalue ) / 1000.0 , Cnr ) ;
# endif
}
else
continue ;
return Cnr ;
}
// Fall back to DVB API 3:
2011-06-02 13:28:42 +02:00
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 ) {
2017-04-20 14:43:54 +02:00
if ( ioctl ( fd_frontend , FE_READ_UNCORRECTED_BLOCKS , & Unc ) ! = - 1 ) {
if ( Unc ! = lastUncValue ) {
# ifdef DEBUG_SIGNALQUALITY
fprintf ( stderr , " FE %d/%d: API3 UNC = %u %u %u \n " , adapter , frontend , Unc , lastUncValue , lastUncDelta ) ;
# endif
lastUncDelta = ( Unc > = lastUncValue ) ? Unc - lastUncValue : lastUncValue - Unc ;
lastUncValue = Unc ;
lastUncChange = time ( NULL ) ;
}
// The number of uncorrected blocks is a counter, which is normally
// at a constant value and only increases if there are new uncorrected
// blocks. So a change in the Unc value indicates reduced signal quality.
// Whenever the Unc counter changes, we take the delta between the old
// and new value into account for calculating the overall signal quality.
// The impact of Unc is considered for 2 seconds, and after that it is
// bisected with every passing second in order to phase it out. Otherwise
// once one or more uncorrected blocks occur, the signal quality would
// be considered low even if there haven't been any more uncorrected bocks
// for quite a while.
Unc = lastUncDelta ;
2017-05-01 12:50:12 +02:00
if ( Unc > 0 ) {
int t = time ( NULL ) - lastUncChange - 2 ;
if ( t > 0 )
Unc > > = min ( t , int ( sizeof ( Unc ) * 8 - 1 ) ) ;
if ( Unc = = 0 )
lastUncDelta = 0 ;
2017-04-20 14:43:54 +02:00
# ifdef DEBUG_SIGNALQUALITY
fprintf ( stderr , " FE %d/%d: API3 UNC = %u \n " , adapter , frontend , Unc ) ;
# endif
2017-05-01 12:50:12 +02:00
}
2011-06-02 13:28:42 +02:00
break ;
2017-04-20 14:43:54 +02:00
}
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.
2018-10-29 12:16:35 +01:00
switch ( dvbFrontend - > SubsystemId ( ) ) {
2013-03-16 15:30:12 +01:00
case 0x13C21019 : // TT-budget S2-3200 (DVB-S/DVB-S2)
case 0x1AE40001 : // TechniSat SkyStar HD2 (DVB-S/DVB-S2)
if ( frontendType = = SYS_DVBS2 ) {
2013-02-19 12:16:42 +01:00
MinSnr = 10 ;
MaxSnr = 70 ;
}
else
MaxSnr = 200 ;
break ;
2013-03-16 15:30:12 +01:00
case 0x20130245 : // PCTV Systems PCTV 73ESE
case 0x2013024F : // PCTV Systems nanoStick T2 290e
MaxSnr = 255 ; break ;
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
2018-10-29 12:16:35 +01:00
fprintf ( stderr , " FE %d/%d: API3 %08X Q = %04X %04X %d %5d %5d %3d%% \n " , adapter , frontend , dvbFrontend - > SubsystemId ( ) , MaxSnr , Snr , HasSnr , HasBer ? int ( Ber ) : - 1 , HasUnc ? int ( Unc ) : - 1 , q ) ;
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 ;
}
2013-08-21 11:02:52 +02:00
cPositioner * cDvbTuner : : GetPositioner ( void )
{
if ( ! positioner ) {
positioner = cPositioner : : GetPositioner ( ) ;
positioner - > SetFrontend ( fd_frontend ) ;
}
return positioner ;
}
2017-01-09 15:21:13 +01:00
void cDvbTuner : : ExecuteDiseqc ( const cDiseqc * Diseqc , 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 ;
2013-08-21 11:02:52 +02:00
cPositioner * Positioner = NULL ;
bool Break = false ;
for ( int i = 0 ; ! Break ; i + + ) {
2011-09-11 14:09:03 +02:00
cmd . msg_len = sizeof ( cmd . msg ) ;
cDiseqc : : eDiseqcActions da = Diseqc - > Execute ( & CurrentAction , cmd . msg , & cmd . msg_len , scr , Frequency ) ;
2013-08-21 11:02:52 +02:00
if ( da = = cDiseqc : : daNone ) {
diseqcOffset = 0 ;
2011-09-11 14:09:03 +02:00
break ;
2013-08-21 11:02:52 +02:00
}
bool d = i > = diseqcOffset ;
2011-09-11 14:09:03 +02:00
switch ( da ) {
2013-08-21 11:02:52 +02:00
case cDiseqc : : daToneOff : if ( d ) CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_OFF ) ) ; break ;
case cDiseqc : : daToneOn : if ( d ) CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_ON ) ) ; break ;
case cDiseqc : : daVoltage13 : if ( d ) CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_13 ) ) ; break ;
case cDiseqc : : daVoltage18 : if ( d ) CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , SEC_VOLTAGE_18 ) ) ; break ;
case cDiseqc : : daMiniA : if ( d ) CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_BURST , SEC_MINI_A ) ) ; break ;
case cDiseqc : : daMiniB : if ( d ) CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_BURST , SEC_MINI_B ) ) ; break ;
case cDiseqc : : daCodes : if ( d ) CHECK ( ioctl ( fd_frontend , FE_DISEQC_SEND_MASTER_CMD , & cmd ) ) ; break ;
case cDiseqc : : daPositionN : if ( ( Positioner = GetPositioner ( ) ) ! = NULL ) {
if ( d ) {
Positioner - > GotoPosition ( Diseqc - > Position ( ) , cSource : : Position ( channel . Source ( ) ) ) ;
Break = Positioner - > IsMoving ( ) ;
}
}
break ;
case cDiseqc : : daPositionA : if ( ( Positioner = GetPositioner ( ) ) ! = NULL ) {
if ( d ) {
Positioner - > GotoAngle ( cSource : : Position ( channel . Source ( ) ) ) ;
Break = Positioner - > IsMoving ( ) ;
}
}
break ;
case cDiseqc : : daScr :
case cDiseqc : : daWait : break ;
2011-09-11 14:09:03 +02:00
default : esyslog ( " ERROR: unknown diseqc command %d " , da ) ;
}
2013-08-21 11:02:52 +02:00
if ( Break )
diseqcOffset = i + 1 ;
2011-09-11 14:09:03 +02:00
}
2013-08-21 11:02:52 +02:00
positioner = Positioner ;
if ( scr & & ! Break )
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 )
{
2013-08-23 09:47:27 +02:00
CHECK ( ioctl ( fd_frontend , FE_SET_VOLTAGE , bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13 ) ) ;
2011-12-04 12:45:26 +01:00
CHECK ( ioctl ( fd_frontend , FE_SET_TONE , SEC_TONE_OFF ) ) ;
2011-09-11 14:09:03 +02:00
}
2002-12-08 09:55:26 +01:00
bool cDvbTuner : : SetFrontend ( void )
{
2017-04-06 17:12:59 +02:00
dtv_property Props [ MAXFRONTENDCMDS ] ;
memset ( & Props , 0 , sizeof ( Props ) ) ;
2008-12-13 12:22:36 +01:00
dtv_properties CmdSeq ;
memset ( & CmdSeq , 0 , sizeof ( CmdSeq ) ) ;
2017-04-06 17:12:59 +02:00
CmdSeq . props = Props ;
2008-12-13 12:22:36 +01:00
SETCMD ( DTV_CLEAR , 0 ) ;
if ( ioctl ( fd_frontend , FE_SET_PROPERTY , & CmdSeq ) < 0 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
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 ) {
2017-01-09 15:21:13 +01:00
int frequency = channel . Frequency ( ) ;
2008-04-12 13:39:12 +02:00
if ( Setup . DiSEqC ) {
2019-05-28 15:54:22 +02:00
if ( const cDiseqc * diseqc = Diseqcs . Get ( device - > DeviceNumber ( ) + 1 , channel . Source ( ) , frequency , dtp . Polarization ( ) , & scr ) ) {
2008-04-12 13:39:12 +02:00
frequency - = diseqc - > Lof ( ) ;
2013-08-21 11:02:52 +02:00
if ( diseqc ! = lastDiseqc | | diseqc - > IsScr ( ) | | diseqc - > Position ( ) > = 0 & & channel . Source ( ) ! = lastSource ) {
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 ;
2013-08-21 11:02:52 +02:00
lastSource = channel . Source ( ) ;
2011-09-11 14:09:03 +02:00
}
2008-04-12 13:39:12 +02:00
}
else {
2015-01-14 12:17:39 +01:00
esyslog ( " ERROR: no DiSEqC parameters found for channel %d (%s) " , channel . Number ( ) , channel . Name ( ) ) ;
2008-04-12 13:39:12 +02:00
return false ;
}
}
else {
int tone = SEC_TONE_OFF ;
2017-01-09 15:21:13 +01:00
if ( frequency < Setup . LnbSLOF ) {
2008-04-12 13:39:12 +02:00
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
2014-03-16 10:48:30 +01:00
SETCMD ( DTV_PILOT , dtp . Pilot ( ) ) ;
2012-01-08 09:27:59 +01:00
SETCMD ( DTV_ROLLOFF , dtp . RollOff ( ) ) ;
2013-04-09 09:53:00 +02:00
if ( DvbApiVersion > = 0x0508 )
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
2018-02-12 14:05:22 +01:00
SETCMD ( DTV_INNER_FEC , dtp . CoderateH ( ) ) ;
2013-04-09 09:53:00 +02:00
if ( DvbApiVersion > = 0x0508 ) {
SETCMD ( DTV_STREAM_ID , dtp . StreamId ( ) ) ;
}
else if ( DvbApiVersion > = 0x0503 )
SETCMD ( DTV_DVBT2_PLP_ID_LEGACY , 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 ) {
2018-10-29 12:16:35 +01:00
esyslog ( " ERROR: frontend %d/%d: %m (%s:%d) " , adapter , frontend , __FILE__ , __LINE__ ) ;
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 ( ) ) {
2018-10-29 12:16:35 +01:00
int WaitTime = 1000 ;
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 :
2013-08-21 11:02:52 +02:00
break ; // we want the TimedWait() below!
2005-08-21 09:25:51 +02:00
case tsSet :
2013-08-21 11:02:52 +02:00
tunerStatus = SetFrontend ( ) ? tsPositioning : tsIdle ;
2005-08-21 09:25:51 +02:00
continue ;
2013-08-21 11:02:52 +02:00
case tsPositioning :
if ( positioner ) {
if ( positioner - > IsMoving ( ) )
break ; // we want the TimedWait() below!
else if ( diseqcOffset ) {
lastDiseqc = NULL ;
tunerStatus = tsSet ; // have it process the rest of the DiSEqC sequence
continue ;
}
}
tunerStatus = tsTuned ;
2020-05-04 08:50:20 +02:00
device - > SectionHandler ( ) - > SetStatus ( true ) ; // may have been turned off when retuning
2013-08-21 11:02:52 +02:00
Timer . Set ( tuneTimeout + ( scr ? rand ( ) % SCR_RANDOM_TIMEOUT : 0 ) ) ;
if ( positioner )
continue ;
// otherwise run directly into tsTuned...
2005-08-21 09:25:51 +02:00
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 ;
2013-08-21 11:02:52 +02:00
lastSource = 0 ;
2006-01-03 10:42:47 +01:00
if ( time ( NULL ) - lastTimeoutReport > 60 ) { // let's not get too many of these
2019-03-15 14:51:22 +01:00
if ( channel . Number ( ) ) // no need to log this for transponders that are announced in the NIT but are not currently broadcasting
isyslog ( " frontend %d/%d timed out while tuning to channel %d (%s), tp %d " , adapter , frontend , channel . Number ( ) , channel . Name ( ) , 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
2013-08-21 11:02:52 +02:00
// run into 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 ;
2013-08-21 11:02:52 +02:00
lastSource = 0 ;
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 ) {
2015-01-14 12:17:39 +01:00
isyslog ( " frontend %d/%d regained lock on channel %d (%s), tp %d " , adapter , frontend , channel . Number ( ) , channel . Name ( ) , channel . Transponder ( ) ) ;
2006-01-04 11:48:38 +01:00
LostLock = false ;
2006-01-01 12:22:18 +01:00
}
2020-05-04 08:50:20 +02:00
if ( device - > SdtFilter ( ) - > TransponderWrong ( ) ) {
isyslog ( " frontend %d/%d is not receiving transponder %d for channel %d (%s) - retuning " , adapter , frontend , channel . Transponder ( ) , channel . Number ( ) , channel . Name ( ) ) ;
device - > SectionHandler ( ) - > SetStatus ( false ) ;
tunerStatus = tsSet ;
lastDiseqc = NULL ;
lastSource = 0 ;
continue ;
}
2006-01-04 11:48:38 +01:00
tunerStatus = tsLocked ;
locked . Broadcast ( ) ;
lastTimeoutReport = 0 ;
}
else if ( tunerStatus = = tsLocked ) {
LostLock = true ;
2015-01-14 12:17:39 +01:00
isyslog ( " frontend %d/%d lost lock on channel %d (%s), tp %d " , adapter , frontend , channel . Number ( ) , channel . Name ( ) , 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 ( ) ;
2014-03-16 10:48:30 +01:00
case 13 : ST ( " S " ) return new cMenuEditMapItem ( tr ( " Pilot " ) , & dtp . pilot , PilotValues ) ; else return GetOsdItem ( ) ;
case 14 : ST ( " T " ) return new cMenuEditIntItem ( tr ( " T2SystemId " ) , & dtp . t2systemId , 0 , 65535 ) ; else return GetOsdItem ( ) ;
case 15 : ST ( " T " ) return new cMenuEditIntItem ( tr ( " SISO/MISO " ) , & dtp . sisoMiso , 0 , 1 ) ; 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 ------------------------------------------------------------
2015-04-18 14:30:47 +02:00
bool cDvbDevice : : useDvbDevices = true ;
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
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 ;
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
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 ) ;
2017-04-15 09:39:55 +02:00
checkTsBuffer = false ;
2007-01-07 14:46:14 +01:00
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
2018-10-29 12:16:35 +01:00
dvbTuner = new cDvbTuner ( this , 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...
}
2018-10-29 12:16:35 +01:00
cString 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
}
2018-10-29 12:16:35 +01:00
int 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 ;
}
2019-03-10 12:10:01 +01:00
int cDvbDevice : : Frontend ( void ) const
{
return dvbTuner ? dvbTuner - > Frontend ( ) : frontend ;
}
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
{
2018-10-29 12:16:35 +01:00
if ( dvbTuner )
return GetDeliverySystemName ( dvbTuner - > FrontendType ( ) ) ;
2012-04-04 09:52:15 +02:00
return " " ;
}
2012-03-13 10:20:42 +01:00
cString cDvbDevice : : DeviceName ( void ) const
{
2018-10-29 12:16:35 +01:00
if ( dvbTuner )
return dvbTuner - > FrontendName ( ) ;
return " " ;
2012-03-13 10:20:42 +01:00
}
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 ) ;
2019-05-28 15:54:22 +02:00
if ( access ( DvbName ( DEV_DVB_DEMUX , Adapter , Frontend ) , F_OK ) = = 0 ) { // we only create devices for actual demuxes
dsyslog ( " detected /dev/dvb/adapter%d/frontend%d " , Adapter , Frontend ) ;
2018-10-29 12:16:35 +01:00
Nodes . Append ( strdup ( cString : : sprintf ( " %2d %2d " , Adapter , Frontend ) ) ) ;
2019-05-28 15:54:22 +02:00
}
2012-02-17 12:38:57 +01:00
}
}
}
}
}
}
2010-01-04 14:16:11 +01:00
int Found = 0 ;
2014-01-01 14:26:26 +01:00
int Used = 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 ) ) {
2014-01-01 14:26:26 +01:00
if ( Found < MAXDEVICES ) {
Found + + ;
2015-04-18 14:30:47 +02:00
if ( useDvbDevices & & UseDevice ( NextCardIndex ( ) ) ) {
2012-02-17 12:38:57 +01:00
if ( Probe ( Adapter , Frontend ) )
2014-01-01 14:26:26 +01:00
Used + + ;
2012-02-17 12:38:57 +01:00
}
2019-05-28 15:54:22 +02:00
else {
dsyslog ( " skipped /dev/dvb/adapter%d/frontend%d " , Adapter , Frontend ) ;
2012-02-17 12:38:57 +01:00
NextCardIndex ( 1 ) ; // skips this one
2019-05-28 15:54:22 +02:00
}
2012-02-17 12:38:57 +01:00
}
}
}
}
}
2014-01-01 14:26:26 +01:00
if ( Found > 0 ) {
2010-01-04 14:16:11 +01:00
isyslog ( " found %d DVB device%s " , Found , Found > 1 ? " s " : " " ) ;
2014-01-01 14:26:26 +01:00
if ( Used ! = Found )
2015-04-18 16:19:28 +02:00
isyslog ( " using only %d DVB device%s " , Used , Used ! = 1 ? " s " : " " ) ;
2014-01-01 14:26:26 +01:00
}
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
}
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 ;
2019-05-28 15:54:22 +02:00
dsyslog ( " device %d bonded with device %d " , DeviceNumber ( ) + 1 , bondedDevice - > DeviceNumber ( ) + 1 ) ;
2011-12-04 12:45:26 +01:00
return true ;
}
}
else
2019-05-28 15:54:22 +02:00
esyslog ( " ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded) " , DeviceNumber ( ) + 1 , Device - > DeviceNumber ( ) + 1 ) ;
2011-12-04 12:45:26 +01:00
}
else
2019-05-28 15:54:22 +02:00
esyslog ( " ERROR: can't bond device %d with itself " , DeviceNumber ( ) + 1 ) ;
2011-12-04 12:45:26 +01:00
}
else
2019-05-28 15:54:22 +02:00
esyslog ( " ERROR: device %d already bonded with device %d, can't bond with device %d " , DeviceNumber ( ) + 1 , bondedDevice - > DeviceNumber ( ) + 1 , Device - > DeviceNumber ( ) + 1 ) ;
2011-12-04 12:45:26 +01:00
return false ;
}
void cDvbDevice : : UnBond ( void )
{
cMutexLock MutexLock ( & bondMutex ) ;
if ( cDvbDevice * d = bondedDevice ) {
if ( dvbTuner )
dvbTuner - > UnBond ( ) ;
2019-05-28 15:54:22 +02:00
dsyslog ( " device %d unbonded from device %d " , DeviceNumber ( ) + 1 , bondedDevice - > DeviceNumber ( ) + 1 ) ;
2011-12-04 12:45:26 +01:00
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 ) ;
2013-08-21 11:02:52 +02:00
if ( bondedDevice | | Positioner ( ) )
2011-12-04 12:45:26 +01:00
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
{
2018-10-29 12:16:35 +01:00
return dvbTuner - > ProvidesDeliverySystem ( DeliverySystem ) ;
2012-01-08 09:27:59 +01:00
}
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
2018-10-29 12:16:35 +01:00
if ( ! dvbTuner - > ProvidesFrontend ( Channel ) )
2008-04-12 13:39:12 +02:00
return false ; // requires modulation system which frontend doesn't provide
2018-10-29 12:16:35 +01:00
cDvbTransponderParameters dtp ( Channel - > Parameters ( ) ) ;
2010-04-05 20:13:05 +02:00
if ( ! cSource : : IsSat ( Channel - > Source ( ) ) | |
2019-05-28 15:54:22 +02:00
( ! Setup . DiSEqC | | Diseqcs . Get ( DeviceNumber ( ) + 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
2018-10-29 12:16:35 +01:00
if ( 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 ) {
2013-03-13 11:23:53 +01:00
cMutexLock MutexLock ( & bondMutex ) ;
2011-12-04 12:45:26 +01:00
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 ;
}
2013-03-13 11:23:53 +01:00
needsDetachReceivers | = d - > Receiving ( ) ;
2011-12-04 12:45:26 +01:00
}
needsDetachBondedReceivers = true ;
2013-03-13 11:23:53 +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
{
2020-06-10 14:52:43 +02:00
return dvbTuner ! = NULL & & DeviceHooksProvidesEIT ( ) ;
2011-08-26 13:03:14 +02:00
}
2008-04-12 13:39:12 +02:00
int cDvbDevice : : NumProvidedSystems ( void ) const
{
2018-10-29 12:16:35 +01:00
return dvbTuner ? dvbTuner - > NumProvidedSystems ( ) : 0 ;
2008-04-12 13:39:12 +02:00
}
2013-08-21 11:02:52 +02:00
const cPositioner * cDvbDevice : : Positioner ( void ) const
{
return dvbTuner ? dvbTuner - > Positioner ( ) : NULL ;
}
2017-05-09 11:53:41 +02:00
bool cDvbDevice : : SignalStats ( int & Valid , double * Strength , double * Cnr , double * BerPre , double * BerPost , double * Per , int * Status ) const
2017-04-17 15:02:44 +02:00
{
2017-05-09 11:53:41 +02:00
return dvbTuner ? dvbTuner - > GetSignalStats ( Valid , Strength , Cnr , BerPre , BerPost , Per , Status ) : false ;
2017-04-17 15:02:44 +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 )
{
2018-10-29 12:16:35 +01:00
if ( dvbTuner - > ProvidesFrontend ( Channel , true ) ) {
2011-12-04 12:45:26 +01:00
dvbTuner - > SetChannel ( Channel ) ;
2018-10-29 12:16:35 +01:00
return true ;
}
return false ;
2002-08-04 14:57:29 +02:00
}
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 )
2019-05-28 15:54:22 +02:00
tsBuffer = new cTSBuffer ( fd_dvr , MEGABYTE ( 5 ) , DeviceNumber ( ) + 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 ) {
2014-01-20 12:14:30 +01:00
if ( cCamSlot * cs = CamSlot ( ) ) {
if ( cs - > WantsTsData ( ) ) {
int Available ;
2017-04-15 09:39:55 +02:00
Data = tsBuffer - > Get ( & Available , checkTsBuffer ) ;
if ( ! Data )
Available = 0 ;
Data = cs - > Decrypt ( Data , Available ) ;
tsBuffer - > Skip ( Available ) ;
checkTsBuffer = Data ! = NULL ;
2014-01-20 12:14:30 +01:00
return true ;
}
2014-01-02 10:47:08 +01:00
}
2014-01-20 12:14:30 +01: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 ) ;
}
2015-01-12 11:42:15 +01:00
else {
FileName = cString : : sprintf ( " /sys/class/dvb/%s/device/idVendor " , e - > d_name ) ;
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId = strtoul ( s , NULL , 16 ) < < 16 ;
fclose ( f ) ;
}
}
2012-02-20 12:54:01 +01:00
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 ) ;
}
2015-01-12 11:42:15 +01:00
else {
FileName = cString : : sprintf ( " /sys/class/dvb/%s/device/idProduct " , e - > d_name ) ;
if ( ( f = fopen ( FileName , " r " ) ) ! = NULL ) {
if ( char * s = ReadLine . Read ( f ) )
SubsystemId | = strtoul ( s , NULL , 16 ) ;
fclose ( f ) ;
}
}
2012-02-20 12:54:01 +01:00
break ;
}
}
}
}
}
}
2011-06-02 13:28:42 +02:00
}
return SubsystemId ;
}