/* * Tvheadend - Linux DVB frontend * * Copyright (C) 2013 Adam Sutton * * 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 3 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, see . */ #include "tvheadend.h" #include "linuxdvb_private.h" #include "notify.h" #include "atomic.h" #include "tvhpoll.h" #include "streaming.h" #include #include #include #include #include #include #include #include static void linuxdvb_frontend_monitor ( void *aux ); static void * linuxdvb_frontend_input_thread ( void *aux ); /* ************************************************************************** * Class definition * *************************************************************************/ static void linuxdvb_frontend_class_save ( idnode_t *in ) { linuxdvb_adapter_t *la = ((linuxdvb_frontend_t*)in)->lfe_adapter; linuxdvb_adapter_save(la); } const idclass_t linuxdvb_frontend_class = { .ic_super = &mpegts_input_class, .ic_class = "linuxdvb_frontend", .ic_caption = "Linux DVB Frontend", .ic_save = linuxdvb_frontend_class_save, .ic_properties = (const property_t[]) { { .type = PT_STR, .id = "fe_path", .name = "Frontend Path", .opts = PO_RDONLY | PO_NOSAVE, .off = offsetof(linuxdvb_frontend_t, lfe_fe_path), }, { .type = PT_STR, .id = "dvr_path", .name = "Input Path", .opts = PO_RDONLY | PO_NOSAVE, .off = offsetof(linuxdvb_frontend_t, lfe_dvr_path), }, { .type = PT_STR, .id = "dmx_path", .name = "Demux Path", .opts = PO_RDONLY | PO_NOSAVE, .off = offsetof(linuxdvb_frontend_t, lfe_dmx_path), }, { .type = PT_INT, .id = "fe_number", .name = "Frontend Number", .opts = PO_RDONLY | PO_NOSAVE, .off = offsetof(linuxdvb_frontend_t, lfe_number), }, { .type = PT_BOOL, .id = "powersave", .name = "Power Save", .off = offsetof(linuxdvb_frontend_t, lfe_powersave), }, { .type = PT_U32, .id = "skip_bytes", .name = "Skip Initial Bytes", .opts = PO_ADVANCED, .off = offsetof(linuxdvb_frontend_t, lfe_skip_bytes), }, {} } }; const idclass_t linuxdvb_frontend_dvbt_class = { .ic_super = &linuxdvb_frontend_class, .ic_class = "linuxdvb_frontend_dvbt", .ic_caption = "Linux DVB-T Frontend", .ic_properties = (const property_t[]){ {} } }; static idnode_set_t * linuxdvb_frontend_dvbs_class_get_childs ( idnode_t *self ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)self; idnode_set_t *is = idnode_set_create(0); idnode_set_add(is, &lfe->lfe_satconf->ls_id, NULL); return is; } static int linuxdvb_frontend_dvbs_class_satconf_set ( void *self, const void *str ) { linuxdvb_frontend_t *lfe = self; if (lfe->lfe_satconf) { if (!strcmp(str ?: "", lfe->lfe_satconf->ls_type)) return 0; linuxdvb_satconf_delete(lfe->lfe_satconf, 1); } lfe->lfe_satconf = linuxdvb_satconf_create(lfe, str, NULL, NULL); return 1; } static const void * linuxdvb_frontend_dvbs_class_satconf_get ( void *self ) { static const char *s; linuxdvb_frontend_t *lfe = self; if (lfe->lfe_satconf) s = lfe->lfe_satconf->ls_type; else s = NULL; return &s; } const idclass_t linuxdvb_frontend_dvbs_class = { .ic_super = &linuxdvb_frontend_class, .ic_class = "linuxdvb_frontend_dvbs", .ic_caption = "Linux DVB-S Frontend", .ic_get_childs = linuxdvb_frontend_dvbs_class_get_childs, .ic_properties = (const property_t[]){ { .type = PT_STR, .id = "satconf", .name = "SatConfig", .opts = PO_NOSAVE, .set = linuxdvb_frontend_dvbs_class_satconf_set, .get = linuxdvb_frontend_dvbs_class_satconf_get, .list = linuxdvb_satconf_type_list, .def.s = "simple" }, { .id = "networks", .type = PT_NONE, }, {} } }; const idclass_t linuxdvb_frontend_dvbc_class = { .ic_super = &linuxdvb_frontend_class, .ic_class = "linuxdvb_frontend_dvbc", .ic_caption = "Linux DVB-C Frontend", .ic_properties = (const property_t[]){ {} } }; const idclass_t linuxdvb_frontend_atsc_class = { .ic_super = &linuxdvb_frontend_class, .ic_class = "linuxdvb_frontend_atsc", .ic_caption = "Linux ATSC Frontend", .ic_properties = (const property_t[]){ {} } }; /* ************************************************************************** * Class methods * *************************************************************************/ static void linuxdvb_frontend_enabled_updated ( mpegts_input_t *mi ) { char buf[512]; linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; mi->mi_display_name(mi, buf, sizeof(buf)); /* Ensure disabled */ if (!mi->mi_enabled) { tvhtrace("linuxdvb", "%s - disabling tuner", buf); if (lfe->lfe_fe_fd > 0) { close(lfe->lfe_fe_fd); lfe->lfe_fe_fd = -1; } gtimer_disarm(&lfe->lfe_monitor_timer); /* Ensure FE opened (if not powersave) */ } else if (!lfe->lfe_powersave && lfe->lfe_fe_fd <= 0 && lfe->lfe_fe_path) { lfe->lfe_fe_fd = tvh_open(lfe->lfe_fe_path, O_RDWR | O_NONBLOCK, 0); tvhtrace("linuxdvb", "%s - opening FE %s (%d)", buf, lfe->lfe_fe_path, lfe->lfe_fe_fd); } } static int linuxdvb_frontend_is_free ( mpegts_input_t *mi ) { linuxdvb_adapter_t *la = ((linuxdvb_frontend_t*)mi)->lfe_adapter; linuxdvb_frontend_t *lfe; LIST_FOREACH(lfe, &la->la_frontends, lfe_link) if (!mpegts_input_is_free((mpegts_input_t*)lfe)) return 0; return 1; } static int linuxdvb_frontend_get_weight ( mpegts_input_t *mi, int flags ) { int weight = 0; linuxdvb_adapter_t *la = ((linuxdvb_frontend_t*)mi)->lfe_adapter; linuxdvb_frontend_t *lfe; LIST_FOREACH(lfe, &la->la_frontends, lfe_link) weight = MAX(weight, mpegts_input_get_weight((mpegts_input_t*)lfe, flags)); return weight; } static int linuxdvb_frontend_get_priority ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; int r = mpegts_input_get_priority(mi, mm, flags); if (lfe->lfe_satconf) r += linuxdvb_satconf_get_priority(lfe->lfe_satconf, mm); return r; } static int linuxdvb_frontend_get_grace ( mpegts_input_t *mi, mpegts_mux_t *mm ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; int r = 5; if (lfe->lfe_satconf) r = linuxdvb_satconf_get_grace(lfe->lfe_satconf, mm); return r; } static int linuxdvb_frontend_is_enabled ( mpegts_input_t *mi, mpegts_mux_t *mm, int flags ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; if (lfe->lfe_fe_path == NULL) return 0; if (!mpegts_input_is_enabled(mi, mm, flags)) return 0; if (access(lfe->lfe_fe_path, R_OK | W_OK)) return 0; if (lfe->lfe_in_setup) return 0; return 1; } static void linuxdvb_frontend_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) { char buf1[256], buf2[256]; linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; mi->mi_display_name(mi, buf1, sizeof(buf1)); mpegts_mux_nice_name(mmi->mmi_mux, buf2, sizeof(buf2)); tvhdebug("linuxdvb", "%s - stopping %s", buf1, buf2); /* Stop thread */ if (lfe->lfe_dvr_pipe.wr > 0) { tvh_write(lfe->lfe_dvr_pipe.wr, "", 1); tvhtrace("linuxdvb", "%s - waiting for dvr thread", buf1); pthread_join(lfe->lfe_dvr_thread, NULL); tvh_pipe_close(&lfe->lfe_dvr_pipe); tvhdebug("linuxdvb", "%s - stopped dvr thread", buf1); } /* Not locked */ lfe->lfe_ready = 0; lfe->lfe_locked = 0; lfe->lfe_status = 0; /* Ensure it won't happen immediately */ gtimer_arm(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor, lfe, 2); if (lfe->lfe_satconf) linuxdvb_satconf_post_stop_mux(lfe->lfe_satconf); lfe->lfe_in_setup = 0; } static int linuxdvb_frontend_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; int res; lfe->lfe_in_setup = 1; lfe->lfe_ioctls = 0; if (lfe->lfe_satconf) res = linuxdvb_satconf_start_mux(lfe->lfe_satconf, mmi); else res = linuxdvb_frontend_tune1((linuxdvb_frontend_t*)mi, mmi, -1); if (res) lfe->lfe_in_setup = 0; return res; } static void linuxdvb_frontend_open_pid0 ( linuxdvb_frontend_t *lfe, mpegts_pid_t *mp ) { char name[256]; struct dmx_pes_filter_params dmx_param; int fd; /* Already opened */ if (mp->mp_fd != -1) return; /* Not locked */ if (!lfe->lfe_locked) return; lfe->mi_display_name((mpegts_input_t*)lfe, name, sizeof(name)); /* Open DMX */ fd = tvh_open(lfe->lfe_dmx_path, O_RDWR, 0); if(fd == -1) { tvherror("linuxdvb", "%s - failed to open dmx for pid %d [e=%s]", name, mp->mp_pid, strerror(errno)); return; } /* Install filter */ tvhtrace("linuxdvb", "%s - open PID %04X (%d) fd %d", name, mp->mp_pid, mp->mp_pid, fd); memset(&dmx_param, 0, sizeof(dmx_param)); dmx_param.pid = mp->mp_pid; dmx_param.input = DMX_IN_FRONTEND; dmx_param.output = DMX_OUT_TS_TAP; dmx_param.pes_type = DMX_PES_OTHER; dmx_param.flags = DMX_IMMEDIATE_START; if(ioctl(fd, DMX_SET_PES_FILTER, &dmx_param)) { tvherror("linuxdvb", "%s - failed to config dmx for pid %d [e=%s]", name, mp->mp_pid, strerror(errno)); close(fd); return; } /* Store */ mp->mp_fd = fd; return; } static mpegts_pid_t * linuxdvb_frontend_open_pid ( mpegts_input_t *mi, mpegts_mux_t *mm, int pid, int type, void *owner ) { mpegts_pid_t *mp; linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; if (!(mp = mpegts_input_open_pid(mi, mm, pid, type, owner))) return NULL; linuxdvb_frontend_open_pid0(lfe, mp); return mp; } static idnode_set_t * linuxdvb_frontend_network_list ( mpegts_input_t *mi ) { linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)mi; const idclass_t *idc; if (lfe->lfe_type == DVB_TYPE_T) idc = &dvb_network_dvbt_class; else if (lfe->lfe_type == DVB_TYPE_C) idc = &dvb_network_dvbc_class; else if (lfe->lfe_type == DVB_TYPE_S) idc = &dvb_network_dvbs_class; else if (lfe->lfe_type == DVB_TYPE_ATSC) idc = &dvb_network_atsc_class; else return NULL; return idnode_find_all(idc, NULL); } /* ************************************************************************** * Data processing * *************************************************************************/ static void linuxdvb_frontend_default_tables ( linuxdvb_frontend_t *lfe, dvb_mux_t *lm ) { mpegts_mux_t *mm = (mpegts_mux_t*)lm; psi_tables_default(mm); /* ATSC */ if (lfe->lfe_type == DVB_TYPE_ATSC) { if (lm->lm_tuning.dmc_fe_modulation == DVB_MOD_VSB_8) psi_tables_atsc_t(mm); else psi_tables_atsc_c(mm); /* DVB */ } else { psi_tables_dvb(mm); } } static inline int ioctl_check( linuxdvb_frontend_t *lfe, int bit ) { return !(lfe->lfe_ioctls & (1 << bit)); } static inline void ioctl_bad( linuxdvb_frontend_t *lfe, int bit ) { lfe->lfe_ioctls |= 1 << bit; } static void linuxdvb_frontend_monitor ( void *aux ) { uint16_t u16; uint32_t u32; char buf[256]; linuxdvb_frontend_t *lfe = aux; mpegts_mux_instance_t *mmi = LIST_FIRST(&lfe->mi_mux_active); mpegts_mux_t *mm; mpegts_pid_t *mp; fe_status_t fe_status; signal_state_t status; signal_status_t sigstat; streaming_message_t sm; service_t *s; int logit = 0; #if DVB_VER_ATLEAST(5,10) struct dtv_property fe_properties[6]; struct dtv_properties dtv_prop; int gotprop; #endif lfe->mi_display_name((mpegts_input_t*)lfe, buf, sizeof(buf)); tvhtrace("linuxdvb", "%s - checking FE status%s", buf, lfe->lfe_ready ? " (ready)" : ""); /* Disabled */ if (!lfe->mi_enabled && mmi) mmi->mmi_mux->mm_stop(mmi->mmi_mux, 1); /* Close FE */ if (lfe->lfe_fe_fd > 0 && !mmi && lfe->lfe_powersave) { tvhtrace("linuxdvb", "%s - closing frontend", buf); close(lfe->lfe_fe_fd); lfe->lfe_fe_fd = -1; } /* Check accessibility */ if (lfe->lfe_fe_fd <= 0) { if (lfe->lfe_fe_path && access(lfe->lfe_fe_path, R_OK | W_OK)) { tvherror("linuxdvb", "%s - device is not accessible", buf); // TODO: disable device return; } } /* Stop timer */ if (!mmi || !lfe->lfe_ready) return; /* re-arm */ gtimer_arm(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor, lfe, 1); /* Get current status */ if (ioctl(lfe->lfe_fe_fd, FE_READ_STATUS, &fe_status) == -1) { tvhwarn("linuxdvb", "%s - FE_READ_STATUS error %s", buf, strerror(errno)); /* TODO: check error value */ return; } else if (fe_status & FE_HAS_LOCK) status = SIGNAL_GOOD; else if (fe_status & (FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_CARRIER)) status = SIGNAL_BAD; else if (fe_status & FE_HAS_SIGNAL) status = SIGNAL_FAINT; else status = SIGNAL_NONE; /* Set default period */ if (fe_status != lfe->lfe_status) { tvhdebug("linuxdvb", "%s - status %7s (%s%s%s%s%s%s)", buf, signal2str(status), (fe_status & FE_HAS_SIGNAL) ? "SIGNAL" : "", (fe_status & FE_HAS_CARRIER) ? " | CARRIER" : "", (fe_status & FE_HAS_VITERBI) ? " | VITERBI" : "", (fe_status & FE_HAS_SYNC) ? " | SYNC" : "", (fe_status & FE_HAS_LOCK) ? " | SIGNAL" : "", (fe_status & FE_TIMEDOUT) ? "TIMEOUT" : ""); } else { tvhtrace("linuxdvb", "%s - status %d (%04X)", buf, status, fe_status); } lfe->lfe_status = fe_status; /* Get current mux */ mm = mmi->mmi_mux; /* Waiting for lock */ if (!lfe->lfe_locked) { /* Locked */ if (status == SIGNAL_GOOD) { tvhdebug("linuxdvb", "%s - locked", buf); lfe->lfe_locked = 1; /* Start input */ tvh_pipe(O_NONBLOCK, &lfe->lfe_dvr_pipe); pthread_mutex_lock(&lfe->lfe_dvr_lock); tvhthread_create(&lfe->lfe_dvr_thread, NULL, linuxdvb_frontend_input_thread, lfe); pthread_cond_wait(&lfe->lfe_dvr_cond, &lfe->lfe_dvr_lock); pthread_mutex_unlock(&lfe->lfe_dvr_lock); /* Table handlers */ linuxdvb_frontend_default_tables(lfe, (dvb_mux_t*)mm); /* Locked - ensure everything is open */ pthread_mutex_lock(&lfe->mi_output_lock); RB_FOREACH(mp, &mm->mm_pids, mp_link) linuxdvb_frontend_open_pid0(lfe, mp); pthread_mutex_unlock(&lfe->mi_output_lock); /* Re-arm (quick) */ } else { gtimer_arm_ms(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor, lfe, 50); /* Monitor 1 per sec */ if (dispatch_clock < lfe->lfe_monitor) return; lfe->lfe_monitor = dispatch_clock + 1; } } /* Statistics - New API */ #if DVB_VER_ATLEAST(5,10) memset(&fe_properties, 0, sizeof(fe_properties)); /* Signal strength */ fe_properties[0].cmd = DTV_STAT_SIGNAL_STRENGTH; /* BER */ fe_properties[1].cmd = DTV_STAT_PRE_ERROR_BIT_COUNT; fe_properties[2].cmd = DTV_STAT_PRE_TOTAL_BIT_COUNT; /* SNR */ fe_properties[3].cmd = DTV_STAT_CNR; /* PER / UNC */ fe_properties[4].cmd = DTV_STAT_ERROR_BLOCK_COUNT; fe_properties[5].cmd = DTV_STAT_TOTAL_BLOCK_COUNT; dtv_prop.num = 6; dtv_prop.props = fe_properties; logit = tvhlog_limit(&lfe->lfe_status_log, 3600); if(ioctl_check(lfe, 0) && !ioctl(lfe->lfe_fe_fd, FE_GET_PROPERTY, &dtv_prop)) { /* Signal strength */ gotprop = 0; if(ioctl_check(lfe, 1) && fe_properties[0].u.st.len > 0) { if(fe_properties[0].u.st.stat[0].scale == FE_SCALE_RELATIVE) { mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.signal = fe_properties[0].u.st.stat[0].uvalue; gotprop = 1; } else if(fe_properties[0].u.st.stat[0].scale == FE_SCALE_DECIBEL) { mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_DECIBEL; mmi->mmi_stats.signal = fe_properties[0].u.st.stat[0].svalue; gotprop = 1; } else { ioctl_bad(lfe, 1); mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled signal scale: %d", fe_properties[0].u.st.stat[0].scale); } } if(!gotprop && ioctl_check(lfe, 2)) { /* try old API */ if (!ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16)) { mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.signal = u16; } else { ioctl_bad(lfe, 2); mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide signal strength value."); } } /* ERROR_BIT_COUNT */ gotprop = 0; if(ioctl_check(lfe, 3) && fe_properties[1].u.st.len > 0) { if(fe_properties[1].u.st.stat[0].scale == FE_SCALE_COUNTER) { mmi->mmi_stats.ec_bit = fe_properties[1].u.st.stat[0].uvalue; gotprop = 1; } else { ioctl_bad(lfe, 3); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled ERROR_BIT_COUNT scale: %d", fe_properties[1].u.st.stat[0].scale); } } /* TOTAL_BIT_COUNT */ if(gotprop && (fe_properties[2].u.st.len > 0)) { gotprop = 0; if(ioctl_check(lfe, 4) && fe_properties[2].u.st.stat[0].scale == FE_SCALE_COUNTER) { mmi->mmi_stats.tc_bit = fe_properties[2].u.st.stat[0].uvalue; gotprop = 1; } else { ioctl_bad(lfe, 4); mmi->mmi_stats.ec_bit = 0; /* both values or none */ if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled TOTAL_BIT_COUNT scale: %d", fe_properties[2].u.st.stat[0].scale); } } if(!gotprop && ioctl_check(lfe, 5)) { /* try old API */ if (!ioctl(lfe->lfe_fe_fd, FE_READ_BER, &u32)) mmi->mmi_stats.ber = u32; else { ioctl_bad(lfe, 5); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide BER value."); } } /* SNR */ gotprop = 0; if(ioctl_check(lfe, 6) && fe_properties[3].u.st.len > 0) { if(fe_properties[3].u.st.stat[0].scale == FE_SCALE_RELATIVE) { mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.snr = fe_properties[3].u.st.stat[0].uvalue; gotprop = 1; } else if(fe_properties[3].u.st.stat[0].scale == FE_SCALE_DECIBEL) { mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_DECIBEL; mmi->mmi_stats.snr = fe_properties[3].u.st.stat[0].svalue; gotprop = 1; } else { ioctl_bad(lfe, 6); mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled SNR scale: %d", fe_properties[3].u.st.stat[0].scale); } } if(!gotprop && ioctl_check(lfe, 7)) { /* try old API */ if (!ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16)) { mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.snr = u16; } else { ioctl_bad(lfe, 7); mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide SNR value."); } } /* ERROR_BLOCK_COUNT == Uncorrected blocks (UNC) */ gotprop = 0; if(ioctl_check(lfe, 8) && fe_properties[4].u.st.len > 0) { if(fe_properties[4].u.st.stat[0].scale == FE_SCALE_COUNTER) { mmi->mmi_stats.unc = mmi->mmi_stats.ec_block = fe_properties[4].u.st.stat[0].uvalue; gotprop = 1; } else { ioctl_bad(lfe, 8); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled ERROR_BLOCK_COUNT scale: %d", fe_properties[4].u.st.stat[0].scale); } } /* TOTAL_BLOCK_COUNT */ if(gotprop && (fe_properties[5].u.st.len > 0)) { gotprop = 0; if(ioctl_check(lfe, 9) && fe_properties[5].u.st.stat[0].scale == FE_SCALE_COUNTER) { mmi->mmi_stats.tc_block = fe_properties[5].u.st.stat[0].uvalue; gotprop = 1; } else { ioctl_bad(lfe, 9); mmi->mmi_stats.ec_block = mmi->mmi_stats.unc = 0; /* both values or none */ if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unhandled TOTAL_BLOCK_COUNT scale: %d", fe_properties[5].u.st.stat[0].scale); } } if(!gotprop && ioctl_check(lfe, 10)) { /* try old API */ if (!ioctl(lfe->lfe_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &u32)) { mmi->mmi_stats.unc = u32; gotprop = 1; } else { ioctl_bad(lfe, 10); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide UNC value."); } } /* Older API */ } else #endif { ioctl_bad(lfe, 0); if (ioctl_check(lfe, 1) && !ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16)) { mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.signal = u16; } else { ioctl_bad(lfe, 1); mmi->mmi_stats.signal_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide signal strength value."); } if (ioctl_check(lfe, 2) && !ioctl(lfe->lfe_fe_fd, FE_READ_BER, &u32)) mmi->mmi_stats.ber = u32; else { ioctl_bad(lfe, 2); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide BER value."); } if (ioctl_check(lfe, 3) && !ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16)) { mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_RELATIVE; mmi->mmi_stats.snr = u16; } else { ioctl_bad(lfe, 3); mmi->mmi_stats.snr_scale = SIGNAL_STATUS_SCALE_UNKNOWN; if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide SNR value."); } if (ioctl_check(lfe, 4) && !ioctl(lfe->lfe_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &u32)) mmi->mmi_stats.unc = u32; else { ioctl_bad(lfe, 4); if (logit) tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide UNC value."); } } /* Send message */ sigstat.status_text = signal2str(status); sigstat.snr = mmi->mmi_stats.snr; sigstat.signal = mmi->mmi_stats.signal; sigstat.ber = mmi->mmi_stats.ber; sigstat.unc = mmi->mmi_stats.unc; sigstat.signal_scale = mmi->mmi_stats.signal_scale; sigstat.snr_scale = mmi->mmi_stats.snr_scale; sigstat.ec_bit = mmi->mmi_stats.ec_bit; sigstat.tc_bit = mmi->mmi_stats.tc_bit; sigstat.ec_block = mmi->mmi_stats.ec_block; sigstat.tc_block = mmi->mmi_stats.tc_block; sm.sm_type = SMT_SIGNAL_STATUS; sm.sm_data = &sigstat; LIST_FOREACH(s, &lfe->mi_transports, s_active_link) { pthread_mutex_lock(&s->s_stream_mutex); streaming_pad_deliver(&s->s_streaming_pad, streaming_msg_clone(&sm)); pthread_mutex_unlock(&s->s_stream_mutex); } } static void * linuxdvb_frontend_input_thread ( void *aux ) { linuxdvb_frontend_t *lfe = aux; mpegts_mux_instance_t *mmi; int dvr = -1; char buf[256]; int nfds; tvhpoll_event_t ev[2]; tvhpoll_t *efd; ssize_t n; size_t skip = (MIN(lfe->lfe_skip_bytes, 1024*1024) / 188) * 188; size_t counter = 0; sbuf_t sb; /* Get MMI */ pthread_mutex_lock(&lfe->lfe_dvr_lock); lfe->mi_display_name((mpegts_input_t*)lfe, buf, sizeof(buf)); mmi = LIST_FIRST(&lfe->mi_mux_active); pthread_cond_signal(&lfe->lfe_dvr_cond); pthread_mutex_unlock(&lfe->lfe_dvr_lock); if (mmi == NULL) return NULL; /* Open DVR */ dvr = tvh_open(lfe->lfe_dvr_path, O_RDONLY | O_NONBLOCK, 0); if (dvr < 0) { tvherror("linuxdvb", "%s - failed to open %s", buf, lfe->lfe_dvr_path); return NULL; } /* Setup poll */ efd = tvhpoll_create(2); memset(ev, 0, sizeof(ev)); ev[0].events = TVHPOLL_IN; ev[0].fd = ev[0].data.fd = dvr; ev[1].events = TVHPOLL_IN; ev[1].fd = ev[1].data.fd = lfe->lfe_dvr_pipe.rd; tvhpoll_add(efd, ev, 2); /* Allocate memory */ sbuf_init_fixed(&sb, 18800); /* Read */ while (tvheadend_running) { nfds = tvhpoll_wait(efd, ev, 1, -1); if (nfds < 1) continue; if (ev[0].data.fd != dvr) break; /* Read */ if ((n = sbuf_read(&sb, dvr)) < 0) { if (ERRNO_AGAIN(errno)) continue; if (errno == EOVERFLOW) { tvhlog(LOG_WARNING, "linuxdvb", "%s - read() EOVERFLOW", buf); continue; } tvhlog(LOG_ERR, "linuxdvb", "%s - read() error %d (%s)", buf, errno, strerror(errno)); break; } /* Skip the initial bytes */ if (counter < skip) { counter += n; if (counter < skip) { sbuf_cut(&sb, n); } else { sbuf_cut(&sb, skip - (counter - n)); } } /* Process */ mpegts_input_recv_packets((mpegts_input_t*)lfe, mmi, &sb, NULL, NULL); } sbuf_free(&sb); tvhpoll_destroy(efd); close(dvr); return NULL; } /* ************************************************************************** * Tuning * *************************************************************************/ typedef struct linuxdvb_tbl { int t; ///< TVH internal value int l; ///< LinuxDVB API value } linuxdvb_tbl_t; #define TABLE_EOD -1 static int linuxdvb2tvh ( const char *prefix, linuxdvb_tbl_t *tbl, int key, int defval ) { while (tbl->t != TABLE_EOD) { if (tbl->l == key) return tbl->t; tbl++; } tvhtrace("linuxdvb", "%s - linuxdvb2tvh failed %d", prefix, key); return defval; } static int tvh2linuxdvb ( const char *prefix, linuxdvb_tbl_t *tbl, int key, int defval ) { while (tbl->t != TABLE_EOD) { if (tbl->t == key) return tbl->l; tbl++; } tvhtrace("linuxdvb", "%s - tvh2linuxdvb failed %d", prefix, key); return defval; } #define TOSTR(s) #s #define TR(s, t, d) tvh2linuxdvb(TOSTR(s), t, dmc->dmc_fe_##s , d) #define TRU(s, t, d) tvh2linuxdvb(TOSTR(s), t, dmc->u.dmc_fe_##s , d) #if DVB_API_VERSION >= 5 static linuxdvb_tbl_t delsys_tbl[] = { { .t = DVB_SYS_DVBC_ANNEX_B, .l = SYS_DVBC_ANNEX_B }, #if DVB_VER_ATLEAST(5,6) { .t = DVB_SYS_DVBC_ANNEX_A, .l = SYS_DVBC_ANNEX_A }, { .t = DVB_SYS_DVBC_ANNEX_C, .l = SYS_DVBC_ANNEX_C }, #else { .t = DVB_SYS_DVBC_ANNEX_A, .l = SYS_DVBC_ANNEX_AC }, { .t = DVB_SYS_DVBC_ANNEX_C, .l = SYS_DVBC_ANNEX_AC }, #endif { .t = DVB_SYS_DVBT, .l = SYS_DVBT }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_SYS_DVBT2, .l = SYS_DVBT2 }, #endif { .t = DVB_SYS_DVBS, .l = SYS_DVBS }, { .t = DVB_SYS_DVBS2, .l = SYS_DVBS2 }, { .t = DVB_SYS_DVBH, .l = SYS_DVBH }, #if DVB_VER_ATLEAST(5,1) { .t = DVB_SYS_DSS, .l = SYS_DSS }, #endif { .t = DVB_SYS_ISDBT, .l = SYS_ISDBT }, { .t = DVB_SYS_ISDBS, .l = SYS_ISDBS }, { .t = DVB_SYS_ISDBC, .l = SYS_ISDBC }, { .t = DVB_SYS_ATSC, .l = SYS_ATSC }, { .t = DVB_SYS_ATSCMH, .l = SYS_ATSCMH }, #if DVB_VER_ATLEAST(5,7) { .t = DVB_SYS_DTMB, .l = SYS_DTMB }, #endif { .t = DVB_SYS_CMMB, .l = SYS_CMMB }, { .t = DVB_SYS_DAB, .l = SYS_DAB }, #if DVB_VER_ATLEAST(5,4) { .t = DVB_SYS_TURBO, .l = SYS_TURBO }, #endif { .t = TABLE_EOD } }; int linuxdvb2tvh_delsys ( int delsys ) { return linuxdvb2tvh("delsys", delsys_tbl, delsys, DVB_SYS_NONE); } #endif int linuxdvb_frontend_tune0 ( linuxdvb_frontend_t *lfe, mpegts_mux_instance_t *mmi, uint32_t freq ) { static linuxdvb_tbl_t inv_tbl[] = { { .t = DVB_INVERSION_AUTO, .l = INVERSION_AUTO }, { .t = DVB_INVERSION_OFF, .l = INVERSION_OFF }, { .t = DVB_INVERSION_ON, .l = INVERSION_ON }, { .t = TABLE_EOD } }; static linuxdvb_tbl_t bw_tbl[] = { { .t = DVB_BANDWIDTH_AUTO, .l = BANDWIDTH_AUTO }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_BANDWIDTH_1_712_MHZ, .l = BANDWIDTH_1_712_MHZ }, { .t = DVB_BANDWIDTH_5_MHZ, .l = BANDWIDTH_5_MHZ }, #endif { .t = DVB_BANDWIDTH_6_MHZ, .l = BANDWIDTH_6_MHZ }, { .t = DVB_BANDWIDTH_7_MHZ, .l = BANDWIDTH_7_MHZ }, { .t = DVB_BANDWIDTH_8_MHZ, .l = BANDWIDTH_8_MHZ }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_BANDWIDTH_10_MHZ, .l = BANDWIDTH_10_MHZ }, #endif { .t = TABLE_EOD } }; static linuxdvb_tbl_t fec_tbl[] = { { .t = DVB_FEC_NONE, .l = FEC_NONE }, { .t = DVB_FEC_AUTO, .l = FEC_AUTO }, { .t = DVB_FEC_1_2, .l = FEC_1_2 }, { .t = DVB_FEC_2_3, .l = FEC_2_3 }, #if DVB_VER_ATLEAST(5,7) { .t = DVB_FEC_2_5, .l = FEC_2_5 }, #endif { .t = DVB_FEC_3_4, .l = FEC_3_4 }, #if DVB_VER_ATLEAST(5,0) { .t = DVB_FEC_3_5, .l = FEC_3_5 }, #endif { .t = DVB_FEC_4_5, .l = FEC_4_5 }, { .t = DVB_FEC_5_6, .l = FEC_5_6 }, { .t = DVB_FEC_6_7, .l = FEC_6_7 }, { .t = DVB_FEC_7_8, .l = FEC_7_8 }, { .t = DVB_FEC_8_9, .l = FEC_8_9 }, #if DVB_VER_ATLEAST(5,0) { .t = DVB_FEC_9_10, .l = FEC_9_10 }, #endif { .t = TABLE_EOD } }; static linuxdvb_tbl_t mod_tbl[] = { { .t = DVB_MOD_AUTO, .l = QAM_AUTO }, { .t = DVB_MOD_QPSK, .l = QPSK }, { .t = DVB_MOD_QAM_16, .l = QAM_16 }, { .t = DVB_MOD_QAM_32, .l = QAM_32 }, { .t = DVB_MOD_QAM_64, .l = QAM_64 }, { .t = DVB_MOD_QAM_128, .l = QAM_128 }, { .t = DVB_MOD_QAM_256, .l = QAM_256 }, { .t = DVB_MOD_QAM_AUTO, .l = QAM_AUTO }, { .t = DVB_MOD_VSB_8, .l = VSB_8 }, { .t = DVB_MOD_VSB_16, .l = VSB_16 }, #if DVB_VER_ATLEAST(5,1) { .t = DVB_MOD_PSK_8, .l = PSK_8, }, { .t = DVB_MOD_APSK_16, .l = APSK_16 }, { .t = DVB_MOD_APSK_32, .l = APSK_32 }, #endif #if DVB_VER_ATLEAST(5,0) { .t = DVB_MOD_DQPSK, .l = DQPSK }, #endif #if DVB_VER_ATLEAST(5,7) { .t = DVB_MOD_QAM_4_NR, .l = QAM_4_NR }, #endif { .t = TABLE_EOD } }; static linuxdvb_tbl_t trans_tbl[] = { { .t = DVB_TRANSMISSION_MODE_AUTO, .l = TRANSMISSION_MODE_AUTO }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_TRANSMISSION_MODE_1K, .l = TRANSMISSION_MODE_1K }, #endif { .t = DVB_TRANSMISSION_MODE_2K, .l = TRANSMISSION_MODE_2K }, #if DVB_VER_ATLEAST(5,1) { .t = DVB_TRANSMISSION_MODE_4K, .l = TRANSMISSION_MODE_4K }, #endif { .t = DVB_TRANSMISSION_MODE_8K, .l = TRANSMISSION_MODE_8K }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_TRANSMISSION_MODE_16K, .l = TRANSMISSION_MODE_16K }, { .t = DVB_TRANSMISSION_MODE_32K, .l = TRANSMISSION_MODE_32K }, #endif #if DVB_VER_ATLEAST(5,7) { .t = DVB_TRANSMISSION_MODE_C3780, .l = TRANSMISSION_MODE_C3780 }, { .t = DVB_TRANSMISSION_MODE_C1, .l = TRANSMISSION_MODE_C1 }, #endif { .t = TABLE_EOD } }; static linuxdvb_tbl_t guard_tbl[] = { { .t = DVB_GUARD_INTERVAL_AUTO, .l = GUARD_INTERVAL_AUTO }, { .t = DVB_GUARD_INTERVAL_1_4, .l = GUARD_INTERVAL_1_4 }, { .t = DVB_GUARD_INTERVAL_1_8, .l = GUARD_INTERVAL_1_8 }, { .t = DVB_GUARD_INTERVAL_1_16, .l = GUARD_INTERVAL_1_16 }, { .t = DVB_GUARD_INTERVAL_1_32, .l = GUARD_INTERVAL_1_32 }, #if DVB_VER_ATLEAST(5,3) { .t = DVB_GUARD_INTERVAL_1_128, .l = GUARD_INTERVAL_1_128 }, { .t = DVB_GUARD_INTERVAL_19_128, .l = GUARD_INTERVAL_19_128 }, { .t = DVB_GUARD_INTERVAL_19_256, .l = GUARD_INTERVAL_19_256 }, #endif { .t = TABLE_EOD } }; static linuxdvb_tbl_t h_tbl[] = { { .t = DVB_HIERARCHY_NONE, .l = HIERARCHY_NONE }, { .t = DVB_HIERARCHY_AUTO, .l = HIERARCHY_AUTO }, { .t = DVB_HIERARCHY_1, .l = HIERARCHY_1 }, { .t = DVB_HIERARCHY_2, .l = HIERARCHY_2 }, { .t = DVB_HIERARCHY_4, .l = HIERARCHY_4 }, { .t = TABLE_EOD } }; #if DVB_API_VERSION >= 5 static linuxdvb_tbl_t pilot_tbl[] = { { .t = DVB_PILOT_AUTO, .l = PILOT_AUTO }, { .t = DVB_PILOT_ON, .l = PILOT_ON }, { .t = DVB_PILOT_OFF, .l = PILOT_OFF }, { .l = TABLE_EOD } }; static linuxdvb_tbl_t rolloff_tbl[] = { { .t = DVB_HIERARCHY_AUTO, .l = ROLLOFF_AUTO }, { .t = DVB_ROLLOFF_20, .l = ROLLOFF_20 }, { .t = DVB_ROLLOFF_25, .l = ROLLOFF_25 }, { .t = DVB_ROLLOFF_35, .l = ROLLOFF_35 }, { .t = TABLE_EOD }, }; #endif int r; struct dvb_frontend_event ev; char buf1[256]; dvb_mux_t *lm = (dvb_mux_t*)mmi->mmi_mux; dvb_mux_conf_t *dmc; struct dvb_frontend_parameters p; /* Open FE */ lfe->mi_display_name((mpegts_input_t*)lfe, buf1, sizeof(buf1)); if (lfe->lfe_fe_fd <= 0) { lfe->lfe_fe_fd = tvh_open(lfe->lfe_fe_path, O_RDWR | O_NONBLOCK, 0); tvhtrace("linuxdvb", "%s - opening FE %s (%d)", buf1, lfe->lfe_fe_path, lfe->lfe_fe_fd); if (lfe->lfe_fe_fd <= 0) { return SM_CODE_TUNING_FAILED; } } lfe->lfe_locked = 0; lfe->lfe_status = 0; /* * copy the universal parameters to the Linux kernel structure */ dmc = &lm->lm_tuning; #if ENABLE_TRACE { char buf2[256]; dvb_mux_conf_str(&lm->lm_tuning, buf2, sizeof(buf2)); tvhtrace("linuxdvb", "tuner %s tunning to %s (freq %i)", buf1, buf2, freq); } #endif memset(&p, 0, sizeof(p)); p.frequency = dmc->dmc_fe_freq; p.inversion = TR(inversion, inv_tbl, INVERSION_AUTO); switch (dmc->dmc_fe_type) { case DVB_TYPE_T: #define _OFDM(xyz) p.u.ofdm.xyz _OFDM(bandwidth) = TRU(ofdm.bandwidth, bw_tbl, BANDWIDTH_AUTO); _OFDM(code_rate_HP) = TRU(ofdm.code_rate_HP, fec_tbl, FEC_AUTO); _OFDM(code_rate_LP) = TRU(ofdm.code_rate_LP, fec_tbl, FEC_AUTO); _OFDM(constellation) = TR(modulation, mod_tbl, QAM_AUTO); _OFDM(transmission_mode) = TRU(ofdm.transmission_mode, trans_tbl, TRANSMISSION_MODE_AUTO); _OFDM(guard_interval) = TRU(ofdm.guard_interval, guard_tbl, GUARD_INTERVAL_AUTO); _OFDM(hierarchy_information) = TRU(ofdm.hierarchy_information, h_tbl, HIERARCHY_AUTO); #undef _OFDM break; case DVB_TYPE_C: p.u.qam.symbol_rate = dmc->u.dmc_fe_qam.symbol_rate; p.u.qam.fec_inner = TRU(qam.fec_inner, fec_tbl, FEC_AUTO); p.u.qam.modulation = TR(modulation, mod_tbl, QAM_AUTO); break; case DVB_TYPE_S: p.u.qpsk.symbol_rate = dmc->u.dmc_fe_qpsk.symbol_rate; p.u.qpsk.fec_inner = TRU(qpsk.fec_inner, fec_tbl, FEC_AUTO); break; case DVB_TYPE_ATSC: p.u.vsb.modulation = TR(modulation, mod_tbl, QAM_AUTO); break; default: tvherror("linuxdvb", "%s - unknown FE type %d", buf1, dmc->dmc_fe_type); return SM_CODE_TUNING_FAILED; } if (freq != (uint32_t)-1) p.frequency = freq; if (dmc->dmc_fe_type != lfe->lfe_type) { tvherror("linuxdvb", "%s - failed to tune [type does not match %i != %i]", buf1, dmc->dmc_fe_type, lfe->lfe_type); return SM_CODE_TUNING_FAILED; } /* S2 tuning */ #if DVB_API_VERSION >= 5 struct dtv_property cmds[20]; struct dtv_properties cmdseq; /* Clear Q */ static struct dtv_property clear_p[] = { { .cmd = DTV_CLEAR }, }; static struct dtv_properties clear_cmdseq = { .num = 1, .props = clear_p }; if ((ioctl(lfe->lfe_fe_fd, FE_SET_PROPERTY, &clear_cmdseq)) != 0) { tvherror("linuxdvb", "%s - DTV_CLEAR failed [e=%s]", buf1, strerror(errno)); return -1; } if (freq == (uint32_t)-1) freq = p.frequency; memset(&cmdseq, 0, sizeof(cmdseq)); cmdseq.props = cmds; memset(&cmds, 0, sizeof(cmds)); /* Tune */ #define S2CMD(c, d)\ cmds[cmdseq.num].cmd = c;\ cmds[cmdseq.num++].u.data = d memset(&cmds, 0, sizeof(cmds)); S2CMD(DTV_DELIVERY_SYSTEM, TR(delsys, delsys_tbl, SYS_UNDEFINED)); S2CMD(DTV_FREQUENCY, freq); S2CMD(DTV_INVERSION, p.inversion); /* DVB-T */ if (lfe->lfe_type == DVB_TYPE_T) { S2CMD(DTV_BANDWIDTH_HZ, dvb_bandwidth(dmc->u.dmc_fe_ofdm.bandwidth)); #if DVB_VER_ATLEAST(5,1) S2CMD(DTV_CODE_RATE_HP, p.u.ofdm.code_rate_HP); S2CMD(DTV_CODE_RATE_LP, p.u.ofdm.code_rate_LP); #endif S2CMD(DTV_MODULATION, p.u.ofdm.constellation); #if DVB_VER_ATLEAST(5,1) S2CMD(DTV_TRANSMISSION_MODE, p.u.ofdm.transmission_mode); S2CMD(DTV_GUARD_INTERVAL, p.u.ofdm.guard_interval); S2CMD(DTV_HIERARCHY, p.u.ofdm.hierarchy_information); #endif /* DVB-C */ } else if (lfe->lfe_type == DVB_TYPE_C) { S2CMD(DTV_SYMBOL_RATE, p.u.qam.symbol_rate); S2CMD(DTV_MODULATION, p.u.qam.modulation); S2CMD(DTV_INNER_FEC, p.u.qam.fec_inner); /* DVB-S */ } else if (lfe->lfe_type == DVB_TYPE_S) { S2CMD(DTV_SYMBOL_RATE, p.u.qpsk.symbol_rate); S2CMD(DTV_INNER_FEC, p.u.qpsk.fec_inner); S2CMD(DTV_PILOT, TR(pilot, pilot_tbl, PILOT_AUTO)); S2CMD(DTV_MODULATION, TR(modulation, mod_tbl, QPSK)); if (lm->lm_tuning.dmc_fe_delsys == DVB_SYS_DVBS) { S2CMD(DTV_ROLLOFF, ROLLOFF_35); } else { S2CMD(DTV_ROLLOFF, TR(rolloff, rolloff_tbl, ROLLOFF_AUTO)); } /* ATSC */ } else { S2CMD(DTV_MODULATION, p.u.vsb.modulation); } /* Tune */ S2CMD(DTV_TUNE, 0); #undef S2CMD #endif /* discard stale events */ while (1) { if (ioctl(lfe->lfe_fe_fd, FE_GET_EVENT, &ev) == -1) break; } /* S2 tuning */ #if DVB_API_VERSION >= 5 #if ENABLE_TRACE int i; for (i = 0; i < cmdseq.num; i++) tvhtrace("linuxdvb", "S2CMD %02u => %u", cmds[i].cmd, cmds[i].u.data); #endif r = ioctl(lfe->lfe_fe_fd, FE_SET_PROPERTY, &cmdseq); /* v3 tuning */ #else r = ioctl(lfe->lfe_fe_fd, FE_SET_FRONTEND, p); #endif /* Failed */ if (r != 0) { tvherror("linuxdvb", "%s - failed to tune [e=%s]", buf1, strerror(errno)); if (errno == EINVAL) mmi->mmi_tune_failed = 1; return SM_CODE_TUNING_FAILED; } return r; } int linuxdvb_frontend_tune1 ( linuxdvb_frontend_t *lfe, mpegts_mux_instance_t *mmi, uint32_t freq ) { int r; char buf1[256], buf2[256]; lfe->mi_display_name((mpegts_input_t*)lfe, buf1, sizeof(buf1)); mpegts_mux_nice_name(mmi->mmi_mux, buf2, sizeof(buf2)); tvhdebug("linuxdvb", "%s - starting %s", buf1, buf2); /* Tune */ tvhtrace("linuxdvb", "%s - tuning", buf1); r = linuxdvb_frontend_tune0(lfe, mmi, freq); /* Start monitor */ if (!r) { time(&lfe->lfe_monitor); lfe->lfe_monitor += 4; gtimer_arm_ms(&lfe->lfe_monitor_timer, linuxdvb_frontend_monitor, lfe, 50); lfe->lfe_ready = 1; } lfe->lfe_in_setup = 0; return r; } /* ************************************************************************** * Creation/Config * *************************************************************************/ linuxdvb_frontend_t * linuxdvb_frontend_create ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *fe_path, const char *dmx_path, const char *dvr_path, dvb_fe_type_t type, const char *name ) { const idclass_t *idc; const char *str, *uuid = NULL, *scuuid = NULL, *sctype = NULL; char id[12], lname[256]; linuxdvb_frontend_t *lfe; htsmsg_t *scconf = NULL; /* Internal config ID */ snprintf(id, sizeof(id), "%s #%d", dvb_type2str(type), number); if (conf) conf = htsmsg_get_map(conf, id); if (conf) uuid = htsmsg_get_str(conf, "uuid"); /* Fudge configuration for old network entry */ if (conf) { if (!htsmsg_get_list(conf, "networks") && (str = htsmsg_get_str(conf, "network"))) { htsmsg_t *l = htsmsg_create_list(); htsmsg_add_str(l, NULL, str); htsmsg_add_msg(conf, "networks", l); } } /* Class */ if (type == DVB_TYPE_S) idc = &linuxdvb_frontend_dvbs_class; else if (type == DVB_TYPE_C) idc = &linuxdvb_frontend_dvbc_class; else if (type == DVB_TYPE_T) idc = &linuxdvb_frontend_dvbt_class; else if (type == DVB_TYPE_ATSC) idc = &linuxdvb_frontend_atsc_class; else { tvherror("linuxdvb", "unknown FE type %d", type); return NULL; } /* Create */ // Note: there is a bit of a chicken/egg issue below, without the // correct "fe_type" we cannot set the network (which is done // in mpegts_input_create()). So we must set early. lfe = calloc(1, sizeof(linuxdvb_frontend_t)); lfe->lfe_number = number; lfe->lfe_type = type; strncpy(lfe->lfe_name, name, sizeof(lfe->lfe_name)); lfe->lfe_name[sizeof(lfe->lfe_name)-1] = '\0'; lfe = (linuxdvb_frontend_t*)mpegts_input_create0((mpegts_input_t*)lfe, idc, uuid, conf); if (!lfe) return NULL; /* Callbacks */ lfe->mi_is_free = linuxdvb_frontend_is_free; lfe->mi_get_weight = linuxdvb_frontend_get_weight; lfe->mi_get_priority = linuxdvb_frontend_get_priority; lfe->mi_get_grace = linuxdvb_frontend_get_grace; /* Default name */ if (!lfe->mi_name) { snprintf(lname, sizeof(lname), "%s : %s", name, id); lfe->mi_name = strdup(lname); } /* Set paths */ lfe->lfe_fe_path = strdup(fe_path); lfe->lfe_dmx_path = strdup(dmx_path); lfe->lfe_dvr_path = strdup(dvr_path); /* Input callbacks */ lfe->mi_is_enabled = linuxdvb_frontend_is_enabled; lfe->mi_start_mux = linuxdvb_frontend_start_mux; lfe->mi_stop_mux = linuxdvb_frontend_stop_mux; lfe->mi_network_list = linuxdvb_frontend_network_list; lfe->mi_open_pid = linuxdvb_frontend_open_pid; lfe->mi_enabled_updated = linuxdvb_frontend_enabled_updated; /* Adapter link */ lfe->lfe_adapter = la; LIST_INSERT_HEAD(&la->la_frontends, lfe, lfe_link); /* DVR lock/cond */ pthread_mutex_init(&lfe->lfe_dvr_lock, NULL); pthread_cond_init(&lfe->lfe_dvr_cond, NULL); /* Satconf */ if (conf) { if ((scconf = htsmsg_get_map(conf, "satconf"))) { sctype = htsmsg_get_str(scconf, "type"); scuuid = htsmsg_get_str(scconf, "uuid"); } } /* Create satconf */ if (lfe->lfe_type == DVB_TYPE_S && !lfe->lfe_satconf) lfe->lfe_satconf = linuxdvb_satconf_create(lfe, sctype, scuuid, scconf); /* Double check enabled */ linuxdvb_frontend_enabled_updated((mpegts_input_t*)lfe); return lfe; } void linuxdvb_frontend_save ( linuxdvb_frontend_t *lfe, htsmsg_t *fe ) { char id[12]; htsmsg_t *m = htsmsg_create_map(); /* Save frontend */ mpegts_input_save((mpegts_input_t*)lfe, m); htsmsg_add_str(m, "type", dvb_type2str(lfe->lfe_type)); if (lfe->lfe_satconf) { htsmsg_t *s = htsmsg_create_map(); linuxdvb_satconf_save(lfe->lfe_satconf, s); htsmsg_add_str(s, "uuid", idnode_uuid_as_str(&lfe->lfe_satconf->ls_id)); htsmsg_add_msg(m, "satconf", s); } /* Add to list */ snprintf(id, sizeof(id), "%s #%d", dvb_type2str(lfe->lfe_type), lfe->lfe_number); htsmsg_add_msg(fe, id, m); } void linuxdvb_frontend_delete ( linuxdvb_frontend_t *lfe ) { lock_assert(&global_lock); /* Ensure we're stopped */ mpegts_input_stop_all((mpegts_input_t*)lfe); /* Stop monitor */ gtimer_disarm(&lfe->lfe_monitor_timer); /* Close FDs */ if (lfe->lfe_fe_fd > 0) close(lfe->lfe_fe_fd); /* Remove from adapter */ LIST_REMOVE(lfe, lfe_link); /* Free memory */ free(lfe->lfe_fe_path); free(lfe->lfe_dmx_path); free(lfe->lfe_dvr_path); /* Delete satconf */ if (lfe->lfe_satconf) linuxdvb_satconf_delete(lfe->lfe_satconf, 0); /* Finish */ mpegts_input_delete((mpegts_input_t*)lfe, 0); } /****************************************************************************** * Editor Configuration * * vim:sts=2:ts=2:sw=2:et *****************************************************************************/