[PR-206] dvb: extend the multi-frontend PR to include cleaner support.

Generally this is intended to be used such that a user can select which
frontend they wish to enable. However in theory with clever configuration,
configure one adapter then the other and enabling close fds and disabling
idle scan, it might be possible to use both frontends.

But since TVH doesn't do any form of arbitration strange errors might
occur if you try to access both frontends at the same time (which for
most such adapters is not possible).
This commit is contained in:
Adam Sutton 2013-01-07 13:42:47 +00:00
parent f1e4c480ec
commit ac5db4e22f
3 changed files with 194 additions and 143 deletions

View file

@ -229,6 +229,7 @@ typedef struct th_dvb_adapter {
char *tda_demux_path;
char *tda_dvr_path;
pthread_t tda_dvr_thread;
th_pipe_t tda_dvr_pipe;
@ -344,7 +345,7 @@ void dvb_adapter_stop (th_dvb_adapter_t *tda);
void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s);
void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled);
void dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on);
void dvb_adapter_set_auto_discovery(th_dvb_adapter_t *tda, int on);

View file

@ -24,6 +24,8 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
@ -49,6 +51,7 @@
struct th_dvb_adapter_queue dvb_adapters;
struct th_dvb_mux_instance_tree dvb_muxes;
static void *dvb_adapter_input_dvr(void *aux);
static void tda_init(th_dvb_adapter_t *tda);
/**
@ -81,6 +84,12 @@ tda_save(th_dvb_adapter_t *tda)
lock_assert(&global_lock);
htsmsg_add_u32(m, "enabled", tda->tda_enabled);
if (tda->tda_fe_path)
htsmsg_add_str(m, "fe_path", tda->tda_fe_path);
if (tda->tda_demux_path)
htsmsg_add_str(m, "dmx_path", tda->tda_demux_path);
if (tda->tda_dvr_path)
htsmsg_add_str(m, "dvr_path", tda->tda_dvr_path);
htsmsg_add_str(m, "type", dvb_adaptertype_to_str(tda->tda_type));
htsmsg_add_str(m, "displayname", tda->tda_displayname);
htsmsg_add_u32(m, "autodiscovery", tda->tda_autodiscovery);
@ -101,6 +110,31 @@ tda_save(th_dvb_adapter_t *tda)
htsmsg_destroy(m);
}
/**
*
*/
void
dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int on)
{
if(tda->tda_enabled == on)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" %s",
tda->tda_displayname, on ? "Enabled" : "Disabled");
tda->tda_enabled = on;
tda_save(tda);
if (!on) {
gtimer_disarm(&tda->tda_mux_scanner_timer);
if (tda->tda_mux_current)
dvb_fe_stop(tda->tda_mux_current, 0);
dvb_adapter_stop(tda);
} else {
tda_init(tda);
}
}
/**
*
@ -124,26 +158,6 @@ dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s)
dvb_adapter_notify(tda);
}
/**
*
*/
void
dvb_adapter_set_enabled(th_dvb_adapter_t *tda, uint32_t enabled)
{
if(tda->tda_enabled == enabled)
return;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "dvb", "Adapter \"%s\" enabled set to \"%s\"",
tda->tda_displayname, enabled ? "Enabled" : "Disabled");
tda->tda_enabled = enabled;
tda_save(tda);
}
/**
*
*/
@ -465,88 +479,113 @@ check_full_stream(th_dvb_adapter_t *tda)
*
*/
static void
tda_add(int adapter_num, int frontend_num, int demux_num)
tda_add(int adapter_num)
{
char path[200], fname[256];
int fe, i, r;
char buf[1024], path[200], fepath[256], dmxpath[256], dvrpath[256];
int i, j, fd;
th_dvb_adapter_t *tda;
char buf[400];
struct dvb_frontend_info fe_info;
DIR *dirp;
/* Check valid adapter */
snprintf(path, sizeof(path), "/dev/dvb/adapter%d", adapter_num);
snprintf(fname, sizeof(fname), "%s/frontend%d", path, frontend_num);
fe = tvh_open(fname, O_RDWR | O_NONBLOCK, 0);
if(fe == -1) {
if(errno != ENOENT)
dirp = opendir(path);
if (!dirp)
return;
/* Check each frontend */
// Note: this algo will fail if there are really exotic variations
// of tuners and demuxers etc... but you can always manually
// override the config
for (i = 0; i < 32; i++) {
/* Open/Query frontend */
snprintf(fepath, sizeof(fepath), "%s/frontend%d", path, i);
fd = tvh_open(fepath, O_RDONLY, 0);
if (fd == -1) {
if (errno != ENOENT)
tvhlog(LOG_ALERT, "dvb",
"%s: unable to open (err=%s)", fepath, strerror(errno));
continue;
}
if (ioctl(fd, FE_GET_INFO, &fe_info)) {
tvhlog(LOG_ALERT, "dvb",
"Unable to open %s -- %s", fname, strerror(errno));
return;
"%s: unable to query (err=%s)", fepath, strerror(errno));
close(fd);
continue;
}
close(fd);
/* Find Demux */
snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, i);
fd = tvh_open(dmxpath, O_RDONLY, 0);
if (fd == -1) {
snprintf(dmxpath, sizeof(dmxpath), "%s/demux%d", path, 0);
fd = tvh_open(dmxpath, O_RDONLY, 0);
}
if (fd == -1) {
tvhlog(LOG_ALERT, "dvb", "%s: unable to find demux", fepath);
continue;
}
close(fd);
/* Find DVR */
snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, i);
fd = tvh_open(dvrpath, O_RDONLY, 0);
if (fd == -1) {
snprintf(dvrpath, sizeof(dvrpath), "%s/dvr%d", path, 0);
fd = tvh_open(dvrpath, O_RDONLY, 0);
}
if (fd == -1) {
tvhlog(LOG_ALERT, "dvb", "%s: unable to find dvr", fepath);
continue;
}
close(fd);
/* Create entry */
tda = tda_alloc();
tda->tda_adapter_num = adapter_num;
tda->tda_rootpath = strdup(path);
tda->tda_fe_path = strdup(fepath);
tda->tda_demux_path = strdup(dmxpath);
tda->tda_dvr_path = strdup(dvrpath);
tda->tda_fe_fd = -1;
tda->tda_dvr_pipe.rd = -1;
tda->tda_full_mux_rx = -1;
tda->tda_type = fe_info.type;
tda->tda_autodiscovery = tda->tda_type != FE_QPSK;
tda->tda_idlescan = 1;
tda->tda_sat = tda->tda_type == FE_QPSK;
tda->tda_displayname = strdup(fe_info.name);
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
memcpy(tda->tda_fe_info, &fe_info, sizeof(struct dvb_frontend_info));
/* ID the device (for configuration etc..) */
// Note: would be better to include frontend dev in name, but that
// would require additional work to migrate config
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
for(j = 0; j < strlen(buf); j++)
if(!isalnum((int)buf[j]))
buf[j] = '_';
tda->tda_identifier = strdup(buf);
/* Check device connection */
dvb_adapter_checkspeed(tda);
/* Adapters known to provide valid SNR */
if(strcasestr(fe_info.name, "Sony CXD2820R"))
tda->tda_snr_valid = 1;
/* Store */
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name,
hostconnection2str(tda->tda_hostconnection),
tda->tda_snr_valid ? ", Reports valid SNR values" : "");
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
}
tda = tda_alloc();
tda->tda_adapter_num = adapter_num;
tda->tda_rootpath = strdup(path);
tda->tda_demux_path = malloc(256);
snprintf(tda->tda_demux_path, 256, "%s/demux%d", path, demux_num);
tda->tda_fe_path = strdup(fname);
tda->tda_fe_fd = -1;
tda->tda_dvr_pipe.rd = -1;
tda->tda_full_mux_rx = -1;
tda->tda_enabled = 0;
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
if(ioctl(fe, FE_GET_INFO, tda->tda_fe_info)) {
tvhlog(LOG_ALERT, "dvb", "%s: Unable to query adapter", fname);
close(fe);
free(tda);
return;
}
close(fe);
fe = -1;
tda->tda_type = tda->tda_fe_info->type;
snprintf(buf, sizeof(buf), "%s_%s", tda->tda_rootpath,
tda->tda_fe_info->name);
r = strlen(buf);
for(i = 0; i < r; i++)
if(!isalnum((int)buf[i]))
buf[i] = '_';
tda->tda_identifier = strdup(buf);
tda->tda_autodiscovery = tda->tda_type != FE_QPSK;
tda->tda_idlescan = 1;
tda->tda_sat = tda->tda_type == FE_QPSK;
/* Come up with an initial displayname, user can change it and it will
be overridden by any stored settings later on */
tda->tda_displayname = strdup(tda->tda_fe_info->name);
dvb_adapter_checkspeed(tda);
if(strcasestr(tda->tda_fe_info->name, "Sony CXD2820R"))
tda->tda_snr_valid = 1;
tvhlog(LOG_INFO, "dvb",
"Found adapter %s (%s) via %s%s", path, tda->tda_fe_info->name,
hostconnection2str(tda->tda_hostconnection),
tda->tda_snr_valid ? ", Reports valid SNR values" : "");
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
}
/**
*
*/
@ -592,21 +631,28 @@ tda_add_from_file(const char *filename)
}
/**
* Initiliase input
* Initiliase
*/
static void tda_init_input (th_dvb_adapter_t *tda)
static void tda_init (th_dvb_adapter_t *tda)
{
if(tda->tda_type == -1 || check_full_stream(tda)) {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath);
dvb_input_raw_setup(tda);
} else {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath);
dvb_input_filtered_setup(tda);
/* Disabled - ignore */
if (!tda->tda_enabled || !tda->tda_rootpath) return;
/* Initiliase input mode */
if(!tda->tda_open_service) {
if(tda->tda_type == -1 || check_full_stream(tda)) {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in full mux mode", tda->tda_rootpath);
dvb_input_raw_setup(tda);
} else {
tvhlog(LOG_INFO, "dvb", "Adapter %s will run in filtered mode", tda->tda_rootpath);
dvb_input_filtered_setup(tda);
}
}
/* Enable mux scanner */
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 1);
}
/**
*
*/
@ -641,7 +687,7 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda )
dvb_adapter_poweroff(tda);
/* Don't stop/close */
if (!tda->tda_idleclose) return;
if (!tda->tda_idleclose && tda->tda_enabled) return;
/* Close front end */
if (tda->tda_fe_fd != -1) {
@ -661,6 +707,8 @@ dvb_adapter_stop ( th_dvb_adapter_t *tda )
tda->tda_dvr_pipe.rd = -1;
tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath);
}
dvb_adapter_notify(tda);
}
/**
@ -671,8 +719,8 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile)
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
const char *name, *s;
int i, j, type;
const char *s;
int i, type;
uint32_t u32;
th_dvb_adapter_t *tda;
@ -681,54 +729,52 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile)
/* Initialise hardware */
for(i = 0; i < 32; i++)
if ((1 << i) & adapter_mask)
for(j = 0; j < 32; j++)
tda_add(i, j, 0);
tda_add(i);
/* Initialise rawts test file */
if(rawfile)
tda_add_from_file(rawfile);
/* Load configuration */
l = hts_settings_load("dvbadapters");
if(l != NULL) {
if ((l = hts_settings_load("dvbadapters")) != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
name = htsmsg_get_str(c, "displayname");
if((s = htsmsg_get_str(c, "type")) == NULL ||
(type = dvb_str_to_adaptertype(s)) < 0)
continue;
// TODO: do we really want to do this? useful for debug?
if((tda = dvb_adapter_find_by_identifier(f->hmf_name)) == NULL) {
/* Not discovered by hardware, create it */
tda = tda_alloc();
tda = tda_alloc();
tda->tda_identifier = strdup(f->hmf_name);
tda->tda_type = type;
tda->tda_enabled = 0;
tda->tda_type = type;
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
} else {
if(type != tda->tda_type)
continue; /* Something is wrong, ignore */
}
free(tda->tda_displayname);
tda->tda_displayname = strdup(name);
tvh_str_update(&tda->tda_displayname, htsmsg_get_str(c, "displayname"));
tvh_str_update(&tda->tda_dvr_path, htsmsg_get_str(c, "dvr_path"));
tvh_str_update(&tda->tda_demux_path, htsmsg_get_str(c, "dmx_path"));
htsmsg_get_u32(c, "enabled", &tda->tda_enabled);
htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery);
htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan);
htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose);
htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr);
htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan);
htsmsg_get_u32(c, "qmon", &tda->tda_qmon);
htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff);
htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid);
htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version);
htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats);
htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority);
htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan);
if (htsmsg_get_u32(c, "enabled", &tda->tda_enabled))
tda->tda_enabled = 1;
htsmsg_get_u32(c, "autodiscovery", &tda->tda_autodiscovery);
htsmsg_get_u32(c, "idlescan", &tda->tda_idlescan);
htsmsg_get_u32(c, "idleclose", &tda->tda_idleclose);
htsmsg_get_u32(c, "skip_checksubscr", &tda->tda_skip_checksubscr);
htsmsg_get_u32(c, "sidtochan", &tda->tda_sidtochan);
htsmsg_get_u32(c, "qmon", &tda->tda_qmon);
htsmsg_get_u32(c, "poweroff", &tda->tda_poweroff);
htsmsg_get_u32(c, "nitoid", &tda->tda_nitoid);
htsmsg_get_u32(c, "diseqc_version", &tda->tda_diseqc_version);
htsmsg_get_u32(c, "diseqc_repeats", &tda->tda_diseqc_repeats);
htsmsg_get_u32(c, "extrapriority", &tda->tda_extrapriority);
htsmsg_get_u32(c, "skip_initialscan", &tda->tda_skip_initialscan);
htsmsg_get_u32(c, "disable_pmt_monitor", &tda->tda_disable_pmt_monitor);
if (htsmsg_get_s32(c, "full_mux_rx", &tda->tda_full_mux_rx))
if (!htsmsg_get_u32(c, "disable_full_mux_rx", &u32) && u32)
@ -737,12 +783,9 @@ dvb_adapter_init(uint32_t adapter_mask, const char *rawfile)
htsmsg_destroy(l);
}
/* Initialise devices */
TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
tda_init_input(tda);
if (tda->tda_idlescan || !tda->tda_idleclose) {
close(tda->tda_fe_fd);
tda->tda_fe_fd = -1;
}
tda_init(tda);
if(tda->tda_sat)
dvb_satconf_init(tda);
@ -766,6 +809,9 @@ dvb_adapter_mux_scanner(void *aux)
if(tda->tda_rootpath == NULL)
return; // No hardware
if(!tda->tda_enabled)
return; // disabled
// default period
gtimer_arm(&tda->tda_mux_scanner_timer, dvb_adapter_mux_scanner, tda, 20);
@ -905,13 +951,10 @@ dvb_adapter_input_dvr(void *aux)
uint8_t tsb[188 * 10];
service_t *t;
struct epoll_event ev;
char path[256];
snprintf(path, sizeof(path), "%s/dvr0", tda->tda_rootpath);
fd = tvh_open(path, O_RDONLY | O_NONBLOCK, 0);
fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0);
if(fd == -1) {
tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", path, strerror(errno));
tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", tda->tda_dvr_path, strerror(errno));
return NULL;
}
@ -1088,6 +1131,13 @@ dvb_adapter_build_msg(th_dvb_adapter_t *tda)
htsmsg_add_u32(m, "ber", tdmi->tdmi_ber);
htsmsg_add_u32(m, "unc", tdmi->tdmi_unc);
htsmsg_add_u32(m, "uncavg", tdmi->tdmi_unc_avg);
} else {
htsmsg_add_str(m, "currentMux", "");
htsmsg_add_u32(m, "signal", 0);
htsmsg_add_u32(m, "snr", 0);
htsmsg_add_u32(m, "ber", 0);
htsmsg_add_u32(m, "unc", 0);
htsmsg_add_u32(m, "uncavg", 0);
}
if(tda->tda_rootpath == NULL)

View file

@ -288,7 +288,6 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
tvhlog(LOG_NOTICE, "dvb", "New mux \"%s\" created by %s", buf, source);
dvb_mux_save(tdmi);
dvb_adapter_notify(tda);
}
dvb_service_load(tdmi, identifier);
@ -303,6 +302,7 @@ dvb_mux_create(th_dvb_adapter_t *tda, const struct dvb_mux_conf *dmc,
dvb_mux_add_to_scan_queue(tdmi);
}
}
dvb_adapter_notify(tda);
return tdmi;
}