#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>
# define O_SEARCH O_RDONLY
#endif
+#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)
{
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);
}
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)
/*** 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);
- return dvb_set_props (d, 5, DTV_CLEAR, 0,
+ return dvb_set_props (d, 6, DTV_CLEAR, 0,
DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
- DTV_FREQUENCY, freq, DTV_MODULATION, mod,
+ DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod,
DTV_SYMBOL_RATE, srate, DTV_INNER_FEC, fec);
}
/*** DVB-S ***/
-/* TODO */
+static unsigned dvb_parse_polarization (char pol)
+{
+ static const dvb_int_map_t tab[5] = {
+ { 0, SEC_VOLTAGE_OFF },
+ { 'H', SEC_VOLTAGE_18 },
+ { 'L', SEC_VOLTAGE_18 },
+ { 'R', SEC_VOLTAGE_13 },
+ { 'V', SEC_VOLTAGE_13 },
+ };
+ return dvb_parse_int (pol, tab, 5, SEC_VOLTAGE_OFF);
+}
+
+int dvb_set_sec (dvb_device_t *d, uint32_t freq, char pol,
+ uint32_t lowf, uint32_t highf, uint32_t switchf)
+{
+ /* 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)
+ msg_Err (d->obj, "cannot enable high LNB voltage: %m");
+
+ /* Windows BDA exposes a higher-level API covering LNB oscillators.
+ * So lets pretend this is platform-specific stuff and do it here. */
+ if (!lowf)
+ { /* Default oscillator frequencies */
+ static const struct
+ {
+ uint16_t min, max, low, high;
+ } tab[] =
+ { /* min max low high */
+ { 10700, 13250, 9750, 10600 }, /* Ku band */
+ { 4500, 4800, 5950, 0 }, /* C band (high) */
+ { 3400, 4200, 5150, 0 }, /* C band (low) */
+ { 2500, 2700, 3650, 0 }, /* S band */
+ { 950, 2150, 0, 0 }, /* adjusted IF (L band) */
+ };
+ 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)
+ {
+ lowf = tab[i].low * 1000;
+ highf = tab[i].high * 1000;
+ goto known;
+ }
+
+ msg_Err (d->obj, "no known band for frequency %u kHz", freq);
+known:
+ msg_Dbg (d->obj, "selected LNB low: %u kHz, LNB high: %u kHz",
+ lowf, highf);
+ }
+
+ /* Use high oscillator frequency? */
+ bool high = highf != 0 && freq > switchf;
+
+ freq -= high ? highf : lowf;
+ if ((int32_t)freq < 0)
+ freq *= -1;
+ assert (freq < 0x7fffffff);
+
+ int tone;
+ switch (var_InheritInteger (d->obj, "dvb-tone"))
+ {
+ case 0: tone = SEC_TONE_OFF; break;
+ case 1: tone = SEC_TONE_ON; break;
+ default: tone = high ? SEC_TONE_ON : SEC_TONE_OFF;
+ }
+
+ /*** LNB selection / DiSEqC ***/
+ unsigned voltage = dvb_parse_polarization (pol);
+ if (dvb_set_props (d, 2, DTV_TONE, SEC_TONE_OFF, DTV_VOLTAGE, voltage))
+ return -1;
+
+ unsigned satno = var_InheritInteger (d->obj, "dvb-satno");
+ if (satno > 0)
+ {
+ /* DiSEqC 1.0 */
+#undef msleep /* we know what we are doing! */
+ 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[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 */
+ msleep (15000); /* wait 15 ms before DiSEqC command */
+ if (ioctl (d->frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd) < 0)
+ {
+ msg_Err (d->obj, "cannot send DiSEqC command: %m");
+ return -1;
+ }
+ msleep (54000 + 15000);
+
+ /* Mini-DiSEqC */
+ satno &= 1;
+ if (ioctl (d->frontend, FE_DISEQC_SEND_BURST,
+ satno ? SEC_MINI_B : SEC_MINI_A) < 0)
+ {
+ msg_Err (d->obj, "cannot send Mini-DiSEqC tone burst: %m");
+ return -1;
+ }
+ msleep (15000);
+ }
+
+ /* Continuous tone (to select high oscillator frequency) */
+ 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)
+{
+ fec = dvb_parse_fec (fec);
+
+ 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, uint32_t fec, int pilot, int rolloff)
+{
+ unsigned mod = dvb_parse_modulation (modstr, QPSK);
+ fec = dvb_parse_fec (fec);
+
+ switch (pilot)
+ {
+ case 0: pilot = PILOT_OFF; break;
+ case 1: pilot = PILOT_ON; break;
+ default: pilot = PILOT_AUTO; break;
+ }
+
+ switch (rolloff)
+ {
+ case 20: rolloff = ROLLOFF_20; break;
+ case 25: rolloff = ROLLOFF_25; break;
+ case 35: rolloff = ROLLOFF_35; break;
+ default: rolloff = PILOT_AUTO; break;
+ }
+
+ 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,
+ DTV_PILOT, pilot, DTV_ROLLOFF, rolloff);
+}
/*** DVB-T ***/
static const dvb_int_map_t tab[] = {
{ -1, TRANSMISSION_MODE_AUTO },
{ 2, TRANSMISSION_MODE_2K },
+#if DVBv5(1)
{ 4, TRANSMISSION_MODE_4K },
+#endif
{ 8, TRANSMISSION_MODE_8K },
#if 0
{ 16, TRANSMISSION_MODE_16K },
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,128), GUARD_INTERVAL_1_128 },*/
+ { VLC_GUARD(1,16), GUARD_INTERVAL_1_16 },
+ { VLC_GUARD(1,32), GUARD_INTERVAL_1_32 },
+ { VLC_GUARD(1,4), GUARD_INTERVAL_1_4 },
+ { VLC_GUARD(1,8), GUARD_INTERVAL_1_8 },
+ /*{ VLC_GUARD(19,128), GUARD_INTERVAL_19_128 },*/
+ /*{ VLC_GUARD(9,256), GUARD_INTERVAL_9_256 },*/
+ { 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);
+ fec_hp = dvb_parse_fec (fec_hp);
+ fec_lp = dvb_parse_fec (fec_lp);
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);
+ transmit_mode = dvb_parse_transmit_mode (transmit_mode);
+ guard = dvb_parse_guard (guard);
+ hierarchy = dvb_parse_hierarchy (hierarchy);
return dvb_set_props (d, 10, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_DVBT,
- DTV_FREQUENCY, freq, DTV_MODULATION, mod,
+ DTV_FREQUENCY, freq * 1000, 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);
}
unsigned mod = dvb_parse_modulation (modstr, VSB_8);
return dvb_set_props (d, 4, DTV_CLEAR, 0, DTV_DELIVERY_SYSTEM, SYS_ATSC,
- DTV_FREQUENCY, freq, DTV_MODULATION, mod);
+ DTV_FREQUENCY, freq * 1000, 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);
+
+ return dvb_set_props (d, 4, DTV_CLEAR, 0,
+ DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_B,
+ DTV_FREQUENCY, freq * 1000, DTV_MODULATION, mod);
}