Permalink
| /***************************************************************************** | |
| * dvb.c: linux-dvb input for DVBlast | |
| ***************************************************************************** | |
| * Copyright (C) 2008-2010, 2015 VideoLAN | |
| * | |
| * Authors: Christophe Massiot <massiot@via.ecp.fr> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 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 General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. | |
| *****************************************************************************/ | |
| #include "config.h" | |
| #ifdef HAVE_DVB_SUPPORT | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <stdbool.h> | |
| #include <stdio.h> | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include <sys/ioctl.h> | |
| #include <sys/socket.h> | |
| #include <sys/uio.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <poll.h> | |
| #include <errno.h> | |
| /* DVB Card Drivers */ | |
| #include <linux/dvb/version.h> | |
| #include <linux/dvb/dmx.h> | |
| #include <linux/dvb/frontend.h> | |
| #include <linux/dvb/ca.h> | |
| #include <ev.h> | |
| #if DVBAPI_VERSION < 508 | |
| #define DTV_STREAM_ID 42 | |
| #define FE_CAN_MULTISTREAM 0x4000000 | |
| #endif | |
| #define MAX_DELIVERY_SYSTEMS 20 | |
| #include "dvblast.h" | |
| #include "en50221.h" | |
| #include "comm.h" | |
| #include <bitstream/common.h> | |
| /***************************************************************************** | |
| * Local declarations | |
| *****************************************************************************/ | |
| #define DVR_READ_TIMEOUT 30000000 /* 30 s */ | |
| #define MAX_READ_ONCE 50 | |
| #define DVR_BUFFER_SIZE 40*188*1024 /* bytes */ | |
| int i_dvr_buffer_size = DVR_BUFFER_SIZE; | |
| static int i_frontend, i_dvr; | |
| static struct ev_io frontend_watcher, dvr_watcher; | |
| static struct ev_timer lock_watcher, mute_watcher, print_watcher; | |
| static fe_status_t i_last_status; | |
| static block_t *p_freelist = NULL; | |
| /***************************************************************************** | |
| * Local prototypes | |
| *****************************************************************************/ | |
| static void DVRRead(struct ev_loop *loop, struct ev_io *w, int revents); | |
| static void DVRMuteCb(struct ev_loop *loop, struct ev_timer *w, int revents); | |
| static void FrontendRead(struct ev_loop *loop, struct ev_io *w, int revents); | |
| static void FrontendLockCb(struct ev_loop *loop, struct ev_timer *w, int revents); | |
| static void FrontendSet( bool b_reset ); | |
| /***************************************************************************** | |
| * dvb_Open | |
| *****************************************************************************/ | |
| void dvb_Open( void ) | |
| { | |
| char psz_tmp[128]; | |
| msg_Dbg( NULL, "compiled with DVB API version %d.%d", DVB_API_VERSION, DVB_API_VERSION_MINOR ); | |
| if ( i_frequency ) | |
| { | |
| sprintf( psz_tmp, "/dev/dvb/adapter%d/frontend%d", i_adapter, i_fenum ); | |
| if( (i_frontend = open(psz_tmp, O_RDWR | O_NONBLOCK)) < 0 ) | |
| { | |
| msg_Err( NULL, "opening device %s failed (%s)", psz_tmp, | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| FrontendSet(true); | |
| } | |
| else | |
| { | |
| i_frontend = -1; | |
| } | |
| sprintf( psz_tmp, "/dev/dvb/adapter%d/dvr%d", i_adapter, i_fenum ); | |
| if( (i_dvr = open(psz_tmp, O_RDONLY | O_NONBLOCK)) < 0 ) | |
| { | |
| msg_Err( NULL, "opening device %s failed (%s)", psz_tmp, | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| if ( ioctl( i_dvr, DMX_SET_BUFFER_SIZE, i_dvr_buffer_size ) < 0 ) | |
| { | |
| msg_Warn( NULL, "couldn't set %s buffer size (%s)", psz_tmp, | |
| strerror(errno) ); | |
| } | |
| ev_io_init(&dvr_watcher, DVRRead, i_dvr, EV_READ); | |
| ev_io_start(event_loop, &dvr_watcher); | |
| if ( i_frontend != -1 ) | |
| { | |
| ev_io_init(&frontend_watcher, FrontendRead, i_frontend, EV_READ); | |
| ev_io_start(event_loop, &frontend_watcher); | |
| } | |
| ev_timer_init(&lock_watcher, FrontendLockCb, | |
| i_frontend_timeout_duration / 1000000., | |
| i_frontend_timeout_duration / 1000000.); | |
| ev_timer_init(&mute_watcher, DVRMuteCb, | |
| DVR_READ_TIMEOUT / 1000000., | |
| DVR_READ_TIMEOUT / 1000000.); | |
| en50221_Init(); | |
| } | |
| /***************************************************************************** | |
| * dvb_Reset | |
| *****************************************************************************/ | |
| void dvb_Reset( void ) | |
| { | |
| if ( i_frequency ) | |
| FrontendSet(true); | |
| } | |
| /***************************************************************************** | |
| * DVR events | |
| *****************************************************************************/ | |
| static void DVRRead(struct ev_loop *loop, struct ev_io *w, int revents) | |
| { | |
| int i, i_len; | |
| block_t *p_ts = p_freelist, **pp_current = &p_ts; | |
| struct iovec p_iov[MAX_READ_ONCE]; | |
| for ( i = 0; i < MAX_READ_ONCE; i++ ) | |
| { | |
| if ( (*pp_current) == NULL ) *pp_current = block_New(); | |
| p_iov[i].iov_base = (*pp_current)->p_ts; | |
| p_iov[i].iov_len = TS_SIZE; | |
| pp_current = &(*pp_current)->p_next; | |
| } | |
| if ( (i_len = readv(i_dvr, p_iov, MAX_READ_ONCE)) < 0 ) | |
| { | |
| msg_Err( NULL, "couldn't read from DVR device (%s)", | |
| strerror(errno) ); | |
| i_len = 0; | |
| } | |
| i_len /= TS_SIZE; | |
| if ( i_len ) | |
| ev_timer_again(loop, &mute_watcher); | |
| pp_current = &p_ts; | |
| while ( i_len && *pp_current ) | |
| { | |
| pp_current = &(*pp_current)->p_next; | |
| i_len--; | |
| } | |
| p_freelist = *pp_current; | |
| *pp_current = NULL; | |
| demux_Run( p_ts ); | |
| } | |
| static void DVRMuteCb(struct ev_loop *loop, struct ev_timer *w, int revents) | |
| { | |
| msg_Warn( NULL, "no DVR output, resetting" ); | |
| ev_timer_stop(loop, w); | |
| switch (i_print_type) { | |
| case PRINT_XML: | |
| fprintf(print_fh, "<EVENT type=\"reset\" cause=\"dvr\" />\n"); | |
| break; | |
| case PRINT_TEXT: | |
| fprintf(print_fh, "reset cause: dvr\n"); | |
| break; | |
| default: | |
| break; | |
| } | |
| if ( i_frequency ) | |
| FrontendSet(false); | |
| en50221_Reset(); | |
| } | |
| /* | |
| * Demux | |
| */ | |
| /***************************************************************************** | |
| * dvb_SetFilter : controls the demux to add a filter | |
| *****************************************************************************/ | |
| int dvb_SetFilter( uint16_t i_pid ) | |
| { | |
| struct dmx_pes_filter_params s_filter_params; | |
| char psz_tmp[128]; | |
| int i_fd; | |
| sprintf( psz_tmp, "/dev/dvb/adapter%d/demux%d", i_adapter, i_fenum ); | |
| if( (i_fd = open(psz_tmp, O_RDWR)) < 0 ) | |
| { | |
| msg_Err( NULL, "DMXSetFilter: opening device failed (%s)", | |
| strerror(errno) ); | |
| return -1; | |
| } | |
| 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; | |
| s_filter_params.pes_type = DMX_PES_OTHER; | |
| if ( ioctl( i_fd, DMX_SET_PES_FILTER, &s_filter_params ) < 0 ) | |
| { | |
| msg_Err( NULL, "failed setting filter on %d (%s)", i_pid, | |
| strerror(errno) ); | |
| close( i_fd ); | |
| return -1; | |
| } | |
| msg_Dbg( NULL, "setting filter on PID %d", i_pid ); | |
| return i_fd; | |
| } | |
| /***************************************************************************** | |
| * dvb_UnsetFilter : removes a filter | |
| *****************************************************************************/ | |
| void dvb_UnsetFilter( int i_fd, uint16_t i_pid ) | |
| { | |
| if ( ioctl( i_fd, DMX_STOP ) < 0 ) | |
| msg_Err( NULL, "DMX_STOP failed (%s)", strerror(errno) ); | |
| else | |
| msg_Dbg( NULL, "unsetting filter on PID %d", i_pid ); | |
| close( i_fd ); | |
| } | |
| /* | |
| * Frontend | |
| */ | |
| /***************************************************************************** | |
| * Print info | |
| *****************************************************************************/ | |
| static void PrintCb( struct ev_loop *loop, struct ev_timer *w, int revents ) | |
| { | |
| uint32_t i_ber = 0; | |
| uint16_t i_strength = 0, i_snr = 0; | |
| uint32_t i_uncorrected = 0; | |
| ioctl(i_frontend, FE_READ_BER, &i_ber); | |
| ioctl(i_frontend, FE_READ_SIGNAL_STRENGTH, &i_strength); | |
| ioctl(i_frontend, FE_READ_SNR, &i_snr); | |
| ioctl(i_frontend, FE_READ_UNCORRECTED_BLOCKS, &i_uncorrected); | |
| switch (i_print_type) | |
| { | |
| case PRINT_XML: | |
| fprintf(print_fh, | |
| "<STATUS type=\"frontend\" ber=\"%"PRIu32"\" strength=\"%"PRIu16"\" snr=\"%"PRIu16"\" uncorrected=\"%"PRIu32"\" />\n", | |
| i_ber, i_strength, i_snr, i_uncorrected); | |
| break; | |
| case PRINT_TEXT: | |
| fprintf(print_fh, "frontend ber: %"PRIu32" strength: %"PRIu16" snr: %"PRIu16" uncorrected: %"PRIu32"\n", | |
| i_ber, i_strength, i_snr, i_uncorrected); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| /***************************************************************************** | |
| * Frontend events | |
| *****************************************************************************/ | |
| static void FrontendRead(struct ev_loop *loop, struct ev_io *w, int revents) | |
| { | |
| struct dvb_frontend_event event; | |
| fe_status_t i_status, i_diff; | |
| for( ;; ) | |
| { | |
| int i_ret = ioctl( i_frontend, FE_GET_EVENT, &event ); | |
| if( i_ret < 0 ) | |
| { | |
| if( errno == EWOULDBLOCK ) | |
| return; /* no more events */ | |
| msg_Err( NULL, "reading frontend event failed (%d) %s", | |
| i_ret, strerror(errno) ); | |
| return; | |
| } | |
| i_status = event.status; | |
| i_diff = i_status ^ i_last_status; | |
| i_last_status = i_status; | |
| { | |
| #define IF_UP( x ) \ | |
| } \ | |
| if ( i_diff & (x) ) \ | |
| { \ | |
| if ( i_status & (x) ) | |
| IF_UP( FE_HAS_SIGNAL ) | |
| msg_Dbg( NULL, "frontend has acquired signal" ); | |
| else | |
| msg_Dbg( NULL, "frontend has lost signal" ); | |
| IF_UP( FE_HAS_CARRIER ) | |
| msg_Dbg( NULL, "frontend has acquired carrier" ); | |
| else | |
| msg_Dbg( NULL, "frontend has lost carrier" ); | |
| IF_UP( FE_HAS_VITERBI ) | |
| msg_Dbg( NULL, "frontend has acquired stable FEC" ); | |
| else | |
| msg_Dbg( NULL, "frontend has lost FEC" ); | |
| IF_UP( FE_HAS_SYNC ) | |
| msg_Dbg( NULL, "frontend has acquired sync" ); | |
| else | |
| msg_Dbg( NULL, "frontend has lost sync" ); | |
| IF_UP( FE_HAS_LOCK ) | |
| { | |
| int32_t i_value = 0; | |
| msg_Info( NULL, "frontend has acquired lock" ); | |
| switch (i_print_type) { | |
| case PRINT_XML: | |
| fprintf(print_fh, "<STATUS type=\"lock\" status=\"1\" />\n"); | |
| break; | |
| case PRINT_TEXT: | |
| fprintf(print_fh, "lock status: 1\n"); | |
| break; | |
| default: | |
| break; | |
| } | |
| ev_timer_stop(loop, &lock_watcher); | |
| ev_timer_again(loop, &mute_watcher); | |
| /* Read some statistics */ | |
| if( ioctl( i_frontend, FE_READ_BER, &i_value ) >= 0 ) | |
| msg_Dbg( NULL, "- Bit error rate: %d", i_value ); | |
| if( ioctl( i_frontend, FE_READ_SIGNAL_STRENGTH, &i_value ) >= 0 ) | |
| msg_Dbg( NULL, "- Signal strength: %d", i_value ); | |
| if( ioctl( i_frontend, FE_READ_SNR, &i_value ) >= 0 ) | |
| msg_Dbg( NULL, "- SNR: %d", i_value ); | |
| if (i_print_period) | |
| { | |
| ev_timer_init( &print_watcher, PrintCb, | |
| i_print_period / 1000000., | |
| i_print_period / 1000000. ); | |
| ev_timer_start( event_loop, &print_watcher ); | |
| } | |
| } | |
| else | |
| { | |
| msg_Dbg( NULL, "frontend has lost lock" ); | |
| switch (i_print_type) { | |
| case PRINT_XML: | |
| fprintf(print_fh, "<STATUS type=\"lock\" status=\"0\"/>\n"); | |
| break; | |
| case PRINT_TEXT: | |
| fprintf(print_fh, "lock status: 0\n"); | |
| break; | |
| default: | |
| break; | |
| } | |
| if (i_frontend_timeout_duration) | |
| { | |
| ev_timer_stop(event_loop, &lock_watcher); | |
| ev_timer_again(loop, &mute_watcher); | |
| } | |
| if (i_print_period) | |
| ev_timer_stop(event_loop, &print_watcher); | |
| } | |
| IF_UP( FE_REINIT ) | |
| { | |
| /* The frontend was reinited. */ | |
| msg_Warn( NULL, "reiniting frontend"); | |
| if ( i_frequency ) | |
| FrontendSet(true); | |
| } | |
| } | |
| #undef IF_UP | |
| } | |
| } | |
| static void FrontendLockCb(struct ev_loop *loop, struct ev_timer *w, int revents) | |
| { | |
| if ( i_quit_timeout_duration ) | |
| { | |
| msg_Err( NULL, "no lock" ); | |
| ev_break(loop, EVBREAK_ALL); | |
| return; | |
| } | |
| msg_Warn( NULL, "no lock, tuning again" ); | |
| ev_timer_stop(loop, w); | |
| switch (i_print_type) { | |
| case PRINT_XML: | |
| fprintf(print_fh, "<EVENT type=\"reset\" cause=\"nolock\" />\n"); | |
| break; | |
| case PRINT_TEXT: | |
| fprintf(print_fh, "reset cause: nolock\n"); | |
| break; | |
| default: | |
| break; | |
| } | |
| if ( i_frequency ) | |
| FrontendSet(false); | |
| } | |
| static int FrontendDoDiseqc(void) | |
| { | |
| fe_sec_voltage_t fe_voltage; | |
| fe_sec_tone_mode_t fe_tone; | |
| int bis_frequency; | |
| switch ( i_voltage ) | |
| { | |
| case 0: fe_voltage = SEC_VOLTAGE_OFF; break; | |
| default: | |
| case 13: fe_voltage = SEC_VOLTAGE_13; break; | |
| case 18: fe_voltage = SEC_VOLTAGE_18; break; | |
| } | |
| fe_tone = b_tone ? SEC_TONE_ON : SEC_TONE_OFF; | |
| /* Automatic mode. */ | |
| if ( i_frequency >= 950000 && i_frequency <= 2150000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in IF-band", i_frequency ); | |
| bis_frequency = i_frequency; | |
| } | |
| else if ( i_frequency >= 2500000 && i_frequency <= 2700000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in S-band", i_frequency ); | |
| bis_frequency = 3650000 - i_frequency; | |
| } | |
| else if ( i_frequency >= 3400000 && i_frequency <= 4200000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in C-band (lower)", i_frequency ); | |
| bis_frequency = 5150000 - i_frequency; | |
| } | |
| else if ( i_frequency >= 4500000 && i_frequency <= 4800000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in C-band (higher)", i_frequency ); | |
| bis_frequency = 5950000 - i_frequency; | |
| } | |
| else if ( i_frequency >= 10700000 && i_frequency < 11700000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in Ku-band (lower)", | |
| i_frequency ); | |
| bis_frequency = i_frequency - 9750000; | |
| } | |
| else if ( i_frequency >= 11700000 && i_frequency <= 13250000 ) | |
| { | |
| msg_Dbg( NULL, "frequency %d is in Ku-band (higher)", | |
| i_frequency ); | |
| bis_frequency = i_frequency - 10600000; | |
| fe_tone = SEC_TONE_ON; | |
| } | |
| else | |
| { | |
| msg_Err( NULL, "frequency %d is out of any known band", | |
| i_frequency ); | |
| exit(1); | |
| } | |
| /* Switch off continuous tone. */ | |
| if ( ioctl( i_frontend, FE_SET_TONE, SEC_TONE_OFF ) < 0 ) | |
| { | |
| msg_Err( NULL, "FE_SET_TONE failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| /* Configure LNB voltage. */ | |
| if ( ioctl( i_frontend, FE_SET_VOLTAGE, fe_voltage ) < 0 ) | |
| { | |
| msg_Err( NULL, "FE_SET_VOLTAGE failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| /* Wait for at least 15 ms. Currently 100 ms because of broken drivers. */ | |
| msleep(100000); | |
| /* Diseqc */ | |
| if ( i_satnum > 0 && i_satnum < 5 ) | |
| { | |
| /* digital satellite equipment control, | |
| * specification is available from http://www.eutelsat.com/ | |
| */ | |
| /* DiSEqC 1.1 */ | |
| struct dvb_diseqc_master_cmd uncmd = | |
| { {0xe0, 0x10, 0x39, 0xf0, 0x00, 0x00}, 4}; | |
| /* DiSEqC 1.0 */ | |
| struct dvb_diseqc_master_cmd cmd = | |
| { {0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}; | |
| cmd.msg[3] = 0xf0 /* reset bits */ | |
| | ((i_satnum - 1) << 2) | |
| | (fe_voltage == SEC_VOLTAGE_13 ? 0 : 2) | |
| | (fe_tone == SEC_TONE_ON ? 1 : 0); | |
| if ( i_uncommitted > 0 && i_uncommitted < 17 ) | |
| { | |
| uncmd.msg[3] = 0xf0 /* reset bits */ | |
| | (i_uncommitted - 1); | |
| if( ioctl( i_frontend, FE_DISEQC_SEND_MASTER_CMD, &uncmd ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_SEND_MASTER_CMD failed (%s)", | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| /* Repeat uncommitted command */ | |
| uncmd.msg[0] = 0xe1; /* framing: master, no reply, repeated TX */ | |
| if( ioctl( i_frontend, FE_DISEQC_SEND_MASTER_CMD, &uncmd ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_SEND_MASTER_CMD failed (%s)", | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| /* Pause 125 ms between uncommitted & committed diseqc commands. */ | |
| msleep(125000); | |
| } | |
| if( ioctl( i_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_SEND_MASTER_CMD failed (%s)", | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| msleep(100000); /* Should be 15 ms. */ | |
| /* Do it again just to be sure. */ | |
| cmd.msg[0] = 0xe1; /* framing: master, no reply, repeated TX */ | |
| if( ioctl( i_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_SEND_MASTER_CMD failed (%s)", | |
| strerror(errno) ); | |
| exit(1); | |
| } | |
| msleep(100000); /* Again, should be 15 ms */ | |
| } | |
| else if ( i_satnum == 0xA || i_satnum == 0xB ) | |
| { | |
| /* A or B simple diseqc ("diseqc-compatible") */ | |
| if( ioctl( i_frontend, FE_DISEQC_SEND_BURST, | |
| i_satnum == 0xB ? SEC_MINI_B : SEC_MINI_A ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_SEND_BURST failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| msleep(100000); /* ... */ | |
| } | |
| if ( ioctl( i_frontend, FE_SET_TONE, fe_tone ) < 0 ) | |
| { | |
| msg_Err( NULL, "FE_SET_TONE failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| msleep(100000); /* ... */ | |
| msg_Dbg( NULL, "configuring LNB to v=%d p=%d satnum=%x uncommitted=%x", | |
| i_voltage, b_tone, i_satnum, i_uncommitted ); | |
| return bis_frequency; | |
| } | |
| #if DVB_API_VERSION >= 5 | |
| #if DVBAPI_VERSION < 505 | |
| #warning Your linux-dvb headers are old, you should consider upgrading your kernel and/or compiling against different kernel headers | |
| #endif | |
| /***************************************************************************** | |
| * Helper functions for S2API | |
| *****************************************************************************/ | |
| static fe_spectral_inversion_t GetInversion(void) | |
| { | |
| switch ( i_inversion ) | |
| { | |
| case 0: return INVERSION_OFF; | |
| case 1: return INVERSION_ON; | |
| default: | |
| msg_Warn( NULL, "invalid inversion %d", i_inversion ); | |
| case -1: return INVERSION_AUTO; | |
| } | |
| } | |
| static fe_code_rate_t GetFEC(fe_caps_t fe_caps, int i_fec_value) | |
| { | |
| #define GET_FEC_INNER(fec, val) \ | |
| if ( (fe_caps & FE_CAN_##fec) && (i_fec_value == val) ) \ | |
| return fec; | |
| GET_FEC_INNER(FEC_AUTO, 999); | |
| GET_FEC_INNER(FEC_AUTO, -1); | |
| if (i_fec_value == 0) | |
| return FEC_NONE; | |
| GET_FEC_INNER(FEC_1_2, 12); | |
| GET_FEC_INNER(FEC_2_3, 23); | |
| GET_FEC_INNER(FEC_3_4, 34); | |
| if (i_fec_value == 35) | |
| return FEC_3_5; | |
| GET_FEC_INNER(FEC_4_5, 45); | |
| GET_FEC_INNER(FEC_5_6, 56); | |
| GET_FEC_INNER(FEC_6_7, 67); | |
| GET_FEC_INNER(FEC_7_8, 78); | |
| GET_FEC_INNER(FEC_8_9, 89); | |
| if (i_fec_value == 910) | |
| return FEC_9_10; | |
| #undef GET_FEC_INNER | |
| msg_Warn(NULL, "invalid FEC %d", i_fec_value ); | |
| return FEC_AUTO; | |
| } | |
| #define GetFECInner(caps) GetFEC(caps, i_fec) | |
| #define GetFECLP(caps) GetFEC(caps, i_fec_lp) | |
| static fe_modulation_t GetModulation(void) | |
| { | |
| #define GET_MODULATION( mod ) \ | |
| if ( !strcasecmp( psz_modulation, #mod ) ) \ | |
| return mod; | |
| GET_MODULATION(QPSK); | |
| GET_MODULATION(QAM_16); | |
| GET_MODULATION(QAM_32); | |
| GET_MODULATION(QAM_64); | |
| GET_MODULATION(QAM_128); | |
| GET_MODULATION(QAM_256); | |
| GET_MODULATION(QAM_AUTO); | |
| GET_MODULATION(VSB_8); | |
| GET_MODULATION(VSB_16); | |
| GET_MODULATION(PSK_8); | |
| GET_MODULATION(APSK_16); | |
| GET_MODULATION(APSK_32); | |
| GET_MODULATION(DQPSK); | |
| #undef GET_MODULATION | |
| msg_Err( NULL, "invalid modulation %s", psz_modulation ); | |
| exit(1); | |
| } | |
| static fe_pilot_t GetPilot(void) | |
| { | |
| switch ( i_pilot ) | |
| { | |
| case 0: return PILOT_OFF; | |
| case 1: return PILOT_ON; | |
| default: | |
| msg_Warn( NULL, "invalid pilot %d", i_pilot ); | |
| case -1: return PILOT_AUTO; | |
| } | |
| } | |
| static fe_rolloff_t GetRollOff(void) | |
| { | |
| switch ( i_rolloff ) | |
| { | |
| case -1: | |
| case 0: return ROLLOFF_AUTO; | |
| case 20: return ROLLOFF_20; | |
| case 25: return ROLLOFF_25; | |
| default: | |
| msg_Warn( NULL, "invalid rolloff %d", i_rolloff ); | |
| case 35: return ROLLOFF_35; | |
| } | |
| } | |
| static fe_guard_interval_t GetGuard(void) | |
| { | |
| switch ( i_guard ) | |
| { | |
| case 32: return GUARD_INTERVAL_1_32; | |
| case 16: return GUARD_INTERVAL_1_16; | |
| case 8: return GUARD_INTERVAL_1_8; | |
| case 4: return GUARD_INTERVAL_1_4; | |
| default: | |
| msg_Warn( NULL, "invalid guard interval %d", i_guard ); | |
| case -1: | |
| case 0: return GUARD_INTERVAL_AUTO; | |
| } | |
| } | |
| static fe_transmit_mode_t GetTransmission(void) | |
| { | |
| switch ( i_transmission ) | |
| { | |
| case 2: return TRANSMISSION_MODE_2K; | |
| case 8: return TRANSMISSION_MODE_8K; | |
| #ifdef TRANSMISSION_MODE_4K | |
| case 4: return TRANSMISSION_MODE_4K; | |
| #endif | |
| default: | |
| msg_Warn( NULL, "invalid tranmission mode %d", i_transmission ); | |
| case -1: | |
| case 0: return TRANSMISSION_MODE_AUTO; | |
| } | |
| } | |
| static fe_hierarchy_t GetHierarchy(void) | |
| { | |
| switch ( i_hierarchy ) | |
| { | |
| case 0: return HIERARCHY_NONE; | |
| case 1: return HIERARCHY_1; | |
| case 2: return HIERARCHY_2; | |
| case 4: return HIERARCHY_4; | |
| default: | |
| msg_Warn( NULL, "invalid intramission mode %d", i_transmission ); | |
| case -1: return HIERARCHY_AUTO; | |
| } | |
| } | |
| /***************************************************************************** | |
| * FrontendInfo : Print frontend info | |
| *****************************************************************************/ | |
| static void FrontendInfo( struct dvb_frontend_info *info, uint32_t version, | |
| fe_delivery_system_t *p_systems, int i_systems ) | |
| { | |
| msg_Dbg( NULL, "using DVB API version %d.%d", version / 256, version % 256 ); | |
| msg_Dbg( NULL, "Frontend \"%s\" supports:", info->name ); | |
| msg_Dbg( NULL, " frequency min: %d, max: %d, stepsize: %d, tolerance: %d", | |
| info->frequency_min, info->frequency_max, | |
| info->frequency_stepsize, info->frequency_tolerance ); | |
| msg_Dbg( NULL, " symbolrate min: %d, max: %d, tolerance: %d", | |
| info->symbol_rate_min, info->symbol_rate_max, info->symbol_rate_tolerance); | |
| msg_Dbg( NULL, " capabilities:" ); | |
| #define FRONTEND_INFO(caps,val,msg) \ | |
| if ( caps & val ) \ | |
| msg_Dbg( NULL, " %s", msg ); | |
| FRONTEND_INFO( info->caps, FE_IS_STUPID, "FE_IS_STUPID" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_INVERSION_AUTO, "INVERSION_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_1_2, "FEC_1_2" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_2_3, "FEC_2_3" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_3_4, "FEC_3_4" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_4_5, "FEC_4_5" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_5_6, "FEC_5_6" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_6_7, "FEC_6_7" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_7_8, "FEC_7_8" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_8_9, "FEC_8_9" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_FEC_AUTO,"FEC_AUTO") | |
| FRONTEND_INFO( info->caps, FE_CAN_QPSK, "QPSK" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_16, "QAM_16" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_32, "QAM_32" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_64, "QAM_64" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_128,"QAM_128") | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_256,"QAM_256") | |
| FRONTEND_INFO( info->caps, FE_CAN_QAM_AUTO,"QAM_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_TRANSMISSION_MODE_AUTO, "TRANSMISSION_MODE_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_BANDWIDTH_AUTO, "BANDWIDTH_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_GUARD_INTERVAL_AUTO, "GUARD_INTERVAL_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_HIERARCHY_AUTO, "HIERARCHY_AUTO" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_8VSB, "8VSB" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_16VSB,"16VSB" ) | |
| FRONTEND_INFO( info->caps, FE_HAS_EXTENDED_CAPS, "EXTENDED_CAPS" ) | |
| #if DVBAPI_VERSION >= 501 | |
| FRONTEND_INFO( info->caps, FE_CAN_2G_MODULATION, "2G_MODULATION" ) | |
| #endif | |
| FRONTEND_INFO( info->caps, FE_CAN_MULTISTREAM, "MULTISTREAM" ) | |
| FRONTEND_INFO( info->caps, FE_NEEDS_BENDING, "NEEDS_BENDING" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_RECOVER, "FE_CAN_RECOVER" ) | |
| FRONTEND_INFO( info->caps, FE_CAN_MUTE_TS, "FE_CAN_MUTE_TS" ) | |
| #undef FRONTEND_INFO | |
| msg_Dbg( NULL, " delivery systems:" ); | |
| int i; | |
| for ( i = 0; i < i_systems; i++ ) | |
| { | |
| switch ( p_systems[i] ) | |
| { | |
| #define DELSYS_INFO(delsys, msg) \ | |
| case delsys: msg_Dbg( NULL, " %s", msg); break; | |
| DELSYS_INFO( SYS_ATSC, "ATSC" ) | |
| DELSYS_INFO( SYS_ATSCMH, "ATSCMH" ) | |
| DELSYS_INFO( SYS_CMMB, "CMBB" ) | |
| DELSYS_INFO( SYS_DAB, "DAB" ) | |
| DELSYS_INFO( SYS_DSS, "DSS" ) | |
| DELSYS_INFO( SYS_DVBC_ANNEX_B, "DVBC_ANNEX_B" ) | |
| DELSYS_INFO( SYS_DVBH, "DVBH" ) | |
| DELSYS_INFO( SYS_DVBS, "DVBS" ) | |
| DELSYS_INFO( SYS_DVBS2, "DVBS2" ) | |
| DELSYS_INFO( SYS_DVBT, "DVBT" ) | |
| DELSYS_INFO( SYS_ISDBC, "ISDBC" ) | |
| DELSYS_INFO( SYS_ISDBS, "ISDBS" ) | |
| DELSYS_INFO( SYS_ISDBT, "ISDBT" ) | |
| DELSYS_INFO( SYS_UNDEFINED, "UNDEFINED" ) | |
| #if DVBAPI_VERSION >= 505 | |
| DELSYS_INFO( SYS_DVBC_ANNEX_A, "DVBC_ANNEX_A" ) | |
| DELSYS_INFO( SYS_DVBC_ANNEX_C, "DVBC_ANNEX_C" ) | |
| DELSYS_INFO( SYS_DVBT2, "DVBT2" ) | |
| DELSYS_INFO( SYS_TURBO, "TURBO" ) | |
| #else | |
| DELSYS_INFO( SYS_DVBC_ANNEX_AC, "DVBC_ANNEX_AC" ) | |
| #endif | |
| #if DVBAPI_VERSION >= 507 | |
| DELSYS_INFO( SYS_DTMB, "DTMB" ) | |
| #else | |
| DELSYS_INFO( SYS_DMBTH, "DMBTH" ) | |
| #endif | |
| default: msg_Dbg( NULL, " Unknown delivery system %u", p_systems[i]); | |
| break; | |
| } | |
| } | |
| } | |
| /***************************************************************************** | |
| * FrontendSet | |
| *****************************************************************************/ | |
| /* S2API */ | |
| #if DVBAPI_VERSION >= 505 | |
| static struct dtv_property info_cmdargs[] = { | |
| { .cmd = DTV_API_VERSION, .u.data = 0 }, | |
| }; | |
| static struct dtv_properties info_cmdseq = { | |
| .num = sizeof(info_cmdargs)/sizeof(struct dtv_property), | |
| .props = info_cmdargs | |
| }; | |
| static struct dtv_property enum_cmdargs[] = { | |
| { .cmd = DTV_ENUM_DELSYS, .u.data = 0 }, | |
| }; | |
| static struct dtv_properties enum_cmdseq = { | |
| .num = sizeof(enum_cmdargs)/sizeof(struct dtv_property), | |
| .props = enum_cmdargs | |
| }; | |
| #endif | |
| static struct dtv_property dvbs_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBS }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = QPSK }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_SYMBOL_RATE, .u.data = 27500000 }, | |
| { .cmd = DTV_INNER_FEC, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties dvbs_cmdseq = { | |
| .num = sizeof(dvbs_cmdargs)/sizeof(struct dtv_property), | |
| .props = dvbs_cmdargs | |
| }; | |
| static struct dtv_property dvbs2_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBS2 }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = PSK_8 }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_SYMBOL_RATE, .u.data = 27500000 }, | |
| { .cmd = DTV_INNER_FEC, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_PILOT, .u.data = PILOT_AUTO }, | |
| { .cmd = DTV_ROLLOFF, .u.data = ROLLOFF_AUTO }, | |
| { .cmd = DTV_STREAM_ID, .u.data = 0 }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties dvbs2_cmdseq = { | |
| .num = sizeof(dvbs2_cmdargs)/sizeof(struct dtv_property), | |
| .props = dvbs2_cmdargs | |
| }; | |
| static struct dtv_property dvbc_cmdargs[] = { | |
| #if DVBAPI_VERSION >= 505 | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBC_ANNEX_A }, | |
| #else | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBC_ANNEX_AC }, | |
| #endif | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_SYMBOL_RATE, .u.data = 27500000 }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties dvbc_cmdseq = { | |
| .num = sizeof(dvbc_cmdargs)/sizeof(struct dtv_property), | |
| .props = dvbc_cmdargs | |
| }; | |
| static struct dtv_property dvbt_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBT }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_BANDWIDTH_HZ, .u.data = 8000000 }, | |
| { .cmd = DTV_CODE_RATE_HP, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_CODE_RATE_LP, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_GUARD_INTERVAL, .u.data = GUARD_INTERVAL_AUTO }, | |
| { .cmd = DTV_TRANSMISSION_MODE,.u.data = TRANSMISSION_MODE_AUTO }, | |
| { .cmd = DTV_HIERARCHY, .u.data = HIERARCHY_AUTO }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_property dvbt2_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_DVBT2 }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_BANDWIDTH_HZ, .u.data = 8000000 }, | |
| { .cmd = DTV_CODE_RATE_HP, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_CODE_RATE_LP, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_GUARD_INTERVAL, .u.data = GUARD_INTERVAL_AUTO }, | |
| { .cmd = DTV_TRANSMISSION_MODE,.u.data = TRANSMISSION_MODE_AUTO }, | |
| { .cmd = DTV_HIERARCHY, .u.data = HIERARCHY_AUTO }, | |
| { .cmd = DTV_STREAM_ID, .u.data = 0 }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties dvbt2_cmdseq = { | |
| .num = sizeof(dvbt2_cmdargs)/sizeof(struct dtv_property), | |
| .props = dvbt2_cmdargs | |
| }; | |
| static struct dtv_properties dvbt_cmdseq = { | |
| .num = sizeof(dvbt_cmdargs)/sizeof(struct dtv_property), | |
| .props = dvbt_cmdargs | |
| }; | |
| /* ATSC + DVB-C annex B */ | |
| static struct dtv_property atsc_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_ATSC }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties atsc_cmdseq = { | |
| .num = sizeof(atsc_cmdargs)/sizeof(struct dtv_property), | |
| .props = atsc_cmdargs | |
| }; | |
| static struct dtv_property isdbt_cmdargs[] = { | |
| { .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_ISDBT }, | |
| { .cmd = DTV_FREQUENCY, .u.data = 0 }, | |
| { .cmd = DTV_BANDWIDTH_HZ, .u.data = 6000000 }, | |
| { .cmd = DTV_INVERSION, .u.data = INVERSION_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERA_FEC, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERA_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERA_SEGMENT_COUNT, .u.data = 0 }, | |
| { .cmd = DTV_ISDBT_LAYERA_TIME_INTERLEAVING,.u.data = 0 }, | |
| { .cmd = DTV_ISDBT_LAYERB_FEC, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERB_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERB_SEGMENT_COUNT, .u.data = 0 }, | |
| { .cmd = DTV_ISDBT_LAYERB_TIME_INTERLEAVING,.u.data = 0 }, | |
| { .cmd = DTV_ISDBT_LAYERC_FEC, .u.data = FEC_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERC_MODULATION, .u.data = QAM_AUTO }, | |
| { .cmd = DTV_ISDBT_LAYERC_SEGMENT_COUNT, .u.data = 0 }, | |
| { .cmd = DTV_ISDBT_LAYERC_TIME_INTERLEAVING,.u.data = 0 }, | |
| { .cmd = DTV_TUNE }, | |
| }; | |
| static struct dtv_properties isdbt_cmdseq = { | |
| .num = sizeof(isdbt_cmdargs)/sizeof(struct dtv_property), | |
| .props = isdbt_cmdargs | |
| }; | |
| #define DELSYS 0 | |
| #define FREQUENCY 1 | |
| #define MODULATION 2 | |
| #define INVERSION 3 | |
| #define SYMBOL_RATE 4 | |
| #define BANDWIDTH 4 | |
| #define FEC_INNER 5 | |
| #define FEC_LP 6 | |
| #define GUARD 7 | |
| #define PILOT 7 | |
| #define TRANSMISSION 8 | |
| #define ROLLOFF 8 | |
| #define MIS 9 | |
| #define HIERARCHY 9 | |
| #define PLP_ID 10 | |
| //ISDBT | |
| #define ISDBT_BANDWIDTH 2 | |
| #define ISDBT_LAYERA_FEC 4 | |
| #define ISDBT_LAYERA_MODULATION 5 | |
| #define ISDBT_LAYERA_SEGMENT_COUNT 6 | |
| #define ISDBT_LAYERA_TIME_INTERLEAVING 7 | |
| #define ISDBT_LAYERB_FEC 8 | |
| #define ISDBT_LAYERB_MODULATION 9 | |
| #define ISDBT_LAYERB_SEGMENT_COUNT 10 | |
| #define ISDBT_LAYERB_TIME_INTERLEAVING 11 | |
| #define ISDBT_LAYERC_FEC 12 | |
| #define ISDBT_LAYERC_MODULATION 13 | |
| #define ISDBT_LAYERC_SEGMENT_COUNT 14 | |
| #define ISDBT_LAYERC_TIME_INTERLEAVING 15 | |
| struct dtv_property pclear[] = { | |
| { .cmd = DTV_CLEAR }, | |
| }; | |
| struct dtv_properties cmdclear = { | |
| .num = 1, | |
| .props = pclear | |
| }; | |
| static fe_delivery_system_t | |
| FrontendGuessSystem( fe_delivery_system_t *p_systems, int i_systems ) | |
| { | |
| if ( psz_delsys != NULL ) | |
| { | |
| if ( !strcasecmp( psz_delsys, "DVBS" ) ) | |
| return SYS_DVBS; | |
| if ( !strcasecmp( psz_delsys, "DVBS2" ) ) | |
| return SYS_DVBS2; | |
| if ( !strcasecmp( psz_delsys, "DVBC_ANNEX_A" ) ) | |
| #if DVBAPI_VERSION >= 505 | |
| return SYS_DVBC_ANNEX_A; | |
| #else | |
| return SYS_DVBC_ANNEX_AC; | |
| #endif | |
| if ( !strcasecmp( psz_delsys, "DVBC_ANNEX_B" ) ) | |
| return SYS_DVBC_ANNEX_B; | |
| if ( !strcasecmp( psz_delsys, "DVBT" ) ) | |
| return SYS_DVBT; | |
| if ( !strcasecmp( psz_delsys, "DVBT2" ) ) | |
| return SYS_DVBT2; | |
| if ( !strcasecmp( psz_delsys, "ATSC" ) ) | |
| return SYS_ATSC; | |
| if ( !strcasecmp( psz_delsys, "ISDBT" ) ) | |
| return SYS_ISDBT; | |
| msg_Err( NULL, "unknown delivery system %s", psz_delsys ); | |
| exit(1); | |
| } | |
| if ( i_systems == 1 ) | |
| return p_systems[0]; | |
| int i; | |
| for ( i = 0; i < i_systems; i++ ) | |
| { | |
| switch ( p_systems[i] ) | |
| { | |
| case SYS_DVBS: | |
| if ( i_frequency < 50000000 ) | |
| return SYS_DVBS; | |
| break; | |
| #if DVBAPI_VERSION >= 505 | |
| case SYS_DVBC_ANNEX_A: | |
| if ( i_frequency > 50000000 || i_srate != 27500000 || | |
| psz_modulation != NULL ) | |
| return SYS_DVBC_ANNEX_A; | |
| break; | |
| #else | |
| case SYS_DVBC_ANNEX_AC: | |
| if ( i_frequency > 50000000 || i_srate != 27500000 || | |
| psz_modulation != NULL ) | |
| return SYS_DVBC_ANNEX_AC; | |
| break; | |
| #endif | |
| case SYS_DVBT: | |
| if ( i_frequency > 50000000 ) | |
| return SYS_DVBT; | |
| break; | |
| case SYS_DVBT2: | |
| if ( i_frequency > 50000000 && (dvb_plp_id) ) | |
| return SYS_DVBT2; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| msg_Warn( NULL, "couldn't guess delivery system, use --delsys" ); | |
| return p_systems[0]; | |
| } | |
| static void FrontendSet( bool b_init ) | |
| { | |
| struct dvb_frontend_info info; | |
| struct dtv_properties *p; | |
| fe_delivery_system_t p_systems[MAX_DELIVERY_SYSTEMS] = { 0 }; | |
| int i_systems = 0; | |
| if ( ioctl( i_frontend, FE_GET_INFO, &info ) < 0 ) | |
| { | |
| msg_Err( NULL, "FE_GET_INFO failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| uint32_t version = 0x300; | |
| #if DVBAPI_VERSION >= 505 | |
| if ( ioctl( i_frontend, FE_GET_PROPERTY, &info_cmdseq ) < 0 ) | |
| { | |
| #endif | |
| /* DVBv3 device */ | |
| switch ( info.type ) | |
| { | |
| case FE_OFDM: | |
| p_systems[i_systems++] = SYS_DVBT; | |
| #if DVBAPI_VERSION >= 505 | |
| if ( info.caps & FE_CAN_2G_MODULATION ) | |
| p_systems[i_systems++] = SYS_DVBT2; | |
| #endif | |
| break; | |
| case FE_QAM: | |
| #if DVBAPI_VERSION >= 505 | |
| p_systems[i_systems++] = SYS_DVBC_ANNEX_A; | |
| #else | |
| p_systems[i_systems++] = SYS_DVBC_ANNEX_AC; | |
| #endif | |
| break; | |
| case FE_QPSK: | |
| p_systems[i_systems++] = SYS_DVBS; | |
| if ( info.caps & FE_CAN_2G_MODULATION ) | |
| p_systems[i_systems++] = SYS_DVBS2; | |
| break; | |
| case FE_ATSC: | |
| if ( info.caps & (FE_CAN_8VSB | FE_CAN_16VSB) ) | |
| p_systems[i_systems++] = SYS_ATSC; | |
| if ( info.caps & (FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO) ) | |
| p_systems[i_systems++] = SYS_DVBC_ANNEX_B; | |
| break; | |
| default: | |
| msg_Err( NULL, "unknown frontend type %d", info.type ); | |
| exit(1); | |
| } | |
| #if DVBAPI_VERSION >= 505 | |
| } | |
| else | |
| { | |
| version = info_cmdargs[0].u.data; | |
| if ( ioctl( i_frontend, FE_GET_PROPERTY, &enum_cmdseq ) < 0 ) | |
| { | |
| msg_Err( NULL, "unable to query frontend" ); | |
| exit(1); | |
| } | |
| i_systems = enum_cmdargs[0].u.buffer.len; | |
| if ( i_systems < 1 ) | |
| { | |
| msg_Err( NULL, "no available delivery system" ); | |
| exit(1); | |
| } | |
| int i; | |
| for ( i = 0; i < i_systems; i++ ) | |
| p_systems[i] = enum_cmdargs[0].u.buffer.data[i]; | |
| } | |
| #endif | |
| if ( b_init ) | |
| FrontendInfo( &info, version, p_systems, i_systems ); | |
| /* Clear frontend commands */ | |
| if ( ioctl( i_frontend, FE_SET_PROPERTY, &cmdclear ) < 0 ) | |
| { | |
| msg_Err( NULL, "Unable to clear frontend" ); | |
| exit(1); | |
| } | |
| fe_delivery_system_t system = FrontendGuessSystem( p_systems, i_systems ); | |
| switch ( system ) | |
| { | |
| case SYS_DVBT: | |
| p = &dvbt_cmdseq; | |
| p->props[DELSYS].u.data = system; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| if ( psz_modulation != NULL ) | |
| p->props[MODULATION].u.data = GetModulation(); | |
| p->props[BANDWIDTH].u.data = i_bandwidth * 1000000; | |
| p->props[FEC_INNER].u.data = GetFECInner(info.caps); | |
| p->props[FEC_LP].u.data = GetFECLP(info.caps); | |
| p->props[GUARD].u.data = GetGuard(); | |
| p->props[TRANSMISSION].u.data = GetTransmission(); | |
| p->props[HIERARCHY].u.data = GetHierarchy(); | |
| msg_Dbg( NULL, "tuning DVB-T frontend to f=%d bandwidth=%d inversion=%d fec_hp=%d fec_lp=%d hierarchy=%d modulation=%s guard=%d transmission=%d", | |
| i_frequency, i_bandwidth, i_inversion, i_fec, i_fec_lp, | |
| i_hierarchy, | |
| psz_modulation == NULL ? "qam_auto" : psz_modulation, | |
| i_guard, i_transmission ); | |
| break; | |
| case SYS_DVBT2: | |
| p = &dvbt2_cmdseq; | |
| p->props[DELSYS].u.data = system; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| if ( psz_modulation != NULL ) | |
| p->props[MODULATION].u.data = GetModulation(); | |
| p->props[BANDWIDTH].u.data = i_bandwidth * 1000000; | |
| p->props[FEC_INNER].u.data = GetFECInner(info.caps); | |
| p->props[FEC_LP].u.data = GetFECLP(info.caps); | |
| p->props[GUARD].u.data = GetGuard(); | |
| p->props[TRANSMISSION].u.data = GetTransmission(); | |
| p->props[HIERARCHY].u.data = GetHierarchy(); | |
| p->props[PLP_ID].u.data = dvb_plp_id; | |
| msg_Dbg( NULL, "tuning DVB-T2 frontend to f=%d bandwidth=%d inversion=%d fec_hp=%d fec_lp=%d hierarchy=%d modulation=%s guard=%d transmission=%d PLP_ID=%d ", | |
| i_frequency, i_bandwidth, i_inversion, i_fec, i_fec_lp, | |
| i_hierarchy, | |
| psz_modulation == NULL ? "qam_auto" : psz_modulation, | |
| i_guard, i_transmission, p->props[PLP_ID].u.data ); | |
| break; | |
| #if DVBAPI_VERSION >= 505 | |
| case SYS_DVBC_ANNEX_A: | |
| #else | |
| case SYS_DVBC_ANNEX_AC: | |
| #endif | |
| p = &dvbc_cmdseq; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| if ( psz_modulation != NULL ) | |
| p->props[MODULATION].u.data = GetModulation(); | |
| p->props[SYMBOL_RATE].u.data = i_srate; | |
| msg_Dbg( NULL, "tuning DVB-C frontend to f=%d srate=%d inversion=%d modulation=%s", | |
| i_frequency, i_srate, i_inversion, | |
| psz_modulation == NULL ? "qam_auto" : psz_modulation ); | |
| break; | |
| case SYS_DVBC_ANNEX_B: | |
| p = &atsc_cmdseq; | |
| p->props[DELSYS].u.data = system; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| if ( psz_modulation != NULL ) | |
| p->props[MODULATION].u.data = GetModulation(); | |
| msg_Dbg( NULL, "tuning ATSC cable frontend to f=%d inversion=%d modulation=%s", | |
| i_frequency, i_inversion, | |
| psz_modulation == NULL ? "qam_auto" : psz_modulation ); | |
| break; | |
| case SYS_DVBS: | |
| case SYS_DVBS2: | |
| if ( psz_modulation != NULL ) | |
| { | |
| p = &dvbs2_cmdseq; | |
| p->props[MODULATION].u.data = GetModulation(); | |
| p->props[PILOT].u.data = GetPilot(); | |
| p->props[ROLLOFF].u.data = GetRollOff(); | |
| p->props[MIS].u.data = i_mis; | |
| } | |
| else | |
| p = &dvbs_cmdseq; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| p->props[SYMBOL_RATE].u.data = i_srate; | |
| p->props[FEC_INNER].u.data = GetFECInner(info.caps); | |
| p->props[FREQUENCY].u.data = FrontendDoDiseqc(); | |
| msg_Dbg( NULL, "tuning DVB-S frontend to f=%d srate=%d inversion=%d fec=%d rolloff=%d modulation=%s pilot=%d mis=%d", | |
| i_frequency, i_srate, i_inversion, i_fec, i_rolloff, | |
| psz_modulation == NULL ? "legacy" : psz_modulation, i_pilot, | |
| i_mis ); | |
| break; | |
| case SYS_ATSC: | |
| p = &atsc_cmdseq; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| if ( psz_modulation != NULL ) | |
| p->props[MODULATION].u.data = GetModulation(); | |
| msg_Dbg( NULL, "tuning ATSC frontend to f=%d inversion=%d modulation=%s", | |
| i_frequency, i_inversion, | |
| psz_modulation == NULL ? "qam_auto" : psz_modulation ); | |
| break; | |
| case SYS_ISDBT: | |
| p = &isdbt_cmdseq; | |
| p->props[DELSYS].u.data = system; | |
| p->props[FREQUENCY].u.data = i_frequency; | |
| p->props[ISDBT_BANDWIDTH].u.data = i_bandwidth * 1000000; | |
| p->props[INVERSION].u.data = GetInversion(); | |
| p->props[ISDBT_LAYERA_FEC].u.data = FEC_AUTO; | |
| p->props[ISDBT_LAYERA_MODULATION].u.data = QAM_AUTO; | |
| p->props[ISDBT_LAYERA_SEGMENT_COUNT].u.data = 0; | |
| p->props[ISDBT_LAYERA_TIME_INTERLEAVING].u.data = 0; | |
| p->props[ISDBT_LAYERB_FEC].u.data = FEC_AUTO; | |
| p->props[ISDBT_LAYERB_MODULATION].u.data = QAM_AUTO; | |
| p->props[ISDBT_LAYERB_SEGMENT_COUNT].u.data = 0; | |
| p->props[ISDBT_LAYERB_TIME_INTERLEAVING].u.data = 0; | |
| p->props[ISDBT_LAYERC_FEC].u.data = FEC_AUTO; | |
| p->props[ISDBT_LAYERC_MODULATION].u.data = QAM_AUTO; | |
| p->props[ISDBT_LAYERC_SEGMENT_COUNT].u.data = 0; | |
| p->props[ISDBT_LAYERC_TIME_INTERLEAVING].u.data = 0; | |
| msg_Dbg( NULL, "tuning ISDB-T frontend to f=%d bandwidth=%d ", | |
| i_frequency, i_bandwidth); | |
| break; | |
| default: | |
| msg_Err( NULL, "unknown frontend type %d", info.type ); | |
| exit(1); | |
| } | |
| /* Empty the event queue */ | |
| for ( ; ; ) | |
| { | |
| struct dvb_frontend_event event; | |
| if ( ioctl( i_frontend, FE_GET_EVENT, &event ) < 0 | |
| && errno == EWOULDBLOCK ) | |
| break; | |
| } | |
| /* Now send it all to the frontend device */ | |
| if ( ioctl( i_frontend, FE_SET_PROPERTY, p ) < 0 ) | |
| { | |
| msg_Err( NULL, "setting frontend failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| i_last_status = 0; | |
| if (i_frontend_timeout_duration) | |
| ev_timer_again(event_loop, &lock_watcher); | |
| } | |
| #else /* !S2API */ | |
| #warning "You are trying to compile DVBlast with an outdated linux-dvb interface." | |
| #warning "DVBlast will be very limited and some options will have no effect." | |
| static void FrontendSet( bool b_init ) | |
| { | |
| struct dvb_frontend_info info; | |
| struct dvb_frontend_parameters fep; | |
| if ( ioctl( i_frontend, FE_GET_INFO, &info ) < 0 ) | |
| { | |
| msg_Err( NULL, "FE_GET_INFO failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| switch ( info.type ) | |
| { | |
| case FE_OFDM: | |
| fep.frequency = i_frequency; | |
| fep.inversion = INVERSION_AUTO; | |
| switch ( i_bandwidth ) | |
| { | |
| case 6: fep.u.ofdm.bandwidth = BANDWIDTH_6_MHZ; break; | |
| case 7: fep.u.ofdm.bandwidth = BANDWIDTH_7_MHZ; break; | |
| default: | |
| case 8: fep.u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; | |
| } | |
| fep.u.ofdm.code_rate_HP = FEC_AUTO; | |
| fep.u.ofdm.code_rate_LP = FEC_AUTO; | |
| fep.u.ofdm.constellation = QAM_AUTO; | |
| fep.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; | |
| fep.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; | |
| fep.u.ofdm.hierarchy_information = HIERARCHY_AUTO; | |
| msg_Dbg( NULL, "tuning OFDM frontend to f=%d, bandwidth=%d", | |
| i_frequency, i_bandwidth ); | |
| break; | |
| case FE_QAM: | |
| fep.frequency = i_frequency; | |
| fep.inversion = INVERSION_AUTO; | |
| fep.u.qam.symbol_rate = i_srate; | |
| fep.u.qam.fec_inner = FEC_AUTO; | |
| fep.u.qam.modulation = QAM_AUTO; | |
| msg_Dbg( NULL, "tuning QAM frontend to f=%d, srate=%d", | |
| i_frequency, i_srate ); | |
| break; | |
| case FE_QPSK: | |
| fep.inversion = INVERSION_AUTO; | |
| fep.u.qpsk.symbol_rate = i_srate; | |
| fep.u.qpsk.fec_inner = FEC_AUTO; | |
| fep.frequency = FrontendDoDiseqc(); | |
| msg_Dbg( NULL, "tuning QPSK frontend to f=%d, srate=%d", | |
| i_frequency, i_srate ); | |
| break; | |
| #if DVBAPI_VERSION >= 301 | |
| case FE_ATSC: | |
| fep.frequency = i_frequency; | |
| fep.u.vsb.modulation = QAM_AUTO; | |
| msg_Dbg( NULL, "tuning ATSC frontend to f=%d", i_frequency ); | |
| break; | |
| #endif | |
| default: | |
| msg_Err( NULL, "unknown frontend type %d", info.type ); | |
| exit(1); | |
| } | |
| /* Empty the event queue */ | |
| for ( ; ; ) | |
| { | |
| struct dvb_frontend_event event; | |
| if ( ioctl( i_frontend, FE_GET_EVENT, &event ) < 0 | |
| && errno == EWOULDBLOCK ) | |
| break; | |
| } | |
| /* Now send it all to the frontend device */ | |
| if ( ioctl( i_frontend, FE_SET_FRONTEND, &fep ) < 0 ) | |
| { | |
| msg_Err( NULL, "setting frontend failed (%s)", strerror(errno) ); | |
| exit(1); | |
| } | |
| i_last_status = 0; | |
| if (i_frontend_timeout_duration) | |
| ev_timer_again(event_loop, &lock_watcher); | |
| } | |
| #endif /* S2API */ | |
| /***************************************************************************** | |
| * dvb_FrontendStatus | |
| *****************************************************************************/ | |
| uint8_t dvb_FrontendStatus( uint8_t *p_answer, ssize_t *pi_size ) | |
| { | |
| struct ret_frontend_status *p_ret = (struct ret_frontend_status *)p_answer; | |
| if ( ioctl( i_frontend, FE_GET_INFO, &p_ret->info ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_GET_INFO failed (%s)", strerror(errno) ); | |
| return RET_ERR; | |
| } | |
| if ( ioctl( i_frontend, FE_READ_STATUS, &p_ret->i_status ) < 0 ) | |
| { | |
| msg_Err( NULL, "ioctl FE_READ_STATUS failed (%s)", strerror(errno) ); | |
| return RET_ERR; | |
| } | |
| if ( p_ret->i_status & FE_HAS_LOCK ) | |
| { | |
| if ( ioctl( i_frontend, FE_READ_BER, &p_ret->i_ber ) < 0 ) | |
| msg_Err( NULL, "ioctl FE_READ_BER failed (%s)", strerror(errno) ); | |
| if ( ioctl( i_frontend, FE_READ_SIGNAL_STRENGTH, &p_ret->i_strength ) | |
| < 0 ) | |
| msg_Err( NULL, "ioctl FE_READ_SIGNAL_STRENGTH failed (%s)", | |
| strerror(errno) ); | |
| if ( ioctl( i_frontend, FE_READ_SNR, &p_ret->i_snr ) < 0 ) | |
| msg_Err( NULL, "ioctl FE_READ_SNR failed (%s)", strerror(errno) ); | |
| } | |
| *pi_size = sizeof(struct ret_frontend_status); | |
| return RET_FRONTEND_STATUS; | |
| } | |
| #endif |