#endif
#include <vlc_common.h>
+#include <vlc_fs.h>
#include <errno.h>
#include <assert.h>
#define DVBv5(minor) \
(DVB_API_VERSION > 5 \
|| (DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= (minor)))
-#if !DVBv5(0)
-# error Linux DVB kernel headers version 2.6.28 or later required.
-#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 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 (dir, path, flags|O_CLOEXEC);
- if (fd != -1)
- fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
- return fd;
-}
typedef struct
{
#ifdef HAVE_DVBPSI
cam_t *cam;
#endif
- struct dvb_frontend_info info;
+ 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
*/
d->obj = obj;
uint8_t adapter = var_InheritInteger (obj, "dvb-adapter");
+ d->device = var_InheritInteger (obj, "dvb-device");
d->dir = dvb_open_adapter (adapter);
if (d->dir == -1)
if (d->budget)
#endif
{
- d->demux = dvb_open_node (d->dir, "demux", 0, O_RDONLY);
+ d->demux = dvb_open_node (d, "demux", O_RDONLY);
if (d->demux == -1)
{
msg_Err (obj, "cannot access demultiplexer: %m");
{
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, "dvr", 0, O_RDONLY);
+ d->demux = dvb_open_node (d, "dvr", O_RDONLY);
if (d->demux == -1)
{
msg_Err (obj, "cannot access DVR: %m");
}
#ifdef HAVE_DVBPSI
- int ca = dvb_open_node (d->dir, "ca", 0, O_RDWR);
+ int ca = dvb_open_node (d, "ca", O_RDWR);
if (ca != -1)
{
d->cam = en50221_Init (obj, ca);
if (d->pids[i].fd != -1)
continue;
- int fd = dvb_open_node (d->dir, "demux", 0, O_RDONLY);
+ int fd = dvb_open_node (d, "demux", O_RDONLY);
if (fd == -1)
goto error;
}
/** Finds a frontend of the correct type */
-static int dvb_find_frontend (dvb_device_t *d, fe_type_t type, fe_caps_t caps)
+static int dvb_open_frontend (dvb_device_t *d)
{
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;
- }
-
- 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;
- }
-
- msg_Dbg (d->obj, "selected frontend %u", n);
- d->frontend = fd;
return 0;
-
- skip:
- close (fd);
+ int fd = dvb_open_node (d, "frontend", O_RDWR);
+ if (fd == -1)
+ {
+ msg_Err (d->obj, "cannot access frontend: %m");
+ return -1;
}
- msg_Err (d->obj, "no suitable frontend found");
- return -1;
+ d->frontend = fd;
+ return 0;
}
+#define dvb_find_frontend(d, sys) (dvb_open_frontend(d))
/**
* Detects supported delivery systems.
*/
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;
- for (unsigned n = 0; n < 256; n++)
+ 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);
+
+ for (size_t i = 0; i < prop[1].u.buffer.len; i++)
{
- 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;
- }
+ unsigned sys = prop[1].u.buffer.data[i];
- struct dvb_frontend_info info;
- if (ioctl (fd, FE_GET_INFO, &info) < 0)
+ if (sys >= (sizeof (systab) / sizeof (systab[0])) || !systab[sys])
{
- msg_Err (d->obj, "cannot get frontend %u info: %m", n);
- close (fd);
+ msg_Warn (d->obj, "unknown delivery system %u", sys);
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,
- };
+ msg_Dbg (d->obj, " system %u", sys);
+ systems |= systab[sys];
+ }
- if (((unsigned)info.type) >= sizeof (types) / sizeof (types[0]))
- {
+ 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);
- continue;
- }
+ }
- unsigned sys = types[info.type];
- if (info.caps & FE_CAN_2G_MODULATION)
- sys |= sys << 1; /* DVB_foo -> DVB_foo|DVB_foo2 */
- systems |= sys;
+ /* 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;
}
{
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.;
}
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)
{
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
fec = dvb_parse_fec (fec);
- if (dvb_find_frontend (d, FE_QAM, FE_IS_STUPID))
+ 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,
+#endif
DTV_FREQUENCY, freq, DTV_MODULATION, mod,
DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec);
}
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");
uint32_t freq = freq_Hz / 1000;
fec = dvb_parse_fec (fec);
- if (dvb_find_frontend (d, FE_QPSK, FE_IS_STUPID))
+ 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,
default: rolloff = PILOT_AUTO; break;
}
- if (dvb_find_frontend (d, FE_QPSK, FE_CAN_2G_MODULATION))
+ 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,
{ 1, TRANSMISSION_MODE_1K },
#endif
{ 2, TRANSMISSION_MODE_2K },
-#if DVBv5(1)
{ 4, TRANSMISSION_MODE_4K },
-#endif
{ 8, TRANSMISSION_MODE_8K },
#if DVBv5(3)
{ 16, TRANSMISSION_MODE_16K },
guard = dvb_parse_guard (guard);
hierarchy = dvb_parse_hierarchy (hierarchy);
- if (dvb_find_frontend (d, FE_OFDM, FE_IS_STUPID))
+ 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, DTV_MODULATION, mod,
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)
+ int transmit_mode, uint32_t guard, uint32_t plp)
{
#if DVBv5(3)
uint32_t mod = dvb_parse_modulation (modstr, QAM_AUTO);
transmit_mode = dvb_parse_transmit_mode (transmit_mode);
guard = dvb_parse_guard (guard);
- if (dvb_find_frontend (d, FE_OFDM, FE_CAN_2G_MODULATION))
+ 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_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");
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
fec = dvb_parse_fec (fec);
- if (dvb_find_frontend (d, FE_QAM, FE_IS_STUPID))
+ 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)
{
-#if DVBv5(1)
uint32_t freq = freq_Hz / 1000;
- if (dvb_find_frontend (d, FE_QPSK, FE_IS_STUPID))
+ 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);
-#else
-# warning ISDB-S needs Linux DVB version 5.1 or later.
- msg_Err (d->obj, "ISDB-S support not compiled-in");
- (void) freq_Hz; (void) ts_id;
- return -1;
-#endif
}
/*** ISDB-T ***/
-#if DVBv5(1)
static int dvb_set_isdbt_layer (dvb_device_t *d, unsigned num,
const isdbt_layer_t *l)
{
DTV_ISDBT_LAYERA_SEGMENT_COUNT + num, count,
DTV_ISDBT_LAYERA_TIME_INTERLEAVING + num, ti);
}
-#endif
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])
{
-#if DVBv5(1)
bandwidth = dvb_parse_bandwidth (bandwidth);
transmit_mode = dvb_parse_transmit_mode (transmit_mode);
guard = dvb_parse_guard (guard);
- if (dvb_find_frontend (d, FE_OFDM, FE_IS_STUPID))
+ 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,
if (dvb_set_isdbt_layer (d, i, layers + i))
return -1;
return 0;
-#else
-# warning ISDB-T needs Linux DVB version 5.1 or later.
- msg_Err (d->obj, "ISDB-T support not compiled-in");
- (void) freq; (void) bandwidth; (void) transmit_mode; (void) guard;
- (void) layers;
- return -1;
-#endif
}
{
unsigned mod = dvb_parse_modulation (modstr, VSB_8);
- if (dvb_find_frontend (d, FE_ATSC, FE_IS_STUPID))
+ 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, DTV_MODULATION, mod);
{
unsigned mod = dvb_parse_modulation (modstr, QAM_AUTO);
- if (dvb_find_frontend (d, FE_QAM, FE_IS_STUPID))
+ if (dvb_find_frontend (d, CQAM))
return -1;
return dvb_set_props (d, 4, DTV_CLEAR, 0,
DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_B,