/***************************************************************************** * linux_dvb.c : functions to control a DVB card under Linux with v4l2 ***************************************************************************** * Copyright (C) 1998-2010 VLC authors and VideoLAN * * Authors: Damien Lucas * Johan Bilien * Jean-Paul Saman * Christopher Ross * Christophe Massiot * David Kaplan * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include /* DVB Card Drivers */ #include #include #include #include "dvb.h" #include "scan.h" #define DMX "/dev/dvb/adapter%d/demux%d" #define FRONTEND "/dev/dvb/adapter%d/frontend%d" #define DVR "/dev/dvb/adapter%d/dvr%d" /* * Frontends */ struct frontend_t { fe_status_t i_last_status; struct dvb_frontend_info info; }; #define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */ /* Local prototypes */ static int FrontendInfo( vlc_object_t *, dvb_sys_t * ); static int FrontendSetQPSK( vlc_object_t *, dvb_sys_t * ); static int FrontendSetQAM( vlc_object_t *, dvb_sys_t * ); static int FrontendSetOFDM( vlc_object_t *, dvb_sys_t * ); static int FrontendSetATSC( vlc_object_t *, dvb_sys_t * ); /***************************************************************************** * FrontendOpen : Determine frontend device information and capabilities *****************************************************************************/ int FrontendOpen( vlc_object_t *p_access, dvb_sys_t *p_sys, const char *psz_access ) { frontend_t * p_frontend; unsigned int i_adapter, i_device; bool b_probe; char frontend[128]; i_adapter = var_GetInteger( p_access, "dvb-adapter" ); i_device = var_GetInteger( p_access, "dvb-device" ); b_probe = var_GetBool( p_access, "dvb-probe" ); if( snprintf( frontend, sizeof(frontend), FRONTEND, i_adapter, i_device ) >= (int)sizeof(frontend) ) { msg_Err( p_access, "snprintf() truncated string for FRONTEND" ); frontend[sizeof(frontend) - 1] = '\0'; } p_sys->p_frontend = p_frontend = malloc( sizeof(frontend_t) ); if( !p_frontend ) return VLC_ENOMEM; msg_Dbg( p_access, "Opening device %s", frontend ); if( (p_sys->i_frontend_handle = vlc_open(frontend, O_RDWR | O_NONBLOCK)) < 0 ) { msg_Err( p_access, "FrontEndOpen: opening device failed: %s", vlc_strerror_c(errno) ); free( p_frontend ); return VLC_EGENERIC; } if( b_probe ) { const char * psz_expected = NULL; const char * psz_real; if( FrontendInfo( p_access, p_sys ) < 0 ) { vlc_close( p_sys->i_frontend_handle ); free( p_frontend ); return VLC_EGENERIC; } switch( p_frontend->info.type ) { case FE_OFDM: psz_real = "DVB-T"; break; case FE_QAM: psz_real = "DVB-C"; break; case FE_QPSK: psz_real = "DVB-S"; break; case FE_ATSC: psz_real = "ATSC"; break; default: psz_real = "unknown"; } /* Sanity checks */ if( (!strncmp( psz_access, "qpsk", 4 ) || !strncmp( psz_access, "dvb-s", 5 ) || !strncmp( psz_access, "satellite", 9 ) ) && (p_frontend->info.type != FE_QPSK) ) { psz_expected = "DVB-S"; } if( (!strncmp( psz_access, "cable", 5 ) || !strncmp( psz_access, "dvb-c", 5 ) ) && (p_frontend->info.type != FE_QAM) ) { psz_expected = "DVB-C"; } if( (!strncmp( psz_access, "terrestrial", 11 ) || !strncmp( psz_access, "dvb-t", 5 ) ) && (p_frontend->info.type != FE_OFDM) ) { psz_expected = "DVB-T"; } if( (!strncmp( psz_access, "usdigital", 9 ) || !strncmp( psz_access, "atsc", 4 ) ) && (p_frontend->info.type != FE_ATSC) ) { psz_expected = "ATSC"; } if( psz_expected != NULL ) { msg_Err( p_access, "requested type %s not supported by %s tuner", psz_expected, psz_real ); vlc_close( p_sys->i_frontend_handle ); free( p_frontend ); return VLC_EGENERIC; } } else /* no frontend probing is done so use default border values. */ { msg_Dbg( p_access, "using default values for frontend info" ); msg_Dbg( p_access, "method of access is %s", psz_access ); p_frontend->info.type = FE_QPSK; if( !strncmp( psz_access, "qpsk", 4 ) || !strncmp( psz_access, "dvb-s", 5 ) ) p_frontend->info.type = FE_QPSK; else if( !strncmp( psz_access, "cable", 5 ) || !strncmp( psz_access, "dvb-c", 5 ) ) p_frontend->info.type = FE_QAM; else if( !strncmp( psz_access, "terrestrial", 11 ) || !strncmp( psz_access, "dvb-t", 5 ) ) p_frontend->info.type = FE_OFDM; else if( !strncmp( psz_access, "usdigital", 9 ) || !strncmp( psz_access, "atsc", 4 ) ) p_frontend->info.type = FE_ATSC; } return VLC_SUCCESS; } /***************************************************************************** * FrontendClose : Close the frontend *****************************************************************************/ void FrontendClose( vlc_object_t *p_access, dvb_sys_t *p_sys ) { VLC_UNUSED(p_access); if( p_sys->p_frontend ) { vlc_close( p_sys->i_frontend_handle ); free( p_sys->p_frontend ); p_sys->p_frontend = NULL; } } /***************************************************************************** * FrontendSet : Tune ! *****************************************************************************/ int FrontendSet( vlc_object_t *p_access, dvb_sys_t *p_sys ) { switch( p_sys->p_frontend->info.type ) { /* DVB-S */ case FE_QPSK: if( FrontendSetQPSK( p_access, p_sys ) ) { msg_Err( p_access, "DVB-S tuning error" ); return VLC_EGENERIC; } break; /* DVB-C */ case FE_QAM: if( FrontendSetQAM( p_access, p_sys ) ) { msg_Err( p_access, "DVB-C tuning error" ); return VLC_EGENERIC; } break; /* DVB-T */ case FE_OFDM: if( FrontendSetOFDM( p_access, p_sys ) ) { msg_Err( p_access, "DVB-T tuning error" ); return VLC_EGENERIC; } break; /* ATSC */ case FE_ATSC: if( FrontendSetATSC( p_access, p_sys ) ) { msg_Err( p_access, "ATSC tuning error" ); return VLC_EGENERIC; } break; default: msg_Err( p_access, "tuner type %s not supported", p_sys->p_frontend->info.name ); return VLC_EGENERIC; } p_sys->p_frontend->i_last_status = 0; return VLC_SUCCESS; } /***************************************************************************** * FrontendPoll : Poll for frontend events *****************************************************************************/ void FrontendPoll( vlc_object_t *p_access, dvb_sys_t *p_sys ) { frontend_t * p_frontend = p_sys->p_frontend; struct dvb_frontend_event event; fe_status_t i_status, i_diff; for( ;; ) { if( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 ) { if( errno != EWOULDBLOCK ) msg_Err( p_access, "frontend event error: %s", vlc_strerror_c(errno) ); return; } i_status = event.status; i_diff = i_status ^ p_frontend->i_last_status; p_frontend->i_last_status = i_status; { #define IF_UP( x ) \ } \ if ( i_diff & (x) ) \ { \ if ( i_status & (x) ) IF_UP( FE_HAS_SIGNAL ) msg_Dbg( p_access, "frontend has acquired signal" ); else msg_Dbg( p_access, "frontend has lost signal" ); IF_UP( FE_HAS_CARRIER ) msg_Dbg( p_access, "frontend has acquired carrier" ); else msg_Dbg( p_access, "frontend has lost carrier" ); IF_UP( FE_HAS_VITERBI ) msg_Dbg( p_access, "frontend has acquired stable FEC" ); else msg_Dbg( p_access, "frontend has lost FEC" ); IF_UP( FE_HAS_SYNC ) msg_Dbg( p_access, "frontend has acquired sync" ); else msg_Dbg( p_access, "frontend has lost sync" ); IF_UP( FE_HAS_LOCK ) { frontend_statistic_t stat; msg_Dbg( p_access, "frontend has acquired lock" ); /* Read some statistics */ if( !FrontendGetStatistic( p_sys, &stat ) ) { if( stat.i_ber >= 0 ) msg_Dbg( p_access, "- Bit error rate: %d", stat.i_ber ); if( stat.i_signal_strenth >= 0 ) msg_Dbg( p_access, "- Signal strength: %d", stat.i_signal_strenth ); if( stat.i_snr >= 0 ) msg_Dbg( p_access, "- SNR: %d", stat.i_snr ); } } else { msg_Dbg( p_access, "frontend has lost lock" ); } IF_UP( FE_REINIT ) { /* The frontend was reinited. */ msg_Warn( p_access, "reiniting frontend"); FrontendSet( p_access, p_sys ); } } #undef IF_UP } } int FrontendGetStatistic( dvb_sys_t *p_sys, frontend_statistic_t *p_stat ) { frontend_t * p_frontend = p_sys->p_frontend; if( (p_frontend->i_last_status & FE_HAS_LOCK) == 0 ) return VLC_EGENERIC; memset( p_stat, 0, sizeof(*p_stat) ); if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &p_stat->i_ber ) < 0 ) p_stat->i_ber = -1; if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &p_stat->i_signal_strenth ) < 0 ) p_stat->i_signal_strenth = -1; if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &p_stat->i_snr ) < 0 ) p_stat->i_snr = -1; return VLC_SUCCESS; } void FrontendGetStatus( dvb_sys_t *p_sys, frontend_status_t *p_status ) { frontend_t * p_frontend = p_sys->p_frontend; p_status->b_has_signal = (p_frontend->i_last_status & FE_HAS_SIGNAL) != 0; p_status->b_has_carrier = (p_frontend->i_last_status & FE_HAS_CARRIER) != 0; p_status->b_has_lock = (p_frontend->i_last_status & FE_HAS_LOCK) != 0; } static int ScanParametersDvbS( vlc_object_t *p_access, dvb_sys_t *p_sys, scan_parameter_t *p_scan ) { const frontend_t *p_frontend = p_sys->p_frontend; p_scan->type = SCAN_DVB_S; p_scan->frequency.i_min = p_frontend->info.frequency_min; p_scan->frequency.i_max = p_frontend->info.frequency_max; /* set satellite config file path */ char *psz_name = var_InheritString( p_access, "dvb-satellite" ); if( psz_name ) { char *data_dir = config_GetDataDir(); if( !data_dir || -1 == asprintf( &p_scan->psz_scanlist_file, "%s" DIR_SEP "dvb" DIR_SEP "dvb-s" DIR_SEP "%s", data_dir, psz_name ) ) { p_scan->psz_scanlist_file = NULL; } p_scan->scanlist_format = FORMAT_DVBv3; free( data_dir ); free( psz_name ); } return VLC_SUCCESS; } static int ScanParametersDvbC( vlc_object_t *p_access, dvb_sys_t *p_sys, scan_parameter_t *p_scan ) { const frontend_t *p_frontend = p_sys->p_frontend; p_scan->type = SCAN_DVB_C; p_scan->b_exhaustive = false; /* */ p_scan->frequency.i_min = p_frontend->info.frequency_min; p_scan->frequency.i_max = p_frontend->info.frequency_max; p_scan->frequency.i_step = p_frontend->info.frequency_stepsize; /* if frontend can do auto, don't scan them */ p_scan->b_modulation_set = ( p_frontend->info.caps & FE_CAN_QAM_AUTO ); /* our scanning code flips modulation from 16..256 automatically*/ /* if user supplies symbolrate, don't scan those */ p_scan->i_symbolrate = var_GetInteger( p_access, "dvb-srate" ); return VLC_SUCCESS; } static int ScanParametersDvbT( vlc_object_t *p_access, dvb_sys_t *p_sys, scan_parameter_t *p_scan ) { const frontend_t *p_frontend = p_sys->p_frontend; p_scan->type = SCAN_DVB_T; p_scan->b_exhaustive = false; p_scan->psz_scanlist_file = var_InheritString( p_access, "dvb-scanlist" ); p_scan->scanlist_format = FORMAT_DVBv5; /* */ p_scan->frequency.i_min = p_frontend->info.frequency_min; p_scan->frequency.i_max = p_frontend->info.frequency_max; p_scan->frequency.i_step = p_frontend->info.frequency_stepsize; return VLC_SUCCESS; } int FrontendFillScanParameter( vlc_object_t *p_access, dvb_sys_t *p_sys, scan_parameter_t *p_scan ) { const frontend_t *p_frontend = p_sys->p_frontend; if( p_frontend->info.type == FE_OFDM ) /* DVB-T */ return ScanParametersDvbT( p_access, p_sys, p_scan ); else if( p_frontend->info.type == FE_QAM ) /* DVB-C */ return ScanParametersDvbC( p_access, p_sys, p_scan ); else if( p_frontend->info.type == FE_QPSK ) return ScanParametersDvbS( p_access, p_sys, p_scan ); /* DVB-S */ msg_Err( p_access, "frontend scanning not supported" ); return VLC_EGENERIC; } /***************************************************************************** * FrontendInfo : Return information about given frontend *****************************************************************************/ static int FrontendInfo( vlc_object_t *p_access, dvb_sys_t *p_sys ) { frontend_t *p_frontend = p_sys->p_frontend; /* Determine type of frontend */ if( ioctl( p_sys->i_frontend_handle, FE_GET_INFO, &p_frontend->info ) < 0 ) { msg_Err( p_access, "frontend info request error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } /* Print out frontend capabilities. */ msg_Dbg(p_access, "Frontend Info:" ); msg_Dbg(p_access, " name = %s", p_frontend->info.name ); switch( p_frontend->info.type ) { case FE_QPSK: msg_Dbg( p_access, " type = QPSK (DVB-S)" ); break; case FE_QAM: msg_Dbg( p_access, " type = QAM (DVB-C)" ); break; case FE_OFDM: msg_Dbg( p_access, " type = OFDM (DVB-T)" ); break; case FE_ATSC: msg_Dbg( p_access, " type = ATSC (USA)" ); break; #if 0 /* DVB_API_VERSION == 3 */ case FE_MEMORY: msg_Dbg( p_access, " type = MEMORY" ); break; case FE_NET: msg_Dbg( p_access, " type = NETWORK" ); break; #endif default: msg_Err( p_access, " unknown frontend type (%d)", p_frontend->info.type ); return VLC_EGENERIC; } msg_Dbg(p_access, " frequency_min = %u (kHz)", p_frontend->info.frequency_min); msg_Dbg(p_access, " frequency_max = %u (kHz)", p_frontend->info.frequency_max); msg_Dbg(p_access, " frequency_stepsize = %u", p_frontend->info.frequency_stepsize); msg_Dbg(p_access, " frequency_tolerance = %u", p_frontend->info.frequency_tolerance); msg_Dbg(p_access, " symbol_rate_min = %u (kHz)", p_frontend->info.symbol_rate_min); msg_Dbg(p_access, " symbol_rate_max = %u (kHz)", p_frontend->info.symbol_rate_max); msg_Dbg(p_access, " symbol_rate_tolerance (ppm) = %u", p_frontend->info.symbol_rate_tolerance); msg_Dbg(p_access, " notifier_delay (ms) = %u", p_frontend->info.notifier_delay ); msg_Dbg(p_access, "Frontend Info capability list:"); if( p_frontend->info.caps == FE_IS_STUPID) msg_Dbg(p_access, " no capabilities - frontend is stupid!"); if( p_frontend->info.caps & FE_CAN_INVERSION_AUTO) msg_Dbg(p_access, " inversion auto"); if( p_frontend->info.caps & FE_CAN_FEC_1_2) msg_Dbg(p_access, " forward error correction 1/2"); if( p_frontend->info.caps & FE_CAN_FEC_2_3) msg_Dbg(p_access, " forward error correction 2/3"); if( p_frontend->info.caps & FE_CAN_FEC_3_4) msg_Dbg(p_access, " forward error correction 3/4"); if( p_frontend->info.caps & FE_CAN_FEC_4_5) msg_Dbg(p_access, " forward error correction 4/5"); if( p_frontend->info.caps & FE_CAN_FEC_5_6) msg_Dbg(p_access, " forward error correction 5/6"); if( p_frontend->info.caps & FE_CAN_FEC_6_7) msg_Dbg(p_access, " forward error correction 6/7"); if( p_frontend->info.caps & FE_CAN_FEC_7_8) msg_Dbg(p_access, " forward error correction 7/8"); if( p_frontend->info.caps & FE_CAN_FEC_8_9) msg_Dbg(p_access, " forward error correction 8/9"); if( p_frontend->info.caps & FE_CAN_FEC_AUTO) msg_Dbg(p_access, " forward error correction auto"); if( p_frontend->info.caps & FE_CAN_QPSK) msg_Dbg(p_access, " QPSK modulation"); if( p_frontend->info.caps & FE_CAN_QAM_16) msg_Dbg(p_access, " QAM 16 modulation"); if( p_frontend->info.caps & FE_CAN_QAM_32) msg_Dbg(p_access, " QAM 32 modulation"); if( p_frontend->info.caps & FE_CAN_QAM_64) msg_Dbg(p_access, " QAM 64 modulation"); if( p_frontend->info.caps & FE_CAN_QAM_128) msg_Dbg(p_access, " QAM 128 modulation"); if( p_frontend->info.caps & FE_CAN_QAM_256) msg_Dbg(p_access, " QAM 256 modulation"); if( p_frontend->info.caps & FE_CAN_QAM_AUTO) msg_Dbg(p_access, " QAM auto modulation"); if( p_frontend->info.caps & FE_CAN_TRANSMISSION_MODE_AUTO) msg_Dbg(p_access, " transmission mode auto"); if( p_frontend->info.caps & FE_CAN_BANDWIDTH_AUTO) msg_Dbg(p_access, " bandwidth mode auto"); if( p_frontend->info.caps & FE_CAN_GUARD_INTERVAL_AUTO) msg_Dbg(p_access, " guard interval mode auto"); if( p_frontend->info.caps & FE_CAN_HIERARCHY_AUTO) msg_Dbg(p_access, " hierarchy mode auto"); if( p_frontend->info.caps & FE_CAN_8VSB) msg_Dbg(p_access, " 8-level VSB modulation"); if( p_frontend->info.caps & FE_CAN_16VSB) msg_Dbg(p_access, " 16-level VSB modulation"); if( p_frontend->info.caps & FE_HAS_EXTENDED_CAPS) msg_Dbg(p_access, " extended capabilities"); /* 3 capabilities that don't exist yet HERE */ #if (DVB_API_VERSION > 5) \ || ((DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 2)) if( p_frontend->info.caps & FE_CAN_TURBO_FEC) msg_Dbg(p_access, " Turbo FEC modulation"); #else # warning Please update your Linux kernel headers! #endif if( p_frontend->info.caps & FE_CAN_2G_MODULATION) msg_Dbg(p_access, " 2nd generation modulation (DVB-S2)"); /* FE_NEEDS_BENDING is deprecated */ if( p_frontend->info.caps & FE_CAN_RECOVER) msg_Dbg(p_access, " cable unplug recovery"); if( p_frontend->info.caps & FE_CAN_MUTE_TS) msg_Dbg(p_access, " spurious TS muting"); msg_Dbg(p_access, "End of capability list"); return VLC_SUCCESS; } /***************************************************************************** * Decoding the DVB parameters (common) *****************************************************************************/ static fe_spectral_inversion_t DecodeInversion( vlc_object_t *p_access ) { int i_val; fe_spectral_inversion_t fe_inversion = 0; i_val = var_GetInteger( p_access, "dvb-inversion" ); msg_Dbg( p_access, "using inversion=%d", i_val ); switch( i_val ) { case 0: fe_inversion = INVERSION_OFF; break; case 1: fe_inversion = INVERSION_ON; break; case 2: fe_inversion = INVERSION_AUTO; break; default: msg_Dbg( p_access, "dvb has inversion not set, using auto"); fe_inversion = INVERSION_AUTO; break; } return fe_inversion; } /***************************************************************************** * FrontendSetQPSK : controls the FE device *****************************************************************************/ static fe_sec_voltage_t DecodeVoltage( vlc_object_t *p_access ) { switch( var_GetInteger( p_access, "dvb-voltage" ) ) { case 0: return SEC_VOLTAGE_OFF; case 13: return SEC_VOLTAGE_13; case 18: return SEC_VOLTAGE_18; default: return SEC_VOLTAGE_OFF; } } static fe_sec_tone_mode_t DecodeTone( vlc_object_t *p_access ) { switch( var_GetInteger( p_access, "dvb-tone" ) ) { case 0: return SEC_TONE_OFF; case 1: return SEC_TONE_ON; default: return SEC_TONE_OFF; } } struct diseqc_cmd_t { struct dvb_diseqc_master_cmd cmd; uint32_t wait; }; static int DoDiseqc( vlc_object_t *p_access, dvb_sys_t *p_sys ) { int i_val; bool b_val; int i_frequency, i_lnb_slof; fe_sec_voltage_t fe_voltage; fe_sec_tone_mode_t fe_tone; i_frequency = var_GetInteger( p_access, "dvb-frequency" ); i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" ); i_val = var_GetInteger( p_access, "dvb-tone" ); if( i_val == -1 /* auto */ ) { if( i_frequency >= i_lnb_slof ) i_val = 1; else i_val = 0; var_SetInteger( p_access, "dvb-tone", i_val ); } fe_voltage = DecodeVoltage( p_access ); fe_tone = DecodeTone( p_access ); /* Switch off continuous tone. */ if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, SEC_TONE_OFF ) < 0 ) { msg_Err( p_access, "switching tone %s error: %s", "off", vlc_strerror_c(errno) ); return VLC_EGENERIC; } /* Configure LNB voltage. */ if( ioctl( p_sys->i_frontend_handle, FE_SET_VOLTAGE, fe_voltage ) < 0 ) { msg_Err( p_access, "voltage error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } b_val = var_GetBool( p_access, "dvb-high-voltage" ); if( ioctl( p_sys->i_frontend_handle, FE_ENABLE_HIGH_LNB_VOLTAGE, b_val ) < 0 && b_val ) { msg_Err( p_access, "high LNB voltage error: %s", vlc_strerror_c(errno) ); } /* Wait for at least 15 ms. */ msleep(15000); i_val = var_GetInteger( p_access, "dvb-satno" ); if( i_val > 0 && i_val < 5 ) { /* digital satellite equipment control, * specification is available from http://www.eutelsat.com/ */ /* 1.x compatible equipment */ struct diseqc_cmd_t cmd = { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 }; /* param: high nibble: reset bits, low nibble set bits, * bits are: option, position, polarization, band */ cmd.cmd.msg[3] = 0xf0 /* reset bits */ | (((i_val - 1) * 4) & 0xc) | (fe_voltage == SEC_VOLTAGE_13 ? 0 : 2) | (fe_tone == SEC_TONE_ON ? 1 : 0); if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_MASTER_CMD, &cmd.cmd ) ) { msg_Err( p_access, "master command sending error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } msleep(15000 + cmd.wait * 1000); /* A or B simple diseqc ("diseqc-compatible") */ if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_BURST, ((i_val - 1) % 2) ? SEC_MINI_B : SEC_MINI_A ) ) { msg_Err( p_access, "burst sending error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } msleep(15000); } if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, fe_tone ) ) { msg_Err( p_access, "switching tone %s error: %s", (fe_tone == SEC_TONE_ON) ? "on" : "off", vlc_strerror_c(errno) ); return VLC_EGENERIC; } msleep(50000); return 0; } static int FrontendSetQPSK( vlc_object_t *p_access, dvb_sys_t *p_sys ) { struct dvb_frontend_parameters fep; int i_val; int i_frequency, i_lnb_slof = 0, i_lnb_lof1, i_lnb_lof2 = 0; /* Prepare the fep structure */ i_frequency = var_GetInteger( p_access, "dvb-frequency" ); i_val = var_GetInteger( p_access, "dvb-lnb-lof1" ); if( i_val == 0 ) { /* Automatic mode. */ if ( i_frequency >= 950000 && i_frequency <= 2150000 ) { msg_Dbg( p_access, "frequency %d is in IF-band", i_frequency ); i_lnb_lof1 = 0; } else if ( i_frequency >= 2500000 && i_frequency <= 2700000 ) { msg_Dbg( p_access, "frequency %d is in S-band", i_frequency ); i_lnb_lof1 = 3650000; } else if ( i_frequency >= 3400000 && i_frequency <= 4200000 ) { msg_Dbg( p_access, "frequency %d is in C-band (lower)", i_frequency ); i_lnb_lof1 = 5150000; } else if ( i_frequency >= 4500000 && i_frequency <= 4800000 ) { msg_Dbg( p_access, "frequency %d is in C-band (higher)", i_frequency ); i_lnb_lof1 = 5950000; } else if ( i_frequency >= 10700000 && i_frequency <= 13250000 ) { msg_Dbg( p_access, "frequency %d is in Ku-band", i_frequency ); i_lnb_lof1 = 9750000; i_lnb_lof2 = 10600000; i_lnb_slof = 11700000; } else { msg_Err( p_access, "frequency %d is out of any known band", i_frequency ); msg_Err( p_access, "specify dvb-lnb-lof1 manually for the local " "oscillator frequency" ); return VLC_EGENERIC; } var_SetInteger( p_access, "dvb-lnb-lof1", i_lnb_lof1 ); var_SetInteger( p_access, "dvb-lnb-lof2", i_lnb_lof2 ); var_SetInteger( p_access, "dvb-lnb-slof", i_lnb_slof ); } else { i_lnb_lof1 = i_val; i_lnb_lof2 = var_GetInteger( p_access, "dvb-lnb-lof2" ); i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" ); } if( i_lnb_slof && i_frequency >= i_lnb_slof ) { i_frequency -= i_lnb_lof2; } else { i_frequency -= i_lnb_lof1; } fep.frequency = i_frequency >= 0 ? i_frequency : -i_frequency; fep.inversion = DecodeInversion( p_access ); fep.u.qpsk.symbol_rate = var_GetInteger( p_access, "dvb-srate" ); fep.u.qpsk.fec_inner = FEC_NONE; if( DoDiseqc( p_access, p_sys ) < 0 ) { return VLC_EGENERIC; } /* Empty the event queue */ for( ; ; ) { struct dvb_frontend_event event; if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 && errno == EWOULDBLOCK ) break; } /* Now send it all to the frontend device */ if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 ) { msg_Err( p_access, "frontend error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * FrontendSetQAM : controls the FE device *****************************************************************************/ static int FrontendSetQAM( vlc_object_t *p_access, dvb_sys_t *p_sys ) { frontend_t *p_frontend = p_sys->p_frontend; struct dvb_frontend_parameters fep; unsigned int i_val; /* Prepare the fep structure */ fep.frequency = var_GetInteger( p_access, "dvb-frequency" ); fep.inversion = DecodeInversion( p_access ); /* Default symbol-rate is for dvb-s, and doesn't fit * for dvb-c, so if it's over the limit of frontend, default to * somewhat common value */ i_val = var_GetInteger( p_access, "dvb-srate" ); if( i_val < p_frontend->info.symbol_rate_max && i_val > p_frontend->info.symbol_rate_min ) fep.u.qam.symbol_rate = i_val; fep.u.qam.fec_inner = FEC_NONE; fep.u.qam.modulation = QAM_AUTO; /* Empty the event queue */ for( ; ; ) { struct dvb_frontend_event event; if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 && errno == EWOULDBLOCK ) break; } /* Now send it all to the frontend device */ if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 ) { msg_Err( p_access, "frontend error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * FrontendSetOFDM : controls the FE device *****************************************************************************/ static fe_bandwidth_t DecodeBandwidth( vlc_object_t *p_access ) { fe_bandwidth_t fe_bandwidth = 0; int i_bandwidth = var_GetInteger( p_access, "dvb-bandwidth" ); msg_Dbg( p_access, "using bandwidth=%d", i_bandwidth ); switch( i_bandwidth ) { case 0: fe_bandwidth = BANDWIDTH_AUTO; break; case 6: fe_bandwidth = BANDWIDTH_6_MHZ; break; case 7: fe_bandwidth = BANDWIDTH_7_MHZ; break; case 8: fe_bandwidth = BANDWIDTH_8_MHZ; break; default: msg_Dbg( p_access, "terrestrial dvb has bandwidth not set, using auto" ); fe_bandwidth = BANDWIDTH_AUTO; break; } return fe_bandwidth; } static fe_transmit_mode_t DecodeTransmission( vlc_object_t *p_access ) { fe_transmit_mode_t fe_transmission = 0; int i_transmission = var_GetInteger( p_access, "dvb-transmission" ); msg_Dbg( p_access, "using transmission=%d", i_transmission ); switch( i_transmission ) { case 0: fe_transmission = TRANSMISSION_MODE_AUTO; break; case 2: fe_transmission = TRANSMISSION_MODE_2K; break; case 8: fe_transmission = TRANSMISSION_MODE_8K; break; default: msg_Dbg( p_access, "terrestrial dvb has transmission mode not set, using auto"); fe_transmission = TRANSMISSION_MODE_AUTO; break; } return fe_transmission; } static fe_hierarchy_t DecodeHierarchy( vlc_object_t *p_access ) { fe_hierarchy_t fe_hierarchy = 0; int i_hierarchy = var_GetInteger( p_access, "dvb-hierarchy" ); msg_Dbg( p_access, "using hierarchy=%d", i_hierarchy ); switch( i_hierarchy ) { case -1: fe_hierarchy = HIERARCHY_NONE; break; case 0: fe_hierarchy = HIERARCHY_AUTO; break; case 1: fe_hierarchy = HIERARCHY_1; break; case 2: fe_hierarchy = HIERARCHY_2; break; case 4: fe_hierarchy = HIERARCHY_4; break; default: msg_Dbg( p_access, "terrestrial dvb has hierarchy not set, using auto"); fe_hierarchy = HIERARCHY_AUTO; break; } return fe_hierarchy; } static int FrontendSetOFDM( vlc_object_t *p_access, dvb_sys_t *p_sys ) { struct dvb_frontend_parameters fep; /* Prepare the fep structure */ fep.frequency = var_GetInteger( p_access, "dvb-frequency" ); fep.inversion = DecodeInversion( p_access ); fep.u.ofdm.bandwidth = DecodeBandwidth( p_access ); fep.u.ofdm.code_rate_HP = FEC_NONE; fep.u.ofdm.code_rate_LP = FEC_NONE; fep.u.ofdm.constellation = QAM_AUTO; fep.u.ofdm.transmission_mode = DecodeTransmission( p_access ); fep.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; fep.u.ofdm.hierarchy_information = DecodeHierarchy( p_access ); /* Empty the event queue */ for( ; ; ) { struct dvb_frontend_event event; if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 && errno == EWOULDBLOCK ) break; } /* Now send it all to the frontend device */ if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 ) { msg_Err( p_access, "frontend error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * FrontendSetATSC : controls the FE device *****************************************************************************/ static int FrontendSetATSC( vlc_object_t *p_access, dvb_sys_t *p_sys ) { struct dvb_frontend_parameters fep; /* Prepare the fep structure */ fep.frequency = var_GetInteger( p_access, "dvb-frequency" ); fep.u.vsb.modulation = VSB_8; /* Empty the event queue */ for( ; ; ) { struct dvb_frontend_event event; if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 && errno == EWOULDBLOCK ) break; } /* Now send it all to the frontend device */ if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 ) { msg_Err( p_access, "frontend error: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; } /* * Demux */ /***************************************************************************** * DMXSetFilter : controls the demux to add a filter *****************************************************************************/ int DMXSetFilter( vlc_object_t *p_access, int i_pid, int * pi_fd, int i_type ) { struct dmx_pes_filter_params s_filter_params; unsigned int i_adapter, i_device; char dmx[128]; i_adapter = var_GetInteger( p_access, "dvb-adapter" ); i_device = var_GetInteger( p_access, "dvb-device" ); if( snprintf( dmx, sizeof(dmx), DMX, i_adapter, i_device ) >= (int)sizeof(dmx) ) { msg_Err( p_access, "snprintf() truncated string for DMX" ); dmx[sizeof(dmx) - 1] = '\0'; } msg_Dbg( p_access, "Opening device %s", dmx ); if( (*pi_fd = vlc_open(dmx, O_RDWR)) < 0 ) { msg_Err( p_access, "DMXSetFilter: opening device failed: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } /* We fill the DEMUX structure : */ s_filter_params.pid = i_pid; s_filter_params.input = DMX_IN_FRONTEND; s_filter_params.output = DMX_OUT_TS_TAP; s_filter_params.flags = DMX_IMMEDIATE_START; switch ( i_type ) { /* First device */ case 1: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO0 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_VIDEO0; break; case 2: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO0 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_AUDIO0; break; case 3: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT0 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_TELETEXT0; break; case 4: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE0 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_SUBTITLE0; break; case 5: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR0 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_PCR0; break; /* Second device */ case 6: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO1 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_VIDEO1; break; case 7: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO1 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_AUDIO1; break; case 8: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT1 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_TELETEXT1; break; case 9: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE1 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_SUBTITLE1; break; case 10: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR1 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_PCR1; break; /* Third device */ case 11: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO2 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_VIDEO2; break; case 12: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO2 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_AUDIO2; break; case 13: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT2 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_TELETEXT2; break; case 14: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE2 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_SUBTITLE2; break; case 15: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR2 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_PCR2; break; /* Forth device */ case 16: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO3 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_VIDEO3; break; case 17: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO3 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_AUDIO3; break; case 18: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT3 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_TELETEXT3; break; case 19: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE3 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_SUBTITLE3; break; case 20: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR3 for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_PCR3; break; /* Usually used by Nova cards */ case 21: default: msg_Dbg(p_access, "DMXSetFilter: DMX_PES_OTHER for PID %d", i_pid); s_filter_params.pes_type = DMX_PES_OTHER; break; } /* We then give the order to the device : */ if( ioctl( *pi_fd, DMX_SET_PES_FILTER, &s_filter_params ) ) { msg_Err( p_access, "setting demux PES filter failed: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * DMXUnsetFilter : removes a filter *****************************************************************************/ int DMXUnsetFilter( vlc_object_t *p_access, int i_fd ) { if( ioctl( i_fd, DMX_STOP ) < 0 ) { msg_Err( p_access, "stopping demux failed: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } msg_Dbg( p_access, "DMXUnsetFilter: closing demux %d", i_fd ); vlc_close( i_fd ); return VLC_SUCCESS; } /* * DVR device */ /***************************************************************************** * DVROpen : *****************************************************************************/ int DVROpen( vlc_object_t *p_access, dvb_sys_t *p_sys ) { unsigned int i_adapter, i_device; char dvr[128]; i_adapter = var_GetInteger( p_access, "dvb-adapter" ); i_device = var_GetInteger( p_access, "dvb-device" ); if( snprintf( dvr, sizeof(dvr), DVR, i_adapter, i_device ) >= (int)sizeof(dvr) ) { msg_Err( p_access, "snprintf() truncated string for DVR" ); dvr[sizeof(dvr) - 1] = '\0'; } msg_Dbg( p_access, "Opening device %s", dvr ); if( (p_sys->i_handle = vlc_open(dvr, O_RDONLY)) < 0 ) { msg_Err( p_access, "DVROpen: opening device failed: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } if( fcntl( p_sys->i_handle, F_SETFL, O_NONBLOCK ) == -1 ) { msg_Warn( p_access, "DVROpen: couldn't set non-blocking mode: %s", vlc_strerror_c(errno) ); } return VLC_SUCCESS; } /***************************************************************************** * DVRClose : *****************************************************************************/ void DVRClose( vlc_object_t *p_access, dvb_sys_t *p_sys ) { VLC_UNUSED(p_access); vlc_close( p_sys->i_handle ); }