linuxdvb: added dynamic and combi DVB tuner support

I have also simplified the hardware tree. Though I still need to
resolve some minor issues related to adapter ID'ing.
This commit is contained in:
Adam Sutton 2014-01-10 23:14:24 +00:00
parent 517af478ab
commit 96b4b22312
17 changed files with 483 additions and 649 deletions

View file

@ -186,7 +186,6 @@ SRCS-$(CONFIG_MPEGTS) += \
# DVB
SRCS-${CONFIG_LINUXDVB} += \
src/input/mpegts/linuxdvb/linuxdvb.c \
src/input/mpegts/linuxdvb/linuxdvb_device.c \
src/input/mpegts/linuxdvb/linuxdvb_adapter.c \
src/input/mpegts/linuxdvb/linuxdvb_frontend.c \
src/input/mpegts/linuxdvb/linuxdvb_network.c \

View file

@ -230,6 +230,12 @@ idnode_uuid_as_str(const idnode_t *in)
p = (p + 1) % 16;
return idnode_uuid_as_str0(in, b);
}
const char *
idnode_uuid_as_str1 ( const uint8_t *bin, size_t len, char *b )
{
bin2hex(b, UUID_STR_LEN, bin, len);
return b;
}
/**
*

View file

@ -114,6 +114,7 @@ int idnode_insert(idnode_t *in, const char *uuid, const idclass_t *idc);
void idnode_unlink(idnode_t *in);
uint32_t idnode_get_short_uuid (const idnode_t *in);
const char *idnode_uuid_as_str1 (const uint8_t *bin, size_t len, char *b);
const char *idnode_uuid_as_str0 (const idnode_t *in, char *b);
const char *idnode_uuid_as_str (const idnode_t *in);
idnode_set_t *idnode_get_childs (idnode_t *in);

View file

@ -17,10 +17,52 @@
*/
#include "input.h"
#include "notify.h"
tvh_input_list_t tvh_inputs;
tvh_hardware_list_t tvh_hardware;
/*
* Create entry
*/
void *
tvh_hardware_create0
( void *o, const idclass_t *idc, const char *uuid, htsmsg_t *conf )
{
tvh_hardware_t *th = o;
/* Create node */
if (idnode_insert(&th->th_id, uuid, idc)) {
free(o);
return NULL;
}
/* Update list */
LIST_INSERT_HEAD(&tvh_hardware, th, th_link);
/* Load config */
if (conf)
idnode_load(&th->th_id, conf);
/* Update */
notify_reload("hardware");
return o;
}
/*
* Delete hardware entry
*/
void
tvh_hardware_delete ( tvh_hardware_t *th )
{
// TODO
LIST_REMOVE(th, th_link);
idnode_unlink(&th->th_id);
notify_reload("hardware");
}
/*
* Input status handling
*/

View file

@ -78,6 +78,9 @@ struct tvh_hardware {
LIST_ENTRY(tvh_hardware) th_link;
};
void *tvh_hardware_create0
( void *o, const idclass_t *idc, const char *uuid, htsmsg_t *conf );
void tvh_hardware_delete ( tvh_hardware_t *th );
/*
* Class and Global list defs

View file

@ -222,7 +222,7 @@ typedef struct dvb_mux_conf
polarisation_t dmc_fe_polarisation;
int dmc_fe_orbital_pos;
char dmc_fe_orbital_dir;
#if DVB_API_VERSION >= 5
#if DVB_VER_ATLEAST(5,0)
fe_modulation_t dmc_fe_modulation;
fe_delivery_system_t dmc_fe_delsys;
fe_rolloff_t dmc_fe_rolloff;
@ -270,6 +270,10 @@ int dvb_str2pilot ( const char *str );
int dvb_bandwidth ( enum fe_bandwidth bw );
#if DVB_VER_ATLEAST(5,10)
int dvb_delsys2type ( enum fe_delivery_system ds );
#endif
#endif /* ENABLE_DVBAPI */
#endif /* DVB_SUPPORT_H */

View file

@ -421,6 +421,33 @@ const static struct strtab delsystab[] = {
};
dvb_str2val(delsys);
#if DVB_VER_ATLEAST(5,10)
int
dvb_delsys2type ( fe_delivery_system_t delsys )
{
switch (delsys) {
case SYS_DVBC_ANNEX_AC:
case SYS_DVBC_ANNEX_B:
case SYS_ISDBC:
return FE_QAM;
case SYS_DVBT:
case SYS_DVBT2:
case SYS_TURBO:
case SYS_ISDBT:
return FE_OFDM;
case SYS_DVBS:
case SYS_DVBS2:
case SYS_ISDBS:
return FE_QPSK;
case SYS_ATSC:
case SYS_ATSCMH:
return FE_ATSC;
default:
return -1;
}
}
#endif
const static struct strtab fectab[] = {
{ "NONE", FEC_NONE },
{ "1/2", FEC_1_2 },

View file

@ -36,5 +36,5 @@ void linuxdvb_init ( int adapter_mask )
linuxdvb_network_init();
/* Initialsie devices */
linuxdvb_device_init(adapter_mask);
linuxdvb_adapter_init();
}

View file

@ -21,17 +21,21 @@
#include "input.h"
#include "linuxdvb_private.h"
#include "queue.h"
#include "fsmonitor.h"
#include "settings.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <openssl/sha.h>
#define FE_PATH "/dev/dvb/adapter%d/frontend%d"
#define DVR_PATH "/dev/dvb/adapter%d/dvr%d"
#define DMX_PATH "/dev/dvb/adapter%d/demux%d"
#define FE_PATH "%s/frontend%d"
#define DVR_PATH "%s/dvr%d"
#define DMX_PATH "%s/demux%d"
/* ***************************************************************************
* DVB Adapter
@ -41,7 +45,7 @@ static void
linuxdvb_adapter_class_save ( idnode_t *in )
{
linuxdvb_adapter_t *la = (linuxdvb_adapter_t*)in;
linuxdvb_device_save(la->la_device);
linuxdvb_adapter_save(la);
}
static idnode_set_t *
@ -85,141 +89,114 @@ const idclass_t linuxdvb_adapter_class =
* Save data
*/
void
linuxdvb_adapter_save ( linuxdvb_adapter_t *la, htsmsg_t *m )
linuxdvb_adapter_save ( linuxdvb_adapter_t *la )
{
htsmsg_t *l;
htsmsg_t *m, *l;
linuxdvb_frontend_t *lfe;
idnode_save(&la->la_id, m);
htsmsg_add_u32(m, "number", la->la_number);
m = htsmsg_create_map();
idnode_save(&la->th_id, m);
/* Frontends */
l = htsmsg_create_map();
LIST_FOREACH(lfe, &la->la_frontends, lfe_link) {
htsmsg_t *e = htsmsg_create_map();
linuxdvb_frontend_save(lfe, e);
htsmsg_add_msg(l, idnode_uuid_as_str(&lfe->ti_id), e);
}
LIST_FOREACH(lfe, &la->la_frontends, lfe_link)
linuxdvb_frontend_save(lfe, l);
htsmsg_add_msg(m, "frontends", l);
}
/*
* Check if enabled
*/
static int
linuxdvb_adapter_is_enabled ( linuxdvb_adapter_t *la )
{
linuxdvb_frontend_t *lfe;
LIST_FOREACH(lfe, &la->la_frontends, lfe_link) {
if (lfe->mi_is_enabled((mpegts_input_t*)lfe))
return 1;
}
return 0;
/* Save */
hts_settings_save(m, "input/linuxdvb/adapters/%s",
idnode_uuid_as_str(&la->th_id));
htsmsg_destroy(m);
}
/*
* Create
*/
linuxdvb_adapter_t *
linuxdvb_adapter_create0
( linuxdvb_device_t *ld, const char *uuid, htsmsg_t *conf )
static linuxdvb_adapter_t *
linuxdvb_adapter_create
( const char *uuid, htsmsg_t *conf,
const char *path, int number, struct dvb_frontend_info *dfi )
{
uint32_t u32;
htsmsg_t *e;
htsmsg_field_t *f;
linuxdvb_adapter_t *la;
char buf[1024];
/* Create */
la = calloc(1, sizeof(linuxdvb_adapter_t));
if (idnode_insert(&la->la_id, uuid, &linuxdvb_adapter_class)) {
if (!tvh_hardware_create0((tvh_hardware_t*)la, &linuxdvb_adapter_class,
uuid, conf)) {
free(la);
return NULL;
}
LIST_INSERT_HEAD(&ld->ld_adapters, la, la_link);
la->la_device = ld;
la->la_dvb_number = -1;
la->la_is_enabled = linuxdvb_adapter_is_enabled;
/* No conf */
if (!conf)
return la;
idnode_load(&la->la_id, conf);
if (!htsmsg_get_u32(conf, "number", &u32))
la->la_number = u32;
/* Frontends */
if ((conf = htsmsg_get_map(conf, "frontends"))) {
HTSMSG_FOREACH(f, conf) {
if (!(e = htsmsg_get_map_by_field(f))) continue;
(void)linuxdvb_frontend_create0(la, f->hmf_name, e, 0);
}
}
/* Setup */
sprintf(buf, "%s [%s]", path, dfi->name);
la->la_rootpath = strdup(path);
la->la_name = strdup(buf);
la->la_dvb_number = number;
return la;
}
/*
* Find existing adapter/device entry
* Add adapter by path
*/
static linuxdvb_adapter_t *
linuxdvb_adapter_find_by_number ( int adapter )
static void
linuxdvb_adapter_add ( const char *path )
{
int a;
char buf[1024];
linuxdvb_device_t *ld;
linuxdvb_adapter_t *la;
/* Find device */
if (!(ld = linuxdvb_device_find_by_adapter(adapter)))
return NULL;
/* Find existing adapter */
a = adapter - ld->ld_devid.di_min_adapter;
LIST_FOREACH(la, &ld->ld_adapters, la_link) {
if (la->la_number == a)
break;
}
/* Create */
if (!la) {
if (!(la = linuxdvb_adapter_create0(ld, NULL, NULL)))
return NULL;
}
/* Update */
la->la_number = a;
snprintf(buf, sizeof(buf), "/dev/dvb/adapter%d", adapter);
tvh_str_update(&la->la_rootpath, buf);
return la;
}
/*
* Load an adapter
*/
void
linuxdvb_adapter_added ( int adapter )
{
int i, r, fd, save = 0;
char fe_path[512], dmx_path[512], dvr_path[512];
int a, i, j, r, fd;
char fe_path[512], dmx_path[512], dvr_path[512], uuid[UUID_STR_LEN];
linuxdvb_adapter_t *la = NULL;
struct dvb_frontend_info dfi;
SHA_CTX sha1;
uint8_t uuidbin[20];
htsmsg_t *conf = NULL, *feconf = NULL;
#if DVB_VER_ATLEAST(5,10)
int fetypes[4] = { 0 };
struct dtv_property cmd = {
.cmd = DTV_ENUM_DELSYS
};
struct dtv_properties cmdseq = {
.num = 1,
.props = &cmd
};
#endif
/* Validate the path */
if (sscanf(path, "/dev/dvb/adapter%d", &a) != 1)
return;
/* Note: some of the below can take a while, so we relinquish the lock
* to stop us blocking everyhing else
*/
pthread_mutex_unlock(&global_lock);
/* Process each frontend */
for (i = 0; i < 32; i++) {
snprintf(fe_path, sizeof(fe_path), FE_PATH, adapter, i);
snprintf(fe_path, sizeof(fe_path), FE_PATH, path, i);
/* No access */
/* Wait for access (first FE can take a fe ms to be setup) */
if (!i) {
for (j = 0; j < 10; j++) {
if (!access(fe_path, R_OK | W_OK)) break;
usleep(100000);
}
}
if (access(fe_path, R_OK | W_OK)) continue;
/* Get frontend info */
fd = tvh_open(fe_path, O_RDONLY | O_NONBLOCK, 0);
for (j = 0; j < 10; j++) {
if ((fd = tvh_open(fe_path, O_RDONLY, 0)) > 0) break;
usleep(100000);
}
if (fd == -1) {
tvhlog(LOG_ERR, "linuxdvb", "unable to open %s", fe_path);
continue;
}
r = ioctl(fd, FE_GET_INFO, &dfi);
#if DVB_VER_ATLEAST(5,10)
if (!r)
r = ioctl(fd, FE_GET_PROPERTY, &cmdseq);
#endif
close(fd);
if(r) {
tvhlog(LOG_ERR, "linuxdvb", "unable to query %s", fe_path);
@ -227,38 +204,188 @@ linuxdvb_adapter_added ( int adapter )
}
/* DVR/DMX (bit of a guess) */
snprintf(dmx_path, sizeof(dmx_path), DMX_PATH, adapter, i);
snprintf(dmx_path, sizeof(dmx_path), DMX_PATH, path, i);
if (access(dmx_path, R_OK | W_OK)) {
snprintf(dmx_path, sizeof(dmx_path), DMX_PATH, adapter, 0);
snprintf(dmx_path, sizeof(dmx_path), DMX_PATH, path, 0);
if (access(dmx_path, R_OK | W_OK)) continue;
}
snprintf(dvr_path, sizeof(dvr_path), DVR_PATH, adapter, i);
snprintf(dvr_path, sizeof(dvr_path), DVR_PATH, path, i);
if (access(dvr_path, R_OK | W_OK)) {
snprintf(dvr_path, sizeof(dvr_path), DVR_PATH, adapter, 0);
snprintf(dvr_path, sizeof(dvr_path), DVR_PATH, path, 0);
if (access(dvr_path, R_OK | W_OK)) continue;
}
/* Create/Find adapter */
pthread_mutex_lock(&global_lock);
if (!la) {
if (!(la = linuxdvb_adapter_find_by_number(adapter))) {
tvhlog(LOG_ERR, "linuxdvb", "failed to find/create adapter%d", adapter);
return;
}
la->la_dvb_number = adapter;
if (!la->la_name) {
char buf[512];
snprintf(buf, sizeof(buf), "%s #%d", dfi.name, la->la_number);
la->la_name = strdup(buf);
/* Create hash for adapter */
SHA1_Init(&sha1);
SHA1_Update(&sha1, (void*)path, strlen(path));
SHA1_Update(&sha1, (void*)dfi.name, strlen(dfi.name));
// TODO: could include more form dfi, and maybe frontend enum
SHA1_Final(uuidbin, &sha1);
idnode_uuid_as_str1(uuidbin, sizeof(uuidbin), uuid);
/* Load config */
conf = hts_settings_load("input/linuxdvb/adapters/%s", uuid);
if (conf)
feconf = htsmsg_get_map(conf, "frontends");
/* Create */
if (!(la = linuxdvb_adapter_create(uuid, conf, path, a, &dfi))) {
tvhlog(LOG_ERR, "linuxdvb", "failed to create %s", path);
return; // Note: save to return here as global_lock is held
}
}
/* Create frontend */
tvhlog(LOG_DEBUG, "linuxdvb", "fe_create(%p, %s, %s, %s)",
la, fe_path, dmx_path, dvr_path);
save |= linuxdvb_frontend_added(la, i, fe_path, dmx_path, dvr_path, &dfi);
linuxdvb_frontend_create(feconf, la, i, fe_path, dmx_path, dvr_path, &dfi);
#if DVB_VER_ATLEAST(5,10)
fetypes[dfi.type] = 1;
for (j = 0; j < cmd.u.buffer.len; j++) {
/* Invalid */
if ((dfi.type = dvb_delsys2type(cmd.u.buffer.data[j])) == -1)
continue;
/* Couldn't find */
if (fetypes[dfi.type])
continue;
/* Create */
linuxdvb_frontend_create(feconf, la, i, fe_path, dmx_path, dvr_path, &dfi);
fetypes[dfi.type] = 1;
}
#endif
pthread_mutex_unlock(&global_lock);
}
if (save)
linuxdvb_device_save(la->la_device);
/* Relock before exit */
pthread_mutex_lock(&global_lock);
/* Save configuration */
if (!conf && la)
linuxdvb_adapter_save(la);
}
static void
linuxdvb_adapter_del ( const char *path )
{
int a;
linuxdvb_frontend_t *lfe, *next;
linuxdvb_adapter_t *la;
tvh_hardware_t *th;
if (sscanf(path, "/dev/dvb/adapter%d", &a) == 1) {
LIST_FOREACH(th, &tvh_hardware, th_link)
if (idnode_is_instance(&th->th_id, &linuxdvb_adapter_class)) {
la = (linuxdvb_adapter_t*)th;
if (!strcmp(path, la->la_rootpath))
break;
}
if (!th) return;
/* Delete the frontends */
for (lfe = LIST_FIRST(&la->la_frontends); lfe != NULL; lfe = next) {
next = LIST_NEXT(lfe, lfe_link);
linuxdvb_frontend_delete(lfe);
}
/* Free memory */
free(la->la_rootpath);
free(la->la_name);
/* Delete */
tvh_hardware_delete((tvh_hardware_t*)la);
}
}
/* **************************************************************************
* Adapter Management
* *************************************************************************/
/*
* Scan for adapters
*/
static void
linuxdvb_adapter_scan ( void )
{
DIR *dir;
struct dirent buf, *de;
char path[1024];
if ((dir = opendir("/dev/dvb"))) {
while (!readdir_r(dir, &buf, &de) && de) {
if (de->d_name[0] == '.') continue;
snprintf(path, sizeof(path), "/dev/dvb/%s", de->d_name);
linuxdvb_adapter_add(path);
}
closedir(dir);
}
}
/*
* /dev/dvb monitor
*/
static void
devdvb_create ( fsmonitor_t *fsm, const char *path )
{
linuxdvb_adapter_add(path);
}
static void
devdvb_delete ( fsmonitor_t *fsm, const char *path )
{
linuxdvb_adapter_del(path);
}
static fsmonitor_t devdvbmon = {
.fsm_create = devdvb_create,
.fsm_delete = devdvb_delete
};
/*
* /dev monitor
*/
static void
dev_create ( fsmonitor_t *fsm, const char *path )
{
if (!strcmp(path, "/dev/dvb")) {
fsmonitor_add("/dev/dvb", &devdvbmon);
linuxdvb_adapter_scan();
}
}
static void
dev_delete ( fsmonitor_t *fsm, const char *path )
{
if (!strcmp(path, "/dev/dvb"))
fsmonitor_del("/dev/dvb", &devdvbmon);
}
static fsmonitor_t devmon = {
.fsm_create = dev_create,
.fsm_delete = dev_delete
};
/*
* Initialise
*/
void
linuxdvb_adapter_init ( void )
{
/* Install monitor on /dev */
(void)fsmonitor_add("/dev", &devmon);
/* Install monitor on /dev/dvb */
if (!access("/dev/dvb", R_OK)) {
(void)fsmonitor_add("/dev/dvb", &devdvbmon);
/* Scan for adapters */
linuxdvb_adapter_scan();
}
}

View file

@ -1,353 +0,0 @@
/*
* Tvheadend - Linux DVB device management
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tvheadend.h"
#include "input.h"
#include "linuxdvb_private.h"
#include "queue.h"
#include "settings.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
/* ***************************************************************************
* Device/BUS Info
* **************************************************************************/
/*
* BUS str table
*/
static struct strtab bustab[] = {
{ "NONE", BUS_NONE },
{ "PCI", BUS_PCI },
{ "USB1", BUS_USB1 },
{ "USB2", BUS_USB2 },
{ "USB3", BUS_USB3 }
};
#define devinfo_bus2str(p) val2str(p, bustab)
#define devinfo_str2bus(p) str2val(p, bustab)
/*
* Get bus information
*/
static void
get_device_info ( device_info_t *di, int a )
{
FILE *fp;
DIR *dp;
struct dirent *de;
uint16_t u16;
int speed;
char path[512], buf[512];
ssize_t c;
int mina = a;
/* Clear */
memset(di, 0, sizeof(device_info_t));
/* Check for subsystem */
#define DVB_DEV_PATH "/sys/class/dvb/dvb%d.frontend0/device"
snprintf(path, sizeof(path), DVB_DEV_PATH "/subsystem", a);
if ((c = readlink(path, buf, sizeof(buf))) != -1) {
buf[c] = '\0';
char *bus = basename(buf);
if (!strcmp(bus, "pci")) {
di->di_bus = BUS_PCI;
snprintf(path, sizeof(path), DVB_DEV_PATH "/subsystem_vendor", a);
if ((fp = fopen(path, "r"))) {
if (fscanf(fp, "0x%hx", &u16) == 1)
di->di_dev = u16;
fclose(fp);
}
di->di_dev <<= 16;
snprintf(path, sizeof(path), DVB_DEV_PATH "/subsystem_device", a);
if ((fp = fopen(path, "r"))) {
if (fscanf(fp, "0x%hx", &u16) == 1)
di->di_dev |= u16;
fclose(fp);
}
} else if (!strcmp(bus, "usb")) {
di->di_bus = BUS_USB1;
snprintf(path, sizeof(path), DVB_DEV_PATH "/idVendor", a);
if ((fp = fopen(path, "r"))) {
if (fscanf(fp, "%hx", &u16) == 1)
di->di_dev = u16;
fclose(fp);
}
di->di_dev <<= 16;
snprintf(path, sizeof(path), DVB_DEV_PATH "/idProduct", a);
if ((fp = fopen(path, "r"))) {
if (fscanf(fp, "%hx", &u16) == 1)
di->di_dev |= u16;
fclose(fp);
}
snprintf(path, sizeof(path), DVB_DEV_PATH "/speed", a);
if ((fp = fopen(path, "r"))) {
if (fscanf(fp, "%d", &speed) == 1) {
if (speed > 480) {
di->di_bus = BUS_USB3;
} else if (speed == 480) {
di->di_bus = BUS_USB2;
}
}
fclose(fp);
}
} else {
tvhlog(LOG_WARNING, "linuxdvb",
"could not determine host connection for adapter%d", a);
}
}
/* Get Path */
snprintf(path, sizeof(path), DVB_DEV_PATH, a);
if ((c = readlink(path, buf, sizeof(buf))) != -1) {
buf[c] = '\0';
strcpy(di->di_path, basename(buf));
}
/* Find minimum adapter number */
snprintf(path, sizeof(path), DVB_DEV_PATH "/dvb", a);
if ((dp = opendir(path))) {
while ((de = readdir(dp))) {
int t;
if ((sscanf(de->d_name, "dvb%d.frontend0", &t)))
if (t < mina) mina = t;
}
closedir(dp);
}
di->di_min_adapter = mina;
/* Create ID */
if (*di->di_path && di->di_dev && di->di_bus) {
snprintf(buf, sizeof(buf), "%s/%s/%04x:%04x",
devinfo_bus2str(di->di_bus), di->di_path,
di->di_dev >> 16, di->di_dev & 0xFFFF);
} else {
snprintf(buf, sizeof(buf), "/dev/dvb/adapter%d", a);
}
di->di_id = strdup(buf);
}
static void
get_min_dvb_adapter ( device_info_t *di )
{
int mina = -1;
char path[512];
DIR *dp;
struct dirent *de;
snprintf(path, sizeof(path), "/sys/bus/%s/devices/%s/dvb",
di->di_bus == BUS_PCI ? "pci" : "usb", di->di_path);
/* Find minimum adapter number */
if ((dp = opendir(path))) {
while ((de = readdir(dp))) {
int t;
if ((sscanf(de->d_name, "dvb%d.frontend0", &t)))
if (mina == -1 || t < mina) mina = t;
}
closedir(dp);
}
di->di_min_adapter = mina;
}
/* ***************************************************************************
* Class
* **************************************************************************/
static void
linuxdvb_device_class_save ( idnode_t *in )
{
linuxdvb_device_save((linuxdvb_device_t*)in);
}
static const char *
linuxdvb_device_class_get_title ( idnode_t *in )
{
return ((linuxdvb_device_t*)in)->ld_devid.di_id;
}
static idnode_set_t *
linuxdvb_device_class_get_childs ( idnode_t *in )
{
linuxdvb_adapter_t *la;
linuxdvb_device_t *ld = (linuxdvb_device_t*)in;
idnode_set_t *is = idnode_set_create();
LIST_FOREACH(la, &ld->ld_adapters, la_link)
idnode_set_add(is, &la->la_id, NULL);
return is;
}
const idclass_t linuxdvb_device_class =
{
.ic_class = "linuxdvb_device",
.ic_caption = "LinuxDVB Device",
.ic_save = linuxdvb_device_class_save,
.ic_get_childs = linuxdvb_device_class_get_childs,
.ic_get_title = linuxdvb_device_class_get_title,
.ic_properties = (const property_t[]){
{
.type = PT_STR,
.id = "devid",
.name = "Device ID",
.opts = PO_RDONLY,
.off = offsetof(linuxdvb_device_t, ld_devid.di_id)
},
{}
}
};
void linuxdvb_device_save ( linuxdvb_device_t *ld )
{
htsmsg_t *m, *e, *l;
linuxdvb_adapter_t *la;
m = htsmsg_create_map();
idnode_save(&ld->th_id, m);
/* Adapters */
l = htsmsg_create_map();
LIST_FOREACH(la, &ld->ld_adapters, la_link) {
e = htsmsg_create_map();
linuxdvb_adapter_save(la, e);
htsmsg_add_msg(l, idnode_uuid_as_str(&la->la_id), e);
}
htsmsg_add_msg(m, "adapters", l);
/* Save */
hts_settings_save(m, "input/linuxdvb/devices/%s",
idnode_uuid_as_str(&ld->th_id));
htsmsg_destroy(m);
}
linuxdvb_device_t *
linuxdvb_device_create0 ( const char *uuid, htsmsg_t *conf )
{
linuxdvb_device_t *ld;
htsmsg_t *e;
htsmsg_field_t *f;
/* Create */
ld = calloc(1, sizeof(linuxdvb_device_t));
if (idnode_insert(&ld->th_id, uuid, &linuxdvb_device_class)) {
free(ld);
return NULL;
}
LIST_INSERT_HEAD(&tvh_hardware, (tvh_hardware_t*)ld, th_link);
/* No config */
if (!conf)
return ld;
/* Load config */
idnode_load(&ld->th_id, conf);
get_min_dvb_adapter(&ld->ld_devid);
/* Adapters */
if ((conf = htsmsg_get_map(conf, "adapters"))) {
HTSMSG_FOREACH(f, conf) {
if (!(e = htsmsg_get_map_by_field(f))) continue;
(void)linuxdvb_adapter_create0(ld, f->hmf_name, e);
}
}
return ld;
}
static linuxdvb_device_t *
linuxdvb_device_find_by_hwid ( const char *hwid )
{
tvh_hardware_t *lh;
LIST_FOREACH(lh, &tvh_hardware, th_link) {
if (idnode_is_instance(&lh->th_id, &linuxdvb_device_class))
if (!strcmp(hwid, ((linuxdvb_device_t*)lh)->ld_devid.di_id ?: ""))
return (linuxdvb_device_t*)lh;
}
return NULL;
}
linuxdvb_device_t *
linuxdvb_device_find_by_adapter ( int a )
{
linuxdvb_device_t *ld;
device_info_t dev;
/* Get device info */
get_device_info(&dev, a);
/* Find existing */
if ((ld = linuxdvb_device_find_by_hwid(dev.di_id))) {
free(dev.di_id);
if (ld->ld_devid.di_bus == BUS_NONE) {
strcpy(ld->ld_devid.di_path, dev.di_path);
ld->ld_devid.di_bus = dev.di_bus;
ld->ld_devid.di_dev = dev.di_dev;
ld->ld_devid.di_min_adapter = dev.di_min_adapter;
}
return ld;
}
/* Create new */
if (!(ld = linuxdvb_device_create0(NULL, NULL))) {
free(dev.di_id);
tvhlog(LOG_ERR, "linuxdvb", "failed to create device for adapter%d", a);
return NULL;
}
/* Copy device info */
memcpy(&ld->ld_devid, &dev, sizeof(dev));
ld->ld_devid.di_id = dev.di_id;
return ld;
}
void linuxdvb_device_init ( int adapter_mask )
{
int a;
DIR *dp;
htsmsg_t *s, *e;
htsmsg_field_t *f;
/* Load configuration */
if ((s = hts_settings_load_r(1, "input/linuxdvb/devices"))) {
HTSMSG_FOREACH(f, s) {
if (!(e = htsmsg_get_map_by_field(f))) continue;
(void)linuxdvb_device_create0(f->hmf_name, e);
}
htsmsg_destroy(s);
}
/* Scan for hardware */
if ((dp = opendir("/dev/dvb"))) {
struct dirent *de;
while ((de = readdir(dp))) {
if (sscanf(de->d_name, "adapter%d", &a) != 1) continue;
if ((0x1 << a) & adapter_mask)
linuxdvb_adapter_added(a);
}
closedir(dp);
}
// TODO: add udev support for hotplug
}

View file

@ -43,9 +43,8 @@ linuxdvb_frontend_input_thread ( void *aux );
static void
linuxdvb_frontend_class_save ( idnode_t *in )
{
linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)in;
if (lfe->lfe_adapter && lfe->lfe_adapter->la_device)
linuxdvb_device_save(lfe->lfe_adapter->la_device);
linuxdvb_adapter_t *la = ((linuxdvb_frontend_t*)in)->lfe_adapter;
linuxdvb_adapter_save(la);
}
static const void*
@ -111,28 +110,28 @@ const idclass_t linuxdvb_frontend_class =
.type = PT_STR,
.id = "fe_path",
.name = "Frontend Path",
.opts = PO_RDONLY,
.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,
.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,
.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,
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(linuxdvb_frontend_t, lfe_number),
},
{
@ -184,7 +183,7 @@ linuxdvb_frontend_dvbs_class_satconf_set ( void *self, const void *str )
linuxdvb_frontend_t *lfe = self;
if (lfe->lfe_satconf && !strcmp(str ?: "", lfe->lfe_satconf->ls_type))
return 0;
linuxdvb_satconf_destroy(lfe->lfe_satconf);
linuxdvb_satconf_delete(lfe->lfe_satconf);
lfe->lfe_satconf = linuxdvb_satconf_create(lfe, str, NULL, NULL);
return 1;
}
@ -262,6 +261,28 @@ const idclass_t linuxdvb_frontend_atsc_class =
* Class methods
* *************************************************************************/
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 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));
return weight;
}
static int
linuxdvb_frontend_is_enabled ( mpegts_input_t *mi )
{
@ -871,41 +892,65 @@ linuxdvb_frontend_tune1
/* **************************************************************************
* Creation/Config
* *************************************************************************/
linuxdvb_frontend_t *
linuxdvb_frontend_create0
( linuxdvb_adapter_t *la, const char *uuid, htsmsg_t *conf, fe_type_t type )
{
const char *str;
const idclass_t *idc;
pthread_t tid;
const char *scuuid = NULL, *sctype = NULL;
htsmsg_t *scconf = NULL;
/* Get type */
if (conf) {
if (!(str = htsmsg_get_str(conf, "type")))
return NULL;
type = dvb_str2type(str);
}
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,
struct dvb_frontend_info *dfi )
{
const idclass_t *idc;
const char *uuid = NULL, *scuuid = NULL, *sctype = NULL;
char id[12], name[256];
linuxdvb_frontend_t *lfe;
htsmsg_t *scconf = NULL;
pthread_t tid;
/* Internal config ID */
snprintf(id, sizeof(id), "%s #%d", dvb_type2str(dfi->type), number);
if (conf)
conf = htsmsg_get_map(conf, id);
if (conf)
uuid = htsmsg_get_str(conf, "uuid");
/* Class */
if (type == FE_QPSK)
if (dfi->type == FE_QPSK)
idc = &linuxdvb_frontend_dvbs_class;
else if (type == FE_QAM)
else if (dfi->type == FE_QAM)
idc = &linuxdvb_frontend_dvbc_class;
else if (type == FE_OFDM)
else if (dfi->type == FE_OFDM)
idc = &linuxdvb_frontend_dvbt_class;
else if (type == FE_ATSC)
else if (dfi->type == FE_ATSC)
idc = &linuxdvb_frontend_atsc_class;
else {
tvherror("linuxdvb", "unknown FE type %d", type);
tvherror("linuxdvb", "unknown FE type %d", dfi->type);
return NULL;
}
linuxdvb_frontend_t *lfe = calloc(1, sizeof(linuxdvb_frontend_t));
lfe->lfe_info.type = type;
/* 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;
memcpy(&lfe->lfe_info, dfi, sizeof(struct dvb_frontend_info));
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;
/* Default name */
if (!lfe->mi_name) {
snprintf(name, sizeof(name), "%s : %s", dfi->name, id);
lfe->mi_name = strdup(name);
}
/* 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;
@ -934,70 +979,19 @@ linuxdvb_frontend_create0
}
/* Create satconf */
if (type == FE_QPSK && !lfe->lfe_satconf)
if (dfi->type == FE_QPSK && !lfe->lfe_satconf)
lfe->lfe_satconf = linuxdvb_satconf_create(lfe, sctype, scuuid, scconf);
/* No conf */
if (!conf)
return lfe;
return lfe;
}
int
linuxdvb_frontend_added
( linuxdvb_adapter_t *la, int fe_num,
const char *fe_path,
const char *dmx_path,
const char *dvr_path,
const struct dvb_frontend_info *fe_info )
{
int save = 0;
linuxdvb_frontend_t *lfe = NULL;
/* Find existing */
LIST_FOREACH(lfe, &la->la_frontends, lfe_link) {
if (lfe->lfe_number == fe_num) {
if (lfe->lfe_info.type != fe_info->type) {
tvhlog(LOG_ERR, "linuxdvb", "detected incorrect fe_type %s != %s",
dvb_type2str(lfe->lfe_info.type), dvb_type2str(fe_info->type));
return 0;
}
break;
}
}
/* Create new */
if (!lfe) {
if (!(lfe = linuxdvb_frontend_create0(la, NULL, NULL, fe_info->type))) {
tvhlog(LOG_ERR, "linuxdvb", "failed to create frontend");
return 0;
}
save = 1;
}
/* Defaults */
if (!lfe->mi_name)
lfe->mi_name = strdup(fe_path);
/* Copy info */
lfe->lfe_number = fe_num;
memcpy(&lfe->lfe_info, fe_info, sizeof(struct dvb_frontend_info));
/* Set paths */
free(lfe->lfe_fe_path);
free(lfe->lfe_dvr_path);
free(lfe->lfe_dmx_path);
lfe->lfe_fe_path = strdup(fe_path);
lfe->lfe_dmx_path = strdup(dmx_path);
lfe->lfe_dvr_path = strdup(dvr_path);
return save;
}
void
linuxdvb_frontend_save ( linuxdvb_frontend_t *lfe, htsmsg_t *m )
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_info.type));
if (lfe->lfe_satconf) {
@ -1006,6 +1000,44 @@ linuxdvb_frontend_save ( linuxdvb_frontend_t *lfe, htsmsg_t *m )
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_info.type), lfe->lfe_number);
htsmsg_add_msg(fe, id, m);
}
void
linuxdvb_frontend_delete ( linuxdvb_frontend_t *lfe )
{
mpegts_mux_instance_t *mmi;
lock_assert(&global_lock);
/* Ensure we're stopped */
if ((mmi = LIST_FIRST(&lfe->mi_mux_active)))
mmi->mmi_mux->mm_stop(mmi->mmi_mux, 1);
/* 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);
/* Finish */
mpegts_input_delete((mpegts_input_t*)lfe);
}
/******************************************************************************

View file

@ -26,7 +26,6 @@
#define LINUXDVB_FREQ_TOL 2000
typedef struct linuxdvb_hardware linuxdvb_hardware_t;
typedef struct linuxdvb_device linuxdvb_device_t;
typedef struct linuxdvb_adapter linuxdvb_adapter_t;
typedef struct linuxdvb_frontend linuxdvb_frontend_t;
typedef struct linuxdvb_satconf linuxdvb_satconf_t;
@ -39,56 +38,15 @@ typedef struct linuxdvb_mux linuxdvb_mux_t;
typedef LIST_HEAD(,linuxdvb_hardware) linuxdvb_hardware_list_t;
typedef TAILQ_HEAD(linuxdvb_satconf_ele_list,linuxdvb_satconf_ele) linuxdvb_satconf_ele_list_t;
/*
* Hardware tree objects
*/
typedef struct device_info
{
char *di_id;
char di_path[128];
enum {
BUS_NONE = 0,
BUS_PCI,
BUS_USB1,
BUS_USB2,
BUS_USB3
} di_bus;
uint32_t di_dev;
int di_min_adapter;
} device_info_t;
struct linuxdvb_device
{
tvh_hardware_t;
/*
* Device info
*/
device_info_t ld_devid;
/*
* Adapters
*/
LIST_HEAD(,linuxdvb_adapter) ld_adapters;
};
struct linuxdvb_adapter
{
idnode_t la_id;
/*
* Link to device
*/
linuxdvb_device_t *la_device;
LIST_ENTRY(linuxdvb_adapter) la_link;
tvh_hardware_t;
/*
* Adapter info
*/
char *la_name;
char *la_rootpath;
uint32_t la_number;
int la_dvb_number;
/*
@ -223,40 +181,26 @@ struct linuxdvb_lnb
* Methods
*/
void linuxdvb_device_init ( int adapter_mask );
void linuxdvb_device_save ( linuxdvb_device_t *ld );
linuxdvb_device_t *linuxdvb_device_create0
(const char *uuid, htsmsg_t *conf);
linuxdvb_device_t * linuxdvb_device_find_by_adapter ( int a );
#define LINUXDVB_SUBSYS_FE 0x01
#define LINUXDVB_SUBSYS_DVR 0x02
void linuxdvb_adapter_save ( linuxdvb_adapter_t *la, htsmsg_t *m );
void linuxdvb_adapter_init ( void );
linuxdvb_adapter_t *linuxdvb_adapter_create0
( linuxdvb_device_t *ld, const char *uuid, htsmsg_t *conf );
void linuxdvb_adapter_added (int a);
void linuxdvb_adapter_save ( linuxdvb_adapter_t *la );
int linuxdvb_adapter_is_free ( linuxdvb_adapter_t *la );
int linuxdvb_adapter_current_weight ( linuxdvb_adapter_t *la );
linuxdvb_adapter_t *linuxdvb_adapter_find_by_hwid ( const char *hwid );
linuxdvb_adapter_t *linuxdvb_adapter_find_by_path ( const char *path );
linuxdvb_frontend_t *
linuxdvb_frontend_create0
( linuxdvb_adapter_t *la, const char *uuid, htsmsg_t *conf, fe_type_t type );
linuxdvb_frontend_create
( htsmsg_t *conf, linuxdvb_adapter_t *la, int number,
const char *fe_path, const char *dmx_path, const char *dvr_path,
struct dvb_frontend_info *dfi );
void linuxdvb_frontend_save ( linuxdvb_frontend_t *lfe, htsmsg_t *m );
int linuxdvb_frontend_added
( linuxdvb_adapter_t *la, int fe_num,
const char *fe_path, const char *dmx_path, const char *dvr_path,
const struct dvb_frontend_info *fe_info );
void linuxdvb_frontend_delete ( linuxdvb_frontend_t *lfe );
void linuxdvb_frontend_add_network
( linuxdvb_frontend_t *lfe, linuxdvb_network_t *net );
@ -360,6 +304,5 @@ linuxdvb_satconf_t *linuxdvb_satconf_create
const char *type, const char *uuid, htsmsg_t *conf );
void linuxdvb_satconf_delete ( linuxdvb_satconf_t *ls );
void linuxdvb_satconf_destroy ( linuxdvb_satconf_t *ls );
#endif /* __TVH_LINUXDVB_PRIVATE_H__ */

View file

@ -115,9 +115,11 @@ linuxdvb_satconf_class_get_title ( idnode_t *p )
static void
linuxdvb_satconf_class_save ( idnode_t *s )
{
#if 0
linuxdvb_satconf_t *ls = (linuxdvb_satconf_t*)s;
linuxdvb_frontend_t *lfe = (linuxdvb_frontend_t*)ls->ls_frontend;
linuxdvb_device_save(lfe->lfe_adapter->la_device);
#endif
}
static const void *
@ -382,12 +384,6 @@ linuxdvb_satconf_type_list ( void *p )
* Create/Delete satconf
* *************************************************************************/
void
linuxdvb_satconf_destroy ( linuxdvb_satconf_t *ls )
{
// TODO
}
linuxdvb_satconf_t *
linuxdvb_satconf_create
( linuxdvb_frontend_t *lfe, const char *type, const char *uuid,

View file

@ -443,7 +443,6 @@ scanfile_find ( const char *id )
/* Type */
if (!(tok = strtok_r(tmp, "/", &s)))
return NULL;
printf("tok = %s\n", tok);
if (!strcasecmp(tok, "dvbt"))
l = &scanfile_regions_DVBT;
else if (!strcasecmp(tok, "dvbc"))
@ -458,7 +457,6 @@ scanfile_find ( const char *id )
/* Region */
if (!(tok = strtok_r(NULL, "/", &s)))
return NULL;
printf("tok = %s\n", tok);
LIST_FOREACH(r, l, sfr_link)
if (!strcmp(r->sfr_id, tok))
break;
@ -467,7 +465,6 @@ scanfile_find ( const char *id )
/* Network */
if (!(tok = strtok_r(NULL, "/", &s)))
return NULL;
printf("tok = %s\n", tok);
LIST_FOREACH(n, &r->sfr_networks, sfn_link)
if (!strcmp(n->sfn_id, tok))
break;

View file

@ -59,6 +59,7 @@
#include "idnode.h"
#include "imagecache.h"
#include "timeshift.h"
#include "fsmonitor.h"
#if ENABLE_LIBAV
#include "libav.h"
#include "plumbing/transcoding.h"
@ -420,6 +421,14 @@ main(int argc, char **argv)
const char *log_debug = NULL, *log_trace = NULL;
char buf[512];
/* Setup global mutexes */
pthread_mutex_init(&ffmpeg_lock, NULL);
pthread_mutex_init(&fork_lock, NULL);
pthread_mutex_init(&global_lock, NULL);
pthread_mutex_init(&atomic_lock, NULL);
pthread_mutex_lock(&global_lock);
pthread_cond_init(&gtimer_cond, NULL);
/* Defaults */
tvheadend_webui_port = 9981;
tvheadend_webroot = NULL;
@ -702,14 +711,7 @@ main(int argc, char **argv)
idnode_init();
hts_settings_init(opt_config);
/* Setup global mutexes */
pthread_mutex_init(&ffmpeg_lock, NULL);
pthread_mutex_init(&fork_lock, NULL);
pthread_mutex_init(&global_lock, NULL);
pthread_mutex_init(&atomic_lock, NULL);
pthread_mutex_lock(&global_lock);
pthread_cond_init(&gtimer_cond, NULL);
/* Initialise clock */
time(&dispatch_clock);
/* Signal handling */
@ -723,6 +725,8 @@ main(int argc, char **argv)
api_init();
fsmonitor_init();
#if ENABLE_LIBAV
libav_init();
transcoding_init();

View file

@ -611,12 +611,6 @@ tvheadend.idnode_grid = function(panel, conf)
var delBtn = null;
var editBtn = null;
/* Selection */
var select = new Ext.grid.CheckboxSelectionModel({
singleSelect : false
});
columns.splice(0, 0, select);
/* Model */
var idnode = new tvheadend.IdNode(d);
for (var i = 0; i < idnode.length(); i++) {
@ -658,6 +652,11 @@ tvheadend.idnode_grid = function(panel, conf)
columns : columns
});
/* Selection */
var select = new Ext.grid.RowSelectionModel({
singleSelect : false
});
/* Event handlers */
store.on('update', function(s, r, o){
var d = (s.getModifiedRecords().length == 0);
@ -995,6 +994,13 @@ tvheadend.idnode_tree = function (conf)
}
});
if (conf.comet) {
tvheadend.comet.on(conf.comet, function(o) {
if (o.reload)
tree.getRootNode().reload();
});
}
// TODO: top-level reload
tvheadend.comet.on('idnodeUpdated', function(o) {
var n = tree.getNodeById(o.uuid);

View file

@ -1,3 +1,3 @@
tvheadend.tvadapters = function() {
return tvheadend.idnode_tree({ url: 'api/hardware/tree', title: 'TV adapters'});
return tvheadend.idnode_tree({ url: 'api/hardware/tree', title: 'TV adapters', comet: 'hardware'});
}