Fix #1666 - dvb: rework the adapter tuning to stop possible deadlock

This commit is contained in:
Adam Sutton 2013-03-22 12:12:29 +00:00
parent 4f2f68a4b6
commit d75b99a610
3 changed files with 52 additions and 123 deletions

View file

@ -209,6 +209,7 @@ typedef struct th_dvb_adapter {
uint32_t tda_enabled;
int tda_locked;
time_t tda_monitor;
const char *tda_rootpath;
char *tda_identifier;
@ -250,7 +251,6 @@ typedef struct th_dvb_adapter {
struct service_list tda_transports; /* Currently bound transports */
gtimer_t tda_fe_monitor_timer;
int tda_fe_monitor_hold;
int tda_sat; // Set if this adapter is a satellite receiver (DVB-S, etc)

View file

@ -1020,13 +1020,25 @@ static void *
dvb_adapter_input_dvr(void *aux)
{
th_dvb_adapter_t *tda = aux;
th_dvb_mux_instance_t *tdmi;
int fd = -1, i, r, c, efd, nfds, dmx = -1;
uint8_t tsb[188 * 10];
service_t *t;
struct epoll_event ev;
int delay = 10, locked = 0;
fe_status_t festat;
int delay = 10;
/* Install RAW demux */
if (tda->tda_rawmode) {
if ((dmx = dvb_adapter_raw_filter(tda)) == -1) {
tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter");
return NULL;
}
}
/* Open DVR */
if ((fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0)) == -1) {
close(dmx);
return NULL;
}
/* Create poll */
efd = epoll_create(2);
@ -1034,6 +1046,8 @@ dvb_adapter_input_dvr(void *aux)
ev.events = EPOLLIN;
ev.data.fd = tda->tda_dvr_pipe.rd;
epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev);
ev.data.fd = fd;
epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
r = i = 0;
while(1) {
@ -1041,62 +1055,12 @@ dvb_adapter_input_dvr(void *aux)
/* Wait for input */
nfds = epoll_wait(efd, &ev, 1, delay);
/* Exit */
if ((nfds > 0) && (ev.data.fd != fd)) break;
/* Check for lock */
if (!locked) {
if (ioctl(tda->tda_fe_fd, FE_READ_STATUS, &festat))
continue;
if (!(festat & FE_HAS_LOCK))
continue;
/* Open DVR */
fd = tvh_open(tda->tda_dvr_path, O_RDONLY | O_NONBLOCK, 0);
if (fd == -1) {
tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s",
tda->tda_dvr_path, strerror(errno));
break;
}
ev.data.fd = fd;
epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
/* Note: table handlers must be installed with global lock */
pthread_mutex_lock(&global_lock);
tda->tda_locked = locked = 1;
delay = -1;
if ((tdmi = tda->tda_mux_current)) {
/* Install table handlers */
dvb_table_add_default(tdmi);
epggrab_mux_start(tdmi);
/* Raw filter */
if(tda->tda_rawmode)
dmx = dvb_adapter_raw_filter(tda);
/* Service filters */
pthread_mutex_lock(&tda->tda_delivery_mutex);
LIST_FOREACH(t, &tda->tda_transports, s_active_link) {
if (t->s_dvb_mux_instance == tdmi) {
tda->tda_open_service(tda, t);
dvb_table_add_pmt(tdmi, t->s_pmt_pid);
}
}
pthread_mutex_unlock(&tda->tda_delivery_mutex);
}
pthread_mutex_unlock(&global_lock);
/* Error */
if (tda->tda_rawmode && (dmx == -1)) {
tvhlog(LOG_ALERT, "dvb", "Unable to install raw mux filter");
break;
}
}
/* No data */
if (nfds < 1) continue;
/* Exit */
if (ev.data.fd != fd) break;
/* Read data */
c = read(fd, tsb+r, sizeof(tsb)-r);
if (c < 0) {

View file

@ -101,8 +101,6 @@ dvb_fe_monitor(void *aux)
int store = 0;
int notify = 0;
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
if(tdmi == NULL)
return;
@ -110,9 +108,8 @@ dvb_fe_monitor(void *aux)
* Read out front end status
*/
if(ioctl(tda->tda_fe_fd, FE_READ_STATUS, &fe_status))
fe_status = 0;
if(fe_status & FE_HAS_LOCK)
status = TDMI_FE_UNKNOWN;
else if(fe_status & FE_HAS_LOCK)
status = -1;
else if(fe_status & (FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_CARRIER))
status = TDMI_FE_BAD_SIGNAL;
@ -121,18 +118,34 @@ dvb_fe_monitor(void *aux)
else
status = TDMI_FE_NO_SIGNAL;
if(tda->tda_fe_monitor_hold > 0) {
/* Post tuning threshold */
if(status == -1) { /* We have a lock, don't hold off */
tda->tda_fe_monitor_hold = 0;
/* Reset FEC counter */
dvb_fe_get_unc(tda);
/**
* Waiting for initial lock
*/
if(tda->tda_locked == 0) {
/* Read */
if (status == -1) {
tda->tda_locked = 1;
dvb_adapter_start(tda, TDA_OPT_ALL);
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
/* Re-arm (50ms) */
} else {
tda->tda_fe_monitor_hold--;
return;
gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50);
/* Monitor (1 per sec) */
if (dispatch_clock < tda->tda_monitor)
return;
tda->tda_monitor = dispatch_clock + 1;
}
} else {
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
}
/*
* Update stats
*/
if(status == -1) {
/* Read FEC counter (delta) */
@ -312,46 +325,6 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune)
#if DVB_API_VERSION >= 5
static int check_frontend (int fe_fd, int dvr, int human_readable) {
(void)dvr;
fe_status_t status;
uint16_t snr, signal;
uint32_t ber;
int timeout = 0;
do {
if (ioctl(fe_fd, FE_READ_STATUS, &status) == -1)
perror("FE_READ_STATUS failed");
/* some frontends might not support all these ioctls, thus we
* avoid printing errors
*/
if (ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal) == -1)
signal = -2;
if (ioctl(fe_fd, FE_READ_SNR, &snr) == -1)
snr = -2;
if (ioctl(fe_fd, FE_READ_BER, &ber) == -1)
ber = -2;
if (human_readable) {
printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | ",
status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber);
} else {
printf ("status %02x | signal %04x | snr %04x | ber %08x | ",
status, signal, snr, ber);
}
if (status & FE_HAS_LOCK)
printf("FE_HAS_LOCK");
printf("\n");
if ((status & FE_HAS_LOCK) || (++timeout >= 10))
break;
usleep(1000000);
} while (1);
return 0;
}
static struct dtv_property clear_p[] = {
{ .cmd = DTV_CLEAR },
};
@ -361,7 +334,6 @@ static struct dtv_properties clear_cmdseq = {
.props = clear_p
};
/**
*
*/
@ -404,8 +376,6 @@ dvb_fe_tune_s2(th_dvb_mux_instance_t *tdmi, dvb_mux_conf_t *dmc)
/* do tuning now */
r = ioctl(tda->tda_fe_fd, FE_SET_PROPERTY, &_dvbs_cmdseq);
if(0)
check_frontend (tda->tda_fe_fd, 0, 1);
return r;
}
@ -493,8 +463,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
dvb_mux_nicename(buf, sizeof(buf), tdmi);
tda->tda_fe_monitor_hold = 4;
#if DVB_API_VERSION >= 5
if (tda->tda_type == FE_QPSK) {
tvhlog(LOG_DEBUG, "dvb", "\"%s\" tuning via s2api to \"%s\" (%d, %d Baud, "
@ -524,19 +492,16 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason)
/* Mark as bad */
if (errno == EINVAL)
dvb_mux_set_enable(tdmi, 0);
dvb_adapter_stop(tda, TDA_OPT_ALL);
return SM_CODE_TUNING_FAILED;
}
tda->tda_mux_current = tdmi;
dvb_adapter_start(tda, TDA_OPT_ALL);
gtimer_arm(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 1);
#if 0
dvb_table_add_default(tdmi);
epggrab_mux_start(tdmi);
#endif
time(&tda->tda_monitor);
tda->tda_monitor += 4; // wait a few secs before monitoring (unlocked)
gtimer_arm_ms(&tda->tda_fe_monitor_timer, dvb_fe_monitor, tda, 50);
dvb_adapter_notify(tda);
return 0;