#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
}
/** Opens the DVB device node of the specified type */
-static int dvb_open_node (int dirfd, uint8_t dev, const char *type, int flags)
+static int dvb_open_node (int dir, const char *type, unsigned dev, int flags)
{
int fd;
char path[strlen (type) + 4];
snprintf (path, sizeof (path), "%s%"PRIu8, type, dev);
- fd = openat (dirfd, path, flags|O_CLOEXEC);
+ fd = openat (dir, path, flags|O_CLOEXEC);
if (fd != -1)
fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
return fd;
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;
+#ifdef HAVE_DVBPSI
+ cam_t *cam;
+#endif
struct dvb_frontend_info info;
bool budget;
//size_t buffer_size;
/**
* 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");
- 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->dir, "demux", 0, 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->dir, "dvr", 0, 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->dir, "ca", 0, 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);
- msg_Dbg (obj, " frequencies %10"PRIu32" to %10"PRIu32,
- d->info.frequency_min, d->info.frequency_max);
- msg_Dbg (obj, " (%"PRIu32" tolerance, %"PRIu32" per step)",
- d->info.frequency_tolerance, d->info.frequency_stepsize);
- msg_Dbg (obj, " bauds rates %10"PRIu32" to %10"PRIu32,
- d->info.symbol_rate_min, d->info.symbol_rate_max);
- msg_Dbg (obj, " (%"PRIu32" tolerance)",
- d->info.symbol_rate_tolerance);
-
- 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->dir, "demux", 0, 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_find_frontend (dvb_device_t *d, fe_type_t type, fe_caps_t caps)
{
- assert (d->frontend != -1);
+ if (d->frontend != -1)
+ {
+ if (d->info.type == type || (d->info.caps & caps) == caps)
+ return 0; /* already got an adequate frontend */
+
+ close (d->frontend);
+ d->frontend = -1;
+ }
+
+ for (unsigned n = 0; n < 256; n++)
+ {
+ int fd = dvb_open_node (d->dir, "frontend", n, O_RDWR);
+ if (fd == -1)
+ {
+ if (errno == ENOENT)
+ break; /* all frontends already enumerated */
+ msg_Err (d->obj, "cannot access frontend %u; %m", n);
+ continue;
+ }
- //bool v2 = d->info.caps & FE_CAN_2G_MODULATION;
+ if (ioctl (fd, FE_GET_INFO, &d->info) < 0)
+ {
+ msg_Err (d->obj, "cannot get frontend %u info: %m", n);
+ goto skip;
+ }
+
+ msg_Dbg (d->obj, "probing frontend %u: %s", n, d->info.name);
+ msg_Dbg (d->obj, " type %u, capabilities 0x%08X", d->info.type,
+ d->info.caps);
+ msg_Dbg (d->obj, " frequencies %10"PRIu32" to %10"PRIu32,
+ d->info.frequency_min, d->info.frequency_max);
+ msg_Dbg (d->obj, " (%"PRIu32" tolerance, %"PRIu32" per step)",
+ d->info.frequency_tolerance, d->info.frequency_stepsize);
+ msg_Dbg (d->obj, " bauds rates %10"PRIu32" to %10"PRIu32,
+ d->info.symbol_rate_min, d->info.symbol_rate_max);
+ msg_Dbg (d->obj, " (%"PRIu32" tolerance)",
+ d->info.symbol_rate_tolerance);
+
+ if (d->info.type != type || (d->info.caps & caps) != caps)
+ {
+ msg_Dbg (d->obj, "skipping frontend %u: wrong type", n);
+ goto skip;
+ }
- switch (d->info.type)
+ msg_Dbg (d->obj, "selected frontend %u", n);
+ d->frontend = fd;
+ return 0;
+
+ skip:
+ close (fd);
+ }
+
+ msg_Err (d->obj, "no suitable frontend found");
+ return -1;
+}
+
+/**
+ * Detects supported delivery systems.
+ * @return a bit mask of supported systems (zero on failure)
+ */
+unsigned dvb_enum_systems (dvb_device_t *d)
+{
+ unsigned systems = 0;
+
+ for (unsigned n = 0; n < 256; n++)
{
- case FE_QPSK: return /*v2 ? &dvbs2 :*/ &dvbs;
- case FE_QAM: return &dvbc;
- case FE_OFDM: return &dvbt;
- case FE_ATSC: return &atsc;
+ int fd = dvb_open_node (d->dir, "frontend", n, O_RDWR);
+ if (fd == -1)
+ {
+ if (errno == ENOENT)
+ break; /* all frontends already enumerated */
+ msg_Err (d->obj, "cannot access frontend %u; %m", n);
+ continue;
+ }
+
+ struct dvb_frontend_info info;
+ if (ioctl (fd, FE_GET_INFO, &info) < 0)
+ {
+ msg_Err (d->obj, "cannot get frontend %u info: %m", n);
+ close (fd);
+ continue;
+ }
+ close (fd);
+
+ /* Linux DVB lacks detection for non-DVB/non-ATSC demods */
+ static const unsigned types[] = {
+ [FE_QPSK] = DVB_S,
+ [FE_QAM] = DVB_C,
+ [FE_OFDM] = DVB_T,
+ [FE_ATSC] = ATSC,
+ };
+
+ if (((unsigned)info.type) >= sizeof (types) / sizeof (types[0]))
+ {
+ msg_Err (d->obj, "unknown frontend type %u", info.type);
+ continue;
+ }
+
+ unsigned sys = types[info.type];
+ if (info.caps & FE_CAN_2G_MODULATION)
+ sys |= sys << 1; /* DVB_foo -> DVB_foo|DVB_foo2 */
+ systems |= sys;
}
- return NULL;
+ return systems;
}
float dvb_get_signal_strength (dvb_device_t *d)
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)
{
struct dtv_property buf[n], *prop = buf;
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
fec = dvb_parse_fec (fec);
+ if (dvb_find_frontend (d, FE_QAM, FE_IS_STUPID))
+ return -1;
return dvb_set_props (d, 6, DTV_CLEAR, 0,
DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
- DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
+ 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;
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, uint32_t fec)
+int dvb_set_dvbs (dvb_device_t *d, uint64_t freq_Hz,
+ uint32_t srate, uint32_t fec)
{
+ uint32_t freq = freq_Hz / 1000;
fec = dvb_parse_fec (fec);
+ if (dvb_find_frontend (d, FE_QPSK, FE_IS_STUPID))
+ 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,
+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);
fec = dvb_parse_fec (fec);
default: rolloff = PILOT_AUTO; break;
}
+ if (dvb_find_frontend (d, FE_QPSK, FE_CAN_2G_MODULATION))
+ 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,
{
static const dvb_int_map_t tab[] = {
{ -1, TRANSMISSION_MODE_AUTO },
-#if 0
+#if DVBv5(3)
{ 1, TRANSMISSION_MODE_1K },
#endif
{ 2, TRANSMISSION_MODE_2K },
{ 4, TRANSMISSION_MODE_4K },
#endif
{ 8, TRANSMISSION_MODE_8K },
-#if 0
+#if DVBv5(3)
{ 16, TRANSMISSION_MODE_16K },
{ 32, TRANSMISSION_MODE_32K },
#endif
{ 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 0
+#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 },
guard = dvb_parse_guard (guard);
hierarchy = dvb_parse_hierarchy (hierarchy);
+ if (dvb_find_frontend (d, FE_OFDM, FE_IS_STUPID))
+ return -1;
return dvb_set_props (d, 10, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT,
DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
DTV_CODE_RATE_HP, fec_hp, DTV_CODE_RATE_LP, fec_lp,
uint32_t fec, uint32_t bandwidth,
int transmit_mode, uint32_t guard)
{
-#if 0
+#if DVBv5(3)
uint32_t mod = dvb_parse_modulation (modstr, QAM_AUTO);
fec = dvb_parse_fec (fec);
bandwidth *= 1000000;
transmit_mode = dvb_parse_transmit_mode (transmit_mode);
guard = dvb_parse_guard (guard);
+ if (dvb_find_frontend (d, FE_OFDM, FE_CAN_2G_MODULATION))
+ return -1;
return dvb_set_props (d, 8, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT2,
DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
DTV_INNER_FEC, fec, DTV_BANDWIDTH_HZ, bandwidth,
DTV_TRANSMISSION_MODE, transmit_mode,
DTV_GUARD_INTERVAL, guard);
#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;
{
unsigned mod = dvb_parse_modulation (modstr, VSB_8);
+ if (dvb_find_frontend (d, FE_ATSC, FE_IS_STUPID))
+ return -1;
return dvb_set_props (d, 4, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ATSC,
DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
}
{
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
+ if (dvb_find_frontend (d, FE_QAM, FE_IS_STUPID))
+ 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);