618 lines
15 KiB
C
618 lines
15 KiB
C
/*
|
||
* TV Input - Linux DVB interface
|
||
* Copyright (C) 2007 Andreas Öman
|
||
*
|
||
* 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 <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <pthread.h>
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/ioctl.h>
|
||
#include <fcntl.h>
|
||
#include <errno.h>
|
||
#include <ctype.h>
|
||
#include <stdio.h>
|
||
#include <unistd.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <dirent.h>
|
||
|
||
#include <linux/dvb/frontend.h>
|
||
#include <linux/dvb/dmx.h>
|
||
|
||
#include <libhts/htscfg.h>
|
||
|
||
#include "tvhead.h"
|
||
#include "dispatch.h"
|
||
#include "dvb.h"
|
||
#include "channels.h"
|
||
#include "transports.h"
|
||
#include "subscriptions.h"
|
||
#include "teletext.h"
|
||
#include "epg.h"
|
||
#include "psi.h"
|
||
#include "dvb_support.h"
|
||
#include "dvb_dvr.h"
|
||
#include "dvb_muxconfig.h"
|
||
#include "notify.h"
|
||
|
||
struct th_dvb_adapter_queue dvb_adapters;
|
||
struct th_dvb_mux_instance_list dvb_muxes;
|
||
static void dvb_mux_scanner(void *aux, int64_t now);
|
||
static void dvb_fec_monitor(void *aux, int64_t now);
|
||
|
||
static int dvb_tda_load(th_dvb_adapter_t *tda);
|
||
static void dvb_tdmi_load(th_dvb_mux_instance_t *tdmi);
|
||
static void dvb_transport_config_change(th_transport_t *t);
|
||
static const char *dvb_source_name(th_transport_t *t);
|
||
|
||
|
||
static th_dvb_adapter_t *
|
||
tda_alloc(void)
|
||
{
|
||
th_dvb_adapter_t *tda = calloc(1, sizeof(th_dvb_adapter_t));
|
||
pthread_mutex_init(&tda->tda_lock, NULL);
|
||
pthread_cond_init(&tda->tda_cond, NULL);
|
||
TAILQ_INIT(&tda->tda_fe_cmd_queue);
|
||
|
||
return tda;
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
dvb_add_adapter(const char *path)
|
||
{
|
||
char fname[256];
|
||
int fe, i, r;
|
||
th_dvb_adapter_t *tda;
|
||
char buf[400];
|
||
|
||
snprintf(fname, sizeof(fname), "%s/frontend0", path);
|
||
|
||
fe = open(fname, O_RDWR | O_NONBLOCK);
|
||
if(fe == -1) {
|
||
if(errno != ENOENT)
|
||
syslog(LOG_ALERT, "Unable to open %s -- %s\n", fname, strerror(errno));
|
||
return;
|
||
}
|
||
|
||
tda = tda_alloc();
|
||
|
||
tda->tda_rootpath = strdup(path);
|
||
tda->tda_demux_path = malloc(256);
|
||
snprintf(tda->tda_demux_path, 256, "%s/demux0", path);
|
||
tda->tda_dvr_path = malloc(256);
|
||
snprintf(tda->tda_dvr_path, 256, "%s/dvr0", path);
|
||
|
||
|
||
tda->tda_fe_fd = fe;
|
||
|
||
tda->tda_fe_info = malloc(sizeof(struct dvb_frontend_info));
|
||
tda->tda_type = tda->tda_fe_info->type;
|
||
|
||
if(ioctl(tda->tda_fe_fd, FE_GET_INFO, tda->tda_fe_info)) {
|
||
syslog(LOG_ALERT, "%s: Unable to query adapter\n", fname);
|
||
close(fe);
|
||
free(tda);
|
||
return;
|
||
}
|
||
|
||
if(dvb_dvr_init(tda) < 0) {
|
||
close(fe);
|
||
free(tda);
|
||
return;
|
||
}
|
||
|
||
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);
|
||
|
||
|
||
/* 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);
|
||
|
||
syslog(LOG_INFO, "Found adapter %s (%s)", path, tda->tda_fe_info->name);
|
||
|
||
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
|
||
dvb_tda_load(tda);
|
||
|
||
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
|
||
dvb_fe_start(tda);
|
||
}
|
||
|
||
|
||
|
||
|
||
void
|
||
dvb_init(void)
|
||
{
|
||
char path[200];
|
||
struct dirent *d;
|
||
DIR *dir;
|
||
int i;
|
||
th_dvb_adapter_t *tda;
|
||
|
||
TAILQ_INIT(&dvb_adapters);
|
||
|
||
for(i = 0; i < 32; i++) {
|
||
snprintf(path, sizeof(path), "/dev/dvb/adapter%d", i);
|
||
dvb_add_adapter(path);
|
||
}
|
||
|
||
/* Load any stale adapters */
|
||
|
||
snprintf(path, sizeof(path), "%s/dvbadapters", settings_dir);
|
||
|
||
if((dir = opendir(path)) == NULL)
|
||
return;
|
||
|
||
while((d = readdir(dir)) != NULL) {
|
||
if(d->d_name[0] != '_')
|
||
continue;
|
||
|
||
if(dvb_adapter_find_by_identifier(d->d_name) != NULL) {
|
||
/* Already loaded */
|
||
printf("%s is already loaded\n", d->d_name);
|
||
continue;
|
||
}
|
||
|
||
tda = tda_alloc();
|
||
|
||
tda = calloc(1, sizeof(th_dvb_adapter_t));
|
||
tda->tda_identifier = strdup(d->d_name);
|
||
printf("Loading stale adapter %s\n", tda->tda_identifier);
|
||
|
||
if(dvb_tda_load(tda) == 0) {
|
||
TAILQ_INSERT_TAIL(&dvb_adapters, tda, tda_global_link);
|
||
} else {
|
||
printf("Error while loading adapter %s -- settings file is corrupt\n",
|
||
tda->tda_identifier);
|
||
}
|
||
}
|
||
closedir(dir);
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* Find a transport based on 'serviceid' on the given mux
|
||
*
|
||
* If it cannot be found we create it |