/*****************************************************************************
* Copyright © 2011 Rémi Denis-Courmont
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
*
- * This library is distributed in the hope that it will be useful,
+ * 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.
+ * GNU Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- ****************************************************************************/
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <vlc_common.h>
+#include <vlc_fs.h>
#include <errno.h>
#include <assert.h>
#include <poll.h>
#include <unistd.h>
#include <sys/ioctl.h>
+#include <linux/dvb/version.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include "dtv/dtv.h"
+#ifdef HAVE_DVBPSI
+# include "dtv/en50221.h"
+#endif
#ifndef O_SEARCH
# define O_SEARCH O_RDONLY
#endif
-/** Opens the device directory for the specified DVB adapter */
-static int dvb_open_adapter (uint8_t adapter)
-{
- char dir[20];
-
- snprintf (dir, sizeof (dir), "/dev/dvb/adapter%"PRIu8, adapter);
- return open (dir, O_SEARCH|O_DIRECTORY|O_CLOEXEC);
-}
-
-/** Opens the DVB device node of the specified type */
-static int dvb_open_node (int dirfd, uint8_t dev, const char *type, int flags)
-{
- int fd;
- char path[strlen (type) + 4];
-
- snprintf (path, sizeof (path), "%s%"PRIu8, type, dev);
- fd = openat (dirfd, path, flags|O_CLOEXEC);
- if (fd != -1)
- fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
- return fd;
-}
+#define DVBv5(minor) \
+ (DVB_API_VERSION > 5 \
+ || (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= (minor)))
typedef struct
{
return dvb_parse_str (str, mods, sizeof (mods) / sizeof (*mods), def);
}
-static int dvb_parse_fec (const char *str)
+static int dvb_parse_fec (uint32_t fec)
{
- static const dvb_str_map_t rates[] =
+ static const dvb_int_map_t rates[] =
{
- { "", FEC_AUTO },
- { "1/2", FEC_1_2 },
+ { 0, FEC_NONE },
+ { VLC_FEC(1,2), FEC_1_2 },
// TODO: 1/3
// TODO: 1/4
- { "2/3", FEC_2_3 },
- { "3/4", FEC_3_4 },
- { "4/5", FEC_4_5 },
- { "5/6", FEC_5_6 },
- { "6/7", FEC_6_7 },
- { "7/8", FEC_7_8 },
- { "8/9", FEC_8_9 },
- { "9/10", FEC_9_10 },
+ { VLC_FEC(2,3), FEC_2_3 },
+ { VLC_FEC(3,4), FEC_3_4 },
+ { VLC_FEC(3,5), FEC_3_5 },
+ { VLC_FEC(4,5), FEC_4_5 },
+ { VLC_FEC(5,6), FEC_5_6 },
+ { VLC_FEC(6,7), FEC_6_7 },
+ { VLC_FEC(7,8), FEC_7_8 },
+ { VLC_FEC(8,9), FEC_8_9 },
+ { VLC_FEC(9,10), FEC_9_10 },
+ { VLC_FEC_AUTO, FEC_AUTO },
};
- return dvb_parse_str (str, rates, sizeof (rates) / sizeof (*rates),
+ return dvb_parse_int (fec, rates, sizeof (rates) / sizeof (*rates),
FEC_AUTO);
}
struct dvb_device
{
vlc_object_t *obj;
- int frontend;
+ int dir;
int demux;
+ int frontend;
#ifndef USE_DMX
# define MAX_PIDS 256
- int dir;
- uint8_t dev_id;
struct
{
int fd;
uint16_t pid;
} pids[MAX_PIDS];
#endif
- int ca;
- struct dvb_frontend_info info;
+#ifdef HAVE_DVBPSI
+ cam_t *cam;
+#endif
+ uint8_t device;
bool budget;
//size_t buffer_size;
};
+/** Opens the device directory for the specified DVB adapter */
+static int dvb_open_adapter (uint8_t adapter)
+{
+ char dir[20];
+
+ snprintf (dir, sizeof (dir), "/dev/dvb/adapter%"PRIu8, adapter);
+ return vlc_open (dir, O_SEARCH|O_DIRECTORY);
+}
+
+/** Opens the DVB device node of the specified type */
+static int dvb_open_node (dvb_device_t *d, const char *type, int flags)
+{
+ int fd;
+ char path[strlen (type) + 4];
+
+ snprintf (path, sizeof (path), "%s%u", type, d->device);
+ fd = vlc_openat (d->dir, path, flags);
+ if (fd != -1)
+ fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
+ return fd;
+}
+
/**
* Opens the DVB tuner
*/
-dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
+dvb_device_t *dvb_open (vlc_object_t *obj)
{
+ dvb_device_t *d = malloc (sizeof (*d));
+ if (unlikely(d == NULL))
+ return NULL;
+
+ d->obj = obj;
+
uint8_t adapter = var_InheritInteger (obj, "dvb-adapter");
- uint8_t device = var_InheritInteger (obj, "dvb-device");
+ d->device = var_InheritInteger (obj, "dvb-device");
- int dirfd = dvb_open_adapter (adapter);
- if (dirfd == -1)
+ d->dir = dvb_open_adapter (adapter);
+ if (d->dir == -1)
{
msg_Err (obj, "cannot access adapter %"PRIu8": %m", adapter);
+ free (d);
return NULL;
}
-
- dvb_device_t *d = malloc (sizeof (*d));
- if (unlikely(d == NULL))
- {
- close (dirfd);
- return NULL;
- }
-
- d->obj = obj;
d->frontend = -1;
- d->ca = -1;
+#ifdef HAVE_DVBPSI
+ d->cam = NULL;
+#endif
d->budget = var_InheritBool (obj, "dvb-budget-mode");
#ifndef USE_DMX
if (d->budget)
#endif
{
- d->demux = dvb_open_node (dirfd, device, "demux", O_RDONLY);
+ d->demux = dvb_open_node (d, "demux", O_RDONLY);
if (d->demux == -1)
{
msg_Err (obj, "cannot access demultiplexer: %m");
+ close (d->dir);
free (d);
- close (dirfd);
return NULL;
}
}
else
{
- d->dir = fcntl (dirfd, F_DUPFD_CLOEXEC);
- d->dev_id = device;
-
for (size_t i = 0; i < MAX_PIDS; i++)
d->pids[i].pid = d->pids[i].fd = -1;
- d->demux = dvb_open_node (d->dir, device, "dvr", O_RDONLY);
+ d->demux = dvb_open_node (d, "dvr", O_RDONLY);
if (d->demux == -1)
{
msg_Err (obj, "cannot access DVR: %m");
+ close (d->dir);
free (d);
- close (dirfd);
return NULL;
}
#endif
}
- if (tune)
+#ifdef HAVE_DVBPSI
+ int ca = dvb_open_node (d, "ca", O_RDWR);
+ if (ca != -1)
{
- d->frontend = dvb_open_node (dirfd, device, "frontend", O_RDWR);
- if (d->frontend == -1)
- {
- msg_Err (obj, "cannot access frontend %"PRIu8
- " of adapter %"PRIu8": %m", device, adapter);
- goto error;
- }
-
- if (ioctl (d->frontend, FE_GET_INFO, &d->info) < 0)
- {
- msg_Err (obj, "cannot get frontend info: %m");
- goto error;
- }
-
- msg_Dbg (obj, "using frontend: %s", d->info.name);
- msg_Dbg (obj, " type %u, capabilities 0x%08X", d->info.type,
- d->info.caps);
-
- d->ca = dvb_open_node (dirfd, device, "ca", O_RDWR);
- if (d->ca == -1)
- msg_Dbg (obj, "conditional access module not available (%m)");
-
+ d->cam = en50221_Init (obj, ca);
+ if (d->cam == NULL)
+ close (ca);
}
- close (dirfd);
+ else
+ msg_Dbg (obj, "conditional access module not available (%m)");
+#endif
return d;
error:
- close (dirfd);
dvb_close (d);
return NULL;
}
#ifndef USE_DMX
if (!d->budget)
{
- close (d->dir);
for (size_t i = 0; i < MAX_PIDS; i++)
if (d->pids[i].fd != -1)
close (d->pids[i].fd);
}
#endif
- if (d->ca != -1)
- close (d->ca);
+#ifdef HAVE_DVBPSI
+ if (d->cam != NULL)
+ en50221_End (d->cam);
+#endif
if (d->frontend != -1)
close (d->frontend);
close (d->demux);
+ close (d->dir);
free (d);
}
struct pollfd ufd[2];
int n;
+#ifdef HAVE_DVBPSI
+ if (d->cam != NULL)
+ en50221_Poll (d->cam);
+#endif
+
ufd[0].fd = d->demux;
ufd[0].events = POLLIN;
if (d->frontend != -1)
if (d->pids[i].fd != -1)
continue;
- int fd = dvb_open_node (d->dir, d->dev_id, "demux", O_RDONLY);
+ int fd = dvb_open_node (d, "demux", O_RDONLY);
if (fd == -1)
goto error;
#endif
}
-const delsys_t *dvb_guess_system (dvb_device_t *d)
+/** Finds a frontend of the correct type */
+static int dvb_open_frontend (dvb_device_t *d)
{
- assert (d->frontend != -1);
+ if (d->frontend != -1)
+ return 0;
+ int fd = dvb_open_node (d, "frontend", O_RDWR);
+ if (fd == -1)
+ {
+ msg_Err (d->obj, "cannot access frontend: %m");
+ return -1;
+ }
+
+ d->frontend = fd;
+ return 0;
+}
+#define dvb_find_frontend(d, sys) (dvb_open_frontend(d))
+
+/**
+ * Detects supported delivery systems.
+ * @return a bit mask of supported systems (zero on failure)
+ */
+unsigned dvb_enum_systems (dvb_device_t *d)
+{
+ if (dvb_open_frontend (d))
+ return 0;
+#if DVBv5(5)
+ struct dtv_property prop[2] = {
+ { .cmd = DTV_API_VERSION },
+ { .cmd = DTV_ENUM_DELSYS },
+ };
+ struct dtv_properties props = {
+ .num = 2,
+ .props = prop
+ };
+
+ if (ioctl (d->frontend, FE_GET_PROPERTY, &props) < 0)
+ {
+ msg_Err (d->obj, "cannot enumerate frontend systems: %m");
+ goto legacy;
+ }
+
+ static const unsigned systab[] = {
+ [SYS_UNDEFINED] = 0,
+ [SYS_DVBC_ANNEX_A] = DVB_C,
+ [SYS_DVBC_ANNEX_B] = CQAM,
+ [SYS_DVBT] = DVB_T,
+ //[SYS_DSS]
+ [SYS_DVBS] = DVB_S,
+ [SYS_DVBS2] = DVB_S2,
+ //[SYS_DVBH]
+ [SYS_ISDBT] = ISDB_T,
+ [SYS_ISDBS] = ISDB_S,
+ [SYS_ISDBC] = ISDB_C, // no drivers exist (as of 3.3-rc6)
+ [SYS_ATSC] = ATSC,
+ //[SYS_ATSCMH]
+ //[SYS_DMBTH]
+ //[SYS_CMMB]
+ //[SYS_DAB]
+ [SYS_DVBT2] = DVB_T2,
+ //[SYS_TURBO]
+ [SYS_DVBC_ANNEX_C] = ISDB_C, // another name for ISDB-C?
+ };
+ unsigned systems = 0;
- //bool v2 = d->info.caps & FE_CAN_2G_MODULATION;
+ msg_Dbg (d->obj, "probing frontend (kernel API v%u.%u, user API v%u.%u)",
+ prop[0].u.data >> 8, prop[0].u.data & 0xFF,
+ DVB_API_VERSION, DVB_API_VERSION_MINOR);
- switch (d->info.type)
+ for (size_t i = 0; i < prop[1].u.buffer.len; i++)
{
- case FE_QPSK: return /*v2 ? &dvbs2 :*/ &dvbs;
- case FE_QAM: return &dvbc;
- case FE_OFDM: return &dvbt;
- case FE_ATSC: return &atsc;
+ unsigned sys = prop[1].u.buffer.data[i];
+
+ if (sys >= (sizeof (systab) / sizeof (systab[0])) || !systab[sys])
+ {
+ msg_Warn (d->obj, "unknown delivery system %u", sys);
+ continue;
+ }
+ msg_Dbg (d->obj, " system %u", sys);
+ systems |= systab[sys];
}
- return NULL;
+
+ return systems;
+legacy:
+ props.num = 1;
+#else
+ struct dtv_property prop[1] = {
+ { .cmd = DTV_API_VERSION },
+ };
+ struct dtv_properties props = {
+ .num = 1,
+ .props = prop
+ };
+ unsigned systems = 0;
+#endif
+ if (ioctl (d->frontend, FE_GET_PROPERTY, &props) < 0)
+ {
+ msg_Err (d->obj, "unsupported kernel DVB version 3 or older (%m)");
+ return 0;
+ }
+
+ msg_Dbg (d->obj, "probing frontend (kernel API v%u.%u, user API v%u.%u)",
+ prop[0].u.data >> 8, prop[0].u.data & 0xFF,
+ DVB_API_VERSION, DVB_API_VERSION_MINOR);
+#if !DVBv5(5)
+ /* Some delivery systems cannot be detected without the DVB API v5.5.
+ * To run correctly on recent kernels (Linux >= 3.3),
+ * VLC needs to be compiled with up-to-date kernel headers. */
+ if ((prop[0].u.data >> 8) > 5
+ || ((prop[0].u.data >> 8) == 5 && (prop[0].u.data & 0xFF) >= 5))
+ msg_Err (d->obj, "obsolete user API version running on a new kernel");
+ msg_Info (d->obj, "please recompile "PACKAGE_NAME" "PACKAGE_VERSION);
+#endif
+ struct dvb_frontend_info info;
+ if (ioctl (d->frontend, FE_GET_INFO, &info) < 0)
+ {
+ msg_Err (d->obj, "cannot get frontend info: %m");
+ return 0;
+ }
+ msg_Dbg (d->obj, " name %s", info.name);
+ msg_Dbg (d->obj, " type %u, capabilities 0x%08X", info.type, info.caps);
+ msg_Dbg (d->obj, " frequencies %10"PRIu32" to %10"PRIu32,
+ info.frequency_min, info.frequency_max);
+ msg_Dbg (d->obj, " (%"PRIu32" tolerance, %"PRIu32" per step)",
+ info.frequency_tolerance, info.frequency_stepsize);
+ msg_Dbg (d->obj, " bauds rates %10"PRIu32" to %10"PRIu32,
+ info.symbol_rate_min, info.symbol_rate_max);
+ msg_Dbg (d->obj, " (%"PRIu32" tolerance)", info.symbol_rate_tolerance);
+
+ /* DVB first generation and ATSC */
+ switch (info.type)
+ {
+ case FE_QPSK: systems = DVB_S; break;
+ case FE_QAM: systems = DVB_C; break;
+ case FE_OFDM: systems = DVB_T; break;
+ case FE_ATSC: systems = ATSC | CQAM; break;
+ default:
+ msg_Err (d->obj, "unknown frontend type %u", info.type);
+ }
+
+ /* DVB 2nd generation */
+ switch (info.type)
+ {
+ case FE_QPSK:
+ case FE_QAM:
+ case FE_OFDM:
+ if (info.caps & FE_CAN_2G_MODULATION)
+ systems |= systems << 1; /* DVB_foo -> DVB_foo|DVB_foo2 */
+ default:
+ break;
+ }
+
+ /* ISDB (only terrestrial before DVBv5.5) */
+ if (info.type == FE_OFDM)
+ systems |= ISDB_T;
+
+ return systems;
}
float dvb_get_signal_strength (dvb_device_t *d)
{
uint16_t strength;
- if (ioctl (d->frontend, FE_READ_SIGNAL_STRENGTH, &strength) < 0)
+ if (d->frontend == -1
+ || ioctl (d->frontend, FE_READ_SIGNAL_STRENGTH, &strength) < 0)
return 0.;
return strength / 65535.;
}
{
uint16_t snr;
- if (ioctl (d->frontend, FE_READ_SNR, &snr) < 0)
+ if (d->frontend == -1 || ioctl (d->frontend, FE_READ_SNR, &snr) < 0)
return 0.;
return snr / 65535.;
}
+#ifdef HAVE_DVBPSI
+void dvb_set_ca_pmt (dvb_device_t *d, struct dvbpsi_pmt_s *pmt)
+{
+ if (d->cam != NULL)
+ en50221_SetCAPMT (d->cam, pmt);
+}
+#endif
+
static int dvb_vset_props (dvb_device_t *d, size_t n, va_list ap)
{
+ assert (n <= DTV_IOCTL_MAX_MSGS);
+
struct dtv_property buf[n], *prop = buf;
struct dtv_properties props = { .num = n, .props = buf };
- memset (prop, 0, sizeof (prop));
+ memset (buf, 0, sizeof (buf));
while (n > 0)
{
prop->cmd = va_arg (ap, uint32_t);
prop->u.data = va_arg (ap, uint32_t);
- msg_Dbg (d->obj, "setting property %"PRIu32" to %"PRIu32,
+ msg_Dbg (d->obj, "setting property %2"PRIu32" to %"PRIu32,
prop->cmd, prop->u.data);
prop++;
n--;
/*** DVB-C ***/
int dvb_set_dvbc (dvb_device_t *d, uint32_t freq, const char *modstr,
- uint32_t srate, const char *fecstr)
+ uint32_t srate, uint32_t fec)
{
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
- unsigned fec = dvb_parse_fec (fecstr);
+ fec = dvb_parse_fec (fec);
+ if (dvb_find_frontend (d, DVB_C))
+ return -1;
return dvb_set_props (d, 6, DTV_CLEAR, 0,
+#if DVBv5(5)
+ DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_A,
+#else
DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
- DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
+#endif
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod,
DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec);
}
return dvb_parse_int (pol, tab, 5, SEC_VOLTAGE_OFF);
}
-int dvb_set_sec (dvb_device_t *d, uint32_t freq, char pol,
+int dvb_set_sec (dvb_device_t *d, uint64_t freq_Hz, char pol,
uint32_t lowf, uint32_t highf, uint32_t switchf)
{
+ uint32_t freq = freq_Hz / 1000;
+
/* Always try to configure high voltage, but only warn on enable failure */
int val = var_InheritBool (d->obj, "dvb-high-voltage");
if (ioctl (d->frontend, FE_ENABLE_HIGH_LNB_VOLTAGE, &val) < 0 && val)
{ 2500, 2700, 3650, 0 }, /* S band */
{ 950, 2150, 0, 0 }, /* adjusted IF (L band) */
};
- uint_fast16_t mhz = freq / 1000;
+ uint_fast16_t mHz = freq / 1000;
for (size_t i = 0; i < sizeof (tab) / sizeof (tab[0]); i++)
- if (mhz >= tab[i].min && mhz <= tab[i].max)
+ if (mHz >= tab[i].min && mHz <= tab[i].max)
{
lowf = tab[i].low * 1000;
highf = tab[i].high * 1000;
unsigned satno = var_InheritInteger (d->obj, "dvb-satno");
if (satno > 0)
{
- /* DiSEqC 1.0 */
#undef msleep /* we know what we are doing! */
+
+ /* DiSEqC Bus Specification:
+ http://www.eutelsat.com/satellites/pdf/Diseqc/Reference%20docs/bus_spec.pdf */
+
+ /* DiSEqC 1.1 */
+ struct dvb_diseqc_master_cmd uncmd;
+
+ /* DiSEqC 1.0 */
struct dvb_diseqc_master_cmd cmd;
satno = (satno - 1) & 3;
cmd.msg[0] = 0xE0; /* framing: master, no reply, 1st TX */
cmd.msg[1] = 0x10; /* address: all LNB/switch */
- cmd.msg[2] = 0x38; /* command: Write Port Group 0 */
+ cmd.msg[2] = 0x38; /* command: Write Port Group 0 (committed) */
cmd.msg[3] = 0xF0 /* data[0]: clear all bits */
| (satno << 2) /* LNB (A, B, C or D) */
| ((voltage == SEC_VOLTAGE_18) << 1) /* polarization */
| (tone == SEC_TONE_ON); /* option */
cmd.msg[4] = cmd.msg[5] = 0; /* unused */
+ cmd.msg_len = 4; /* length*/
+
msleep (15000); /* wait 15 ms before DiSEqC command */
+ unsigned uncommitted = var_InheritInteger (d->obj, "dvb-uncommitted");
+ if (uncommitted > 0)
+ {
+ uncommitted = (uncommitted - 1) & 3;
+ uncmd.msg[0] = 0xE0; /* framing: master, no reply, 1st TX */
+ uncmd.msg[1] = 0x10; /* address: all LNB/switch */
+ uncmd.msg[2] = 0x39; /* command: Write Port Group 1 (uncommitted) */
+ uncmd.msg[3] = 0xF0 /* data[0]: clear all bits */
+ | (uncommitted << 2) /* LNB (A, B, C or D) */
+ | ((voltage == SEC_VOLTAGE_18) << 1) /* polarization */
+ | (tone == SEC_TONE_ON); /* option */
+ uncmd.msg[4] = uncmd.msg[5] = 0; /* unused */
+ uncmd.msg_len = 4; /* length*/
+ if (ioctl (d->frontend, FE_DISEQC_SEND_MASTER_CMD, &uncmd) < 0)
+ {
+ msg_Err (d->obj, "cannot send DiSEqC command: %m");
+ return -1;
+ }
+ /* Repeat uncommitted command */
+ uncmd.msg[0] = 0xE1; /* framing: master, no reply, repeated TX */
+ if (ioctl (d->frontend, FE_DISEQC_SEND_MASTER_CMD, &uncmd) < 0)
+ {
+ msg_Err (d->obj, "cannot send DiSEqC command: %m");
+ return -1;
+ }
+ msleep(125000); /* wait 125 ms before committed DiSEqC command */
+ }
if (ioctl (d->frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd) < 0)
{
msg_Err (d->obj, "cannot send DiSEqC command: %m");
return dvb_set_props (d, 2, DTV_FREQUENCY, freq, DTV_TONE, tone);
}
-int dvb_set_dvbs (dvb_device_t *d, uint32_t freq,
- uint32_t srate, const char *fecstr)
+int dvb_set_dvbs (dvb_device_t *d, uint64_t freq_Hz,
+ uint32_t srate, uint32_t fec)
{
- unsigned fec = dvb_parse_fec (fecstr);
+ uint32_t freq = freq_Hz / 1000;
+ fec = dvb_parse_fec (fec);
+ if (dvb_find_frontend (d, DVB_S))
+ return -1;
return dvb_set_props (d, 5, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBS,
DTV_FREQUENCY, freq, DTV_SYMBOL_RATE, srate,
DTV_INNER_FEC, fec);
}
-int dvb_set_dvbs2 (dvb_device_t *d, uint32_t freq, const char *modstr,
- uint32_t srate, const char *fecstr, int pilot, int rolloff)
+int dvb_set_dvbs2 (dvb_device_t *d, uint64_t freq_Hz, const char *modstr,
+ uint32_t srate, uint32_t fec, int pilot, int rolloff)
{
+ uint32_t freq = freq_Hz / 1000;
unsigned mod = dvb_parse_modulation (modstr, QPSK);
- unsigned fec = dvb_parse_fec (fecstr);
+ fec = dvb_parse_fec (fec);
switch (pilot)
{
default: rolloff = PILOT_AUTO; break;
}
+ if (dvb_find_frontend (d, DVB_S2))
+ return -1;
return dvb_set_props (d, 8, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBS2,
DTV_FREQUENCY, freq, DTV_MODULATION, mod,
DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec,
/*** DVB-T ***/
+static uint32_t dvb_parse_bandwidth (uint32_t i)
+{
+ switch (i)
+ {
+ //case 0: return 0;
+ case 2: return 1712000;
+ default: return i * 1000000;
+ }
+}
+
static int dvb_parse_transmit_mode (int i)
{
static const dvb_int_map_t tab[] = {
{ -1, TRANSMISSION_MODE_AUTO },
+#if DVBv5(3)
+ { 1, TRANSMISSION_MODE_1K },
+#endif
{ 2, TRANSMISSION_MODE_2K },
-#if DVBv5(1)
{ 4, TRANSMISSION_MODE_4K },
-#endif
{ 8, TRANSMISSION_MODE_8K },
-#if 0
+#if DVBv5(3)
{ 16, TRANSMISSION_MODE_16K },
{ 32, TRANSMISSION_MODE_32K },
#endif
TRANSMISSION_MODE_AUTO);
}
-static int dvb_parse_guard (const char *str)
+static int dvb_parse_guard (uint32_t guard)
{
- static const dvb_str_map_t tab[] = {
- { "", GUARD_INTERVAL_AUTO },
- /*{ "1/128", GUARD_INTERVAL_1_128 },*/
- { "1/16", GUARD_INTERVAL_1_16 },
- { "1/32", GUARD_INTERVAL_1_32 },
- { "1/4", GUARD_INTERVAL_1_4 },
- { "1/8", GUARD_INTERVAL_1_8 },
- /*{ "19/128", GUARD_INTERVAL_19_128 },*/
- /*{ "9/256", GUARD_INTERVAL_9_256 },*/
+ static const dvb_int_map_t tab[] = {
+ { VLC_GUARD(1,4), GUARD_INTERVAL_1_4 },
+ { VLC_GUARD(1,8), GUARD_INTERVAL_1_8 },
+ { VLC_GUARD(1,16), GUARD_INTERVAL_1_16 },
+ { VLC_GUARD(1,32), GUARD_INTERVAL_1_32 },
+#if DVBv5(3)
+ { VLC_GUARD(1,128), GUARD_INTERVAL_1_128 },
+ { VLC_GUARD(19,128), GUARD_INTERVAL_19_128 },
+ { VLC_GUARD(19,256), GUARD_INTERVAL_19_256 },
+#endif
+ { VLC_GUARD_AUTO, GUARD_INTERVAL_AUTO },
};
- return dvb_parse_str (str, tab, sizeof (tab) / sizeof (*tab),
+ return dvb_parse_int (guard, tab, sizeof (tab) / sizeof (*tab),
GUARD_INTERVAL_AUTO);
}
}
int dvb_set_dvbt (dvb_device_t *d, uint32_t freq, const char *modstr,
- const char *fechstr, const char *feclstr, uint32_t bandwidth,
- int transmit_val, const char *guardstr, int hierarchy_val)
+ uint32_t fec_hp, uint32_t fec_lp, uint32_t bandwidth,
+ int transmit_mode, uint32_t guard, int hierarchy)
{
uint32_t mod = dvb_parse_modulation (modstr, QAM_AUTO);
- uint32_t fec_hp = dvb_parse_fec (fechstr);
- uint32_t fec_lp = dvb_parse_fec (feclstr);
- bandwidth *= 1000000;
- uint32_t transmit_mode = dvb_parse_transmit_mode (transmit_val);
- uint32_t guard_it = dvb_parse_guard (guardstr);
- uint32_t hierarchy = dvb_parse_hierarchy (hierarchy_val);
-
+ fec_hp = dvb_parse_fec (fec_hp);
+ fec_lp = dvb_parse_fec (fec_lp);
+ bandwidth = dvb_parse_bandwidth (bandwidth);
+ transmit_mode = dvb_parse_transmit_mode (transmit_mode);
+ guard = dvb_parse_guard (guard);
+ hierarchy = dvb_parse_hierarchy (hierarchy);
+
+ if (dvb_find_frontend (d, DVB_T))
+ return -1;
return dvb_set_props (d, 10, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT,
- DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod,
DTV_CODE_RATE_HP, fec_hp, DTV_CODE_RATE_LP, fec_lp,
DTV_BANDWIDTH_HZ, bandwidth,
DTV_TRANSMISSION_MODE, transmit_mode,
- DTV_GUARD_INTERVAL, guard_it,
+ DTV_GUARD_INTERVAL, guard,
DTV_HIERARCHY, hierarchy);
}
+int dvb_set_dvbt2 (dvb_device_t *d, uint32_t freq, const char *modstr,
+ uint32_t fec, uint32_t bandwidth,
+ int transmit_mode, uint32_t guard, uint32_t plp)
+{
+#if DVBv5(3)
+ uint32_t mod = dvb_parse_modulation (modstr, QAM_AUTO);
+ fec = dvb_parse_fec (fec);
+ bandwidth = dvb_parse_bandwidth (bandwidth);
+ transmit_mode = dvb_parse_transmit_mode (transmit_mode);
+ guard = dvb_parse_guard (guard);
+
+ if (dvb_find_frontend (d, DVB_T2))
+ return -1;
+ return dvb_set_props (d, 8, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT2,
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod,
+ DTV_INNER_FEC, fec, DTV_BANDWIDTH_HZ, bandwidth,
+ DTV_TRANSMISSION_MODE, transmit_mode,
+ DTV_GUARD_INTERVAL, guard,
+ DTV_DVBT2_PLP_ID, plp);
+#else
+# warning DVB-T2 needs Linux DVB version 5.3 or later.
+ msg_Err (d->obj, "DVB-T2 support not compiled-in");
+ (void) freq; (void) modstr; (void) fec; (void) bandwidth;
+ (void) transmit_mode; (void) guard;
+ return -1;
+#endif
+}
+
+
+/*** ISDB-C ***/
+int dvb_set_isdbc (dvb_device_t *d, uint32_t freq, const char *modstr,
+ uint32_t srate, uint32_t fec)
+{
+ unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
+ fec = dvb_parse_fec (fec);
+
+ if (dvb_find_frontend (d, ISDB_C))
+ return -1;
+ return dvb_set_props (d, 6, DTV_CLEAR, 0,
+#if DVBv5(5)
+ DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_C,
+#else
+# warning ISDB-C might need Linux DVB version 5.5 or later.
+ DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
+#endif
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod,
+ DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec);
+}
+
+
+/*** ISDB-S ***/
+int dvb_set_isdbs (dvb_device_t *d, uint64_t freq_Hz, uint16_t ts_id)
+{
+ uint32_t freq = freq_Hz / 1000;
+
+ if (dvb_find_frontend (d, ISDB_S))
+ return -1;
+ return dvb_set_props (d, 5, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ISDBS,
+ DTV_FREQUENCY, freq,
+ DTV_ISDBS_TS_ID, (uint32_t)ts_id);
+}
+
+
+/*** ISDB-T ***/
+static int dvb_set_isdbt_layer (dvb_device_t *d, unsigned num,
+ const isdbt_layer_t *l)
+{
+ uint32_t mod = dvb_parse_modulation (l->modulation, QAM_AUTO);
+ uint32_t fec = dvb_parse_fec (l->code_rate);
+ uint32_t count = l->segment_count;
+ uint32_t ti = l->time_interleaving;
+
+ num *= DTV_ISDBT_LAYERB_FEC - DTV_ISDBT_LAYERA_FEC;
+
+ return dvb_set_props (d, 5, DTV_DELIVERY_SYSTEM, SYS_ISDBT,
+ DTV_ISDBT_LAYERA_FEC + num, fec,
+ DTV_ISDBT_LAYERA_MODULATION + num, mod,
+ DTV_ISDBT_LAYERA_SEGMENT_COUNT + num, count,
+ DTV_ISDBT_LAYERA_TIME_INTERLEAVING + num, ti);
+}
+
+int dvb_set_isdbt (dvb_device_t *d, uint32_t freq, uint32_t bandwidth,
+ int transmit_mode, uint32_t guard,
+ const isdbt_layer_t layers[3])
+{
+ bandwidth = dvb_parse_bandwidth (bandwidth);
+ transmit_mode = dvb_parse_transmit_mode (transmit_mode);
+ guard = dvb_parse_guard (guard);
+
+ if (dvb_find_frontend (d, ISDB_T))
+ return -1;
+ if (dvb_set_props (d, 5, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ISDBT,
+ DTV_FREQUENCY, freq, DTV_BANDWIDTH_HZ, bandwidth,
+ DTV_GUARD_INTERVAL, guard))
+ return -1;
+ for (unsigned i = 0; i < 3; i++)
+ if (dvb_set_isdbt_layer (d, i, layers + i))
+ return -1;
+ return 0;
+}
+
/*** ATSC ***/
int dvb_set_atsc (dvb_device_t *d, uint32_t freq, const char *modstr)
{
unsigned mod = dvb_parse_modulation (modstr, VSB_8);
+ if (dvb_find_frontend (d, ATSC))
+ return -1;
return dvb_set_props (d, 4, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ATSC,
- DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod);
}
int dvb_set_cqam (dvb_device_t *d, uint32_t freq, const char *modstr)
{
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
+ if (dvb_find_frontend (d, CQAM))
+ return -1;
return dvb_set_props (d, 4, DTV_CLEAR, 0,
DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_B,
- DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
+ DTV_FREQUENCY, freq, DTV_MODULATION, mod);
}