diff --git a/src/dvb/dvb.h b/src/dvb/dvb.h index aaccd946..335ac3c0 100644 --- a/src/dvb/dvb.h +++ b/src/dvb/dvb.h @@ -29,6 +29,12 @@ struct service; struct th_dvb_table; struct th_dvb_mux_instance; +#define TDA_OPT_FE 0x1 +#define TDA_OPT_DVR 0x2 +#define TDA_OPT_DMX 0x4 +#define TDA_OPT_PWR 0x8 +#define TDA_OPT_ALL (TDA_OPT_FE | TDA_OPT_DVR | TDA_OPT_DMX | TDA_OPT_PWR) + #define DVB_VER_INT(maj,min) (((maj) << 16) + (min)) #define DVB_VER_ATLEAST(maj, min) \ @@ -202,6 +208,8 @@ typedef struct th_dvb_adapter { uint32_t tda_enabled; + int tda_locked; + const char *tda_rootpath; char *tda_identifier; uint32_t tda_autodiscovery; @@ -342,11 +350,9 @@ void dvb_adapter_init(uint32_t adapter_mask, const char *rawfile); void dvb_adapter_mux_scanner(void *aux); -void dvb_adapter_start (th_dvb_adapter_t *tda); +void dvb_adapter_start (th_dvb_adapter_t *tda, int opt); -void dvb_adapter_stop (th_dvb_adapter_t *tda); - -void dvb_adapter_stop_dvr (th_dvb_adapter_t *tda); +void dvb_adapter_stop (th_dvb_adapter_t *tda, int opt); void dvb_adapter_set_displayname(th_dvb_adapter_t *tda, const char *s); diff --git a/src/dvb/dvb_adapter.c b/src/dvb/dvb_adapter.c index 8b3878dc..02b3fb8b 100644 --- a/src/dvb/dvb_adapter.c +++ b/src/dvb/dvb_adapter.c @@ -141,7 +141,7 @@ dvb_adapter_set_enabled(th_dvb_adapter_t *tda, int 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); + dvb_adapter_stop(tda, TDA_OPT_ALL); } else { tda_init(tda); } @@ -692,22 +692,26 @@ static void tda_init (th_dvb_adapter_t *tda) * */ void -dvb_adapter_start ( th_dvb_adapter_t *tda ) +dvb_adapter_start ( th_dvb_adapter_t *tda, int opt ) { if(tda->tda_enabled == 0) { tvhlog(LOG_INFO, "dvb", "Adapter \"%s\" cannot be started - it's disabled", tda->tda_displayname); return; } + + /* Default to ALL */ + if (!opt) + opt = TDA_OPT_ALL; /* Open front end */ - if (tda->tda_fe_fd == -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd == -1)) { tda->tda_fe_fd = tvh_open(tda->tda_fe_path, O_RDWR | O_NONBLOCK, 0); if (tda->tda_fe_fd == -1) return; tvhlog(LOG_DEBUG, "dvb", "%s opened frontend %s", tda->tda_rootpath, tda->tda_fe_path); } /* Start DVR thread */ - if (tda->tda_dvr_pipe.rd == -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd == -1)) { int err = tvh_pipe(O_NONBLOCK, &tda->tda_dvr_pipe); assert(err != -1); pthread_create(&tda->tda_dvr_thread, NULL, dvb_adapter_input_dvr, tda); @@ -716,10 +720,14 @@ dvb_adapter_start ( th_dvb_adapter_t *tda ) } void -dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) +dvb_adapter_stop ( th_dvb_adapter_t *tda, int opt ) { + /* Poweroff */ + if (opt & TDA_OPT_PWR) + dvb_adapter_poweroff(tda); + /* Stop DVR thread */ - if (tda->tda_dvr_pipe.rd != -1) { + if ((opt & TDA_OPT_DVR) && (tda->tda_dvr_pipe.rd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s stopping thread", tda->tda_rootpath); int err = tvh_write(tda->tda_dvr_pipe.wr, "", 1); assert(!err); @@ -729,26 +737,17 @@ dvb_adapter_stop_dvr ( th_dvb_adapter_t *tda ) tda->tda_dvr_pipe.rd = -1; tvhlog(LOG_DEBUG, "dvb", "%s stopped thread", tda->tda_rootpath); } -} -void -dvb_adapter_stop ( th_dvb_adapter_t *tda ) -{ - /* Poweroff */ - dvb_adapter_poweroff(tda); - - /* Don't stop/close */ + /* Don't close FE */ if (!tda->tda_idleclose && tda->tda_enabled) return; /* Close front end */ - if (tda->tda_fe_fd != -1) { + if ((opt & TDA_OPT_FE) && (tda->tda_fe_fd != -1)) { tvhlog(LOG_DEBUG, "dvb", "%s closing frontend", tda->tda_rootpath); close(tda->tda_fe_fd); tda->tda_fe_fd = -1; } - dvb_adapter_stop_dvr(tda); - dvb_adapter_notify(tda); } @@ -980,8 +979,39 @@ dvb_adapter_clean(th_dvb_adapter_t *tda) service_remove_subscriber(t, NULL, SM_CODE_SUBSCRIPTION_OVERRIDDEN); } +/** + * Install RAW PES filter + */ +static int +dvb_adapter_raw_filter(th_dvb_adapter_t *tda) +{ + int dmx = -1; + struct dmx_pes_filter_params dmx_param; + dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); + if(dmx == -1) { + tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", + tda->tda_demux_path, strerror(errno)); + return -1; + } + memset(&dmx_param, 0, sizeof(dmx_param)); + dmx_param.pid = 0x2000; + 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(dmx, DMX_SET_PES_FILTER, &dmx_param) == -1) { + tvhlog(LOG_ERR, "dvb", + "Unable to configure demuxer \"%s\" for all PIDs -- %s", + tda->tda_demux_path, strerror(errno)); + close(dmx); + return -1; + } + + return dmx; +} /** * @@ -990,54 +1020,18 @@ static void * dvb_adapter_input_dvr(void *aux) { th_dvb_adapter_t *tda = aux; - int fd, i, r, c, efd, nfds, dmx = -1; + 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; - - 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)); - return NULL; - } - - if(tda->tda_rawmode) { - - // Receive unfiltered raw transport stream - - dmx = tvh_open(tda->tda_demux_path, O_RDWR, 0); - if(dmx == -1) { - tvhlog(LOG_ALERT, "dvb", "Unable to open %s -- %s", - tda->tda_demux_path, strerror(errno)); - close(fd); - return NULL; - } - - struct dmx_pes_filter_params dmx_param; - - memset(&dmx_param, 0, sizeof(dmx_param)); - dmx_param.pid = 0x2000; - 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(dmx, DMX_SET_PES_FILTER, &dmx_param)) { - tvhlog(LOG_ERR, "dvb", - "Unable to configure demuxer \"%s\" for all PIDs -- %s", - tda->tda_demux_path, strerror(errno)); - close(dmx); - close(fd); - return NULL; - } - } + int delay = 10, locked = 0; + fe_status_t festat; /* Create poll */ efd = epoll_create(2); memset(&ev, 0, sizeof(ev)); ev.events = EPOLLIN; - ev.data.fd = fd; - epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev); ev.data.fd = tda->tda_dvr_pipe.rd; epoll_ctl(efd, EPOLL_CTL_ADD, tda->tda_dvr_pipe.rd, &ev); @@ -1045,10 +1039,65 @@ dvb_adapter_input_dvr(void *aux) while(1) { /* Wait for input */ - nfds = epoll_wait(efd, &ev, 1, -1); - if (nfds < 1) continue; - if (ev.data.fd != fd) break; + 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; + + /* Read data */ c = read(fd, tsb+r, sizeof(tsb)-r); if (c < 0) { if (errno == EAGAIN || errno == EINTR) diff --git a/src/dvb/dvb_fe.c b/src/dvb/dvb_fe.c index 77f76217..d0924c69 100644 --- a/src/dvb/dvb_fe.c +++ b/src/dvb/dvb_fe.c @@ -273,6 +273,7 @@ void dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) { th_dvb_adapter_t *tda = tdmi->tdmi_adapter; + dvb_table_feed_t *dtf; lock_assert(&global_lock); @@ -285,8 +286,13 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) dvb_mux_save(tdmi); } - dvb_adapter_stop_dvr(tda); + dvb_adapter_stop(tda, TDA_OPT_DVR); + pthread_mutex_lock(&tda->tda_delivery_mutex); + while((dtf = TAILQ_FIRST(&tda->tda_table_feed))) + TAILQ_REMOVE(&tda->tda_table_feed, dtf, dtf_link); + pthread_mutex_unlock(&tda->tda_delivery_mutex); dvb_table_flush_all(tdmi); + tda->tda_locked = 0; assert(tdmi->tdmi_scan_queue == NULL); @@ -300,7 +306,7 @@ dvb_fe_stop(th_dvb_mux_instance_t *tdmi, int retune) if (!retune) { gtimer_disarm(&tda->tda_fe_monitor_timer); - dvb_adapter_stop(tda); + dvb_adapter_stop(tda, TDA_OPT_ALL); } } @@ -421,9 +427,11 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) char buf[256]; int r; - lock_assert(&global_lock); + if(tda->tda_enabled == 0) + return SM_CODE_TUNING_FAILED; + if(tda->tda_mux_current == tdmi) return 0; @@ -434,7 +442,8 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if(tda->tda_mux_current != NULL) dvb_fe_stop(tda->tda_mux_current, 1); - dvb_adapter_start(tda); + + dvb_adapter_start(tda, TDA_OPT_FE | TDA_OPT_PWR); if(tda->tda_type == FE_QPSK) { @@ -486,7 +495,6 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) 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, " @@ -517,15 +525,18 @@ dvb_fe_tune(th_dvb_mux_instance_t *tdmi, const char *reason) if (errno == EINVAL) dvb_mux_set_enable(tdmi, 0); 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 dvb_adapter_notify(tda); return 0; diff --git a/src/dvb/dvb_service.c b/src/dvb/dvb_service.c index 61242bef..31e25e64 100644 --- a/src/dvb/dvb_service.c +++ b/src/dvb/dvb_service.c @@ -86,18 +86,21 @@ dvb_service_start(service_t *t, unsigned int weight, int force_start) dvb_adapter_clean(tda); } + r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); + pthread_mutex_lock(&tda->tda_delivery_mutex); - r = dvb_fe_tune(t->s_dvb_mux_instance, "Transport start"); if(!r) LIST_INSERT_HEAD(&tda->tda_transports, t, s_active_link); pthread_mutex_unlock(&tda->tda_delivery_mutex); - if(!r) - tda->tda_open_service(tda, t); + if (tda->tda_locked) { + if(!r) + tda->tda_open_service(tda, t); - dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + dvb_table_add_pmt(t->s_dvb_mux_instance, t->s_pmt_pid); + } return r; }