]> git.sesse.net Git - vlc/blobdiff - modules/access/dtv/access.c
DTV: Linux-specific DiSEqC 1.0 master support
[vlc] / modules / access / dtv / access.c
index 893ff8044b96a40891b523af3a0cd32973dc8c7f..851df49bb4404d3267668e80ff7f0707b55f657f 100644 (file)
@@ -50,7 +50,7 @@
     "Only useful programs are normally demultiplexed from the transponder. " \
     "This option will disable demultiplexing and receive all programs.")
 
-#define FREQ_TEXT N_("Frequency (Hz)")
+#define FREQ_TEXT N_("Frequency (kHz)")
 #define FREQ_LONGTEXT N_( \
     "TV channels are grouped by transponder (a.k.a. multiplex) " \
     "on a given frequency. This is required to tune the receiver.")
@@ -81,11 +81,8 @@ static const char *const modulation_user[] = { N_("Undefined"),
 #define INVERSION_LONGTEXT N_( \
     "If the demodulator cannot detect spectral inversion correctly, " \
     "it needs to be configured manually.")
-//TODO const int inversion_linux[] = { INVERSION_AUTO,
-//    INVERSION_OFF, INVERSION_ON,
-//};
-const int inversion_vlc[] = { -1, 0, 1 };
-static const char *const auto_off_on[] = { N_("Automatic"),
+const int auto_off_on_vlc[] = { -1, 0, 1 };
+static const char *const auto_off_on_user[] = { N_("Automatic"),
     N_("Off"), N_("On") };
 
 #define CODE_RATE_TEXT N_("FEC code rate")
@@ -134,6 +131,59 @@ static const char *const hierarchy_user[] = { N_("Automatic"),
     N_("None"), "1", "2", "4",
 };
 
+#define PILOT_TEXT N_("Pilot")
+
+#define ROLLOFF_TEXT N_("Roll-off factor")
+const int rolloff_vlc[] = { -1,
+    35, 20, 25,
+};
+static const char *const rolloff_user[] = { N_("Automatic"),
+    N_("0.35 (same as DVB-S)"), N_("0.20"), N_("0.25"),
+};
+
+#define POLARIZATION_TEXT N_("Polarization (Voltage)")
+#define POLARIZATION_LONGTEXT N_( \
+    "To select the polarization of the transponder, a different voltage " \
+    "is normally applied to the low noise block-downconverter (LNB).")
+static const char *const polarization_vlc[] = { "", "V", "H", "R", "L" };
+static const char *const polarization_user[] = { N_("Unspecified (0V)"),
+    N_("Vertical (13V)"), N_("Horizontal (18V)"),
+    N_("Circular Right Hand (13V)"), N_("Circular Left Hand (18V)") };
+
+#define HIGH_VOLTAGE_TEXT N_("High LNB voltage")
+#define HIGH_VOLTAGE_LONGTEXT N_( \
+    "If the cables between the satellilte low noise block-downconverter and " \
+    "the receiver are long, higher voltage may be required.\n" \
+    "Not all receivers support this.")
+
+#define LNB_LOW_TEXT N_("Local oscillator low frequency (kHz)")
+#define LNB_HIGH_TEXT N_("Local oscillator high frequency (kHz)")
+#define LNB_LONGTEXT N_( \
+    "The downconverter (LNB) will substract the local oscillator frequency " \
+    "from the satellite transmission frequency. " \
+    "The intermediate frequency (IF) on the RF cable is the result.")
+#define LNB_SWITCH_TEXT N_("Universal LNB switch frequency (kHz)")
+#define LNB_SWITCH_LONGTEXT N_( \
+    "If the satellite transmission frequency exceeds the switch frequency, " \
+    "the oscillator high frequency will be used as reference. " \
+    "Furthermore the automatic continuous 22kHz tone will be sent.")
+#define TONE_TEXT N_("Continuous 22kHz tone")
+#define TONE_LONGTEXT N_( \
+    "A continuous tone at 22kHz can be sent on the cable. " \
+    "This normally selects the higher frequency band from a universal LNB.")
+
+#define SATNO_TEXT N_("DiSEqC LNB number")
+#define SATNO_LONGTEXT N_( \
+    "If the satellite receiver is connected to multiple " \
+    "low noise block-downconverters (LNB) through a DiSEqC 1.0 switch, " \
+    "the correct LNB can be selected (1 to 4). " \
+    "If there is no switch, this parameter should be 0.")
+#ifdef __linux__
+static const int satno_vlc[] = { 0, 1, 2, 3, 4 };
+static const char *const satno_user[] = { N_("Unspecified"),
+    "A/1", "B/2", "C/3", "D/4" };
+#endif
+
 static int  Open (vlc_object_t *);
 static void Close (vlc_object_t *);
 
@@ -145,8 +195,8 @@ vlc_module_begin ()
     set_capability ("access", 0)
     set_callbacks (Open, Close)
     add_shortcut ("dtv", "tv", "dvb", /* "radio", "dab",*/
-                  "cable", "dvb-c", /*"satellite", "dvb-s", "dvb-s2",*/
-                  "terrestrial", "dvb-t", "atsc")
+                  "cable", "dvb-c", "satellite", "dvb-s", "dvb-s2",
+                  "terrestrial", "dvb-t", "atsc", "cqam")
 
     /* All options starting with dvb- can be overriden in the MRL, so they
      * must all be "safe". Nevertheless, we do not mark as safe those that are
@@ -165,19 +215,10 @@ vlc_module_begin ()
     add_bool ("dvb-budget-mode", false, BUDGET_TEXT, BUDGET_LONGTEXT, true)
 #endif
     add_integer ("dvb-frequency", 0, FREQ_TEXT, FREQ_LONGTEXT, false)
-        change_integer_range (0, UINT64_C(0xffffffff) * 1000)
-        change_safe ()
-    add_string ("dvb-modulation", 0,
-                 MODULATION_TEXT, MODULATION_LONGTEXT, false)
-        change_string_list (modulation_vlc, modulation_user, NULL)
+        change_integer_range (0, 107999999)
         change_safe ()
-    /* Legacy modulation option for backward compatibility: TODO */
-    /*add_integer ("dvb-modulation", 0, " ", " ", true)
-        change_integer_range (-1, 256)
-        change_private ()
-        change_safe ()*/
     add_integer ("dvb-inversion", -1, INVERSION_TEXT, INVERSION_LONGTEXT, true)
-        change_integer_list (inversion_vlc, auto_off_on)
+        change_integer_list (auto_off_on_vlc, auto_off_on_user)
         change_safe ()
 
     set_section (N_("Terrestrial reception parameters"), NULL)
@@ -203,41 +244,57 @@ vlc_module_begin ()
         change_integer_list (hierarchy_vlc, hierarchy_user)
         change_safe ()
 
-    set_section (N_("Cable (DVB-C) and satellite (DVB-S) parameters"), NULL)
+    set_section (N_("Cable and satellite reception parameters"), NULL)
+    add_string ("dvb-modulation", 0,
+                 MODULATION_TEXT, MODULATION_LONGTEXT, false)
+        change_string_list (modulation_vlc, modulation_user, NULL)
+        change_safe ()
     add_integer ("dvb-srate", 0, SRATE_TEXT, SRATE_LONGTEXT, false)
         change_integer_range (0, UINT64_C(0xffffffff))
         change_safe ()
     add_string ("dvb-code-rate", "", CODE_RATE_TEXT, CODE_RATE_LONGTEXT, true)
         change_string_list (code_rate_vlc, code_rate_user, NULL)
         change_safe ()
-    /* Legacy FEC option for backward compatibility: TODO */
     add_integer ("dvb-fec", 9, " ", " ", true)
         change_integer_range (0, 9)
         change_private ()
         change_safe ()
-#ifdef FIXME
-    set_section (N_("Satellite (DVB-S) parameters"), NULL)
-    add_integer ("dvb-satno", 0, SATNO_TEXT, SATNO_LONGTEXT, true)
-        change_integer_list (satno_vlc, satno_user)
+    set_section (N_("DVB-S2 parameters"), NULL)
+    add_integer ("dvb-pilot", -1, PILOT_TEXT, PILOT_TEXT, true)
+        change_integer_list (auto_off_on_vlc, auto_off_on_user)
+        change_safe ()
+    add_integer ("dvb-rolloff", -1, ROLLOFF_TEXT, ROLLOFF_TEXT, true)
+        change_integer_list (rolloff_vlc, rolloff_user)
         change_safe ()
-    add_integer ("dvb-voltage", 13, VOLTAGE_TEXT, VOLTAGE_LONGTEXT, true)
-        change_integer_list (voltage_vlc, voltage_user)
+    set_section (N_("Satellite equipment control"), NULL)
+    add_string ("dvb-polarization", "",
+                POLARIZATION_TEXT, POLARIZATION_LONGTEXT, false)
+        change_string_list (polarization_vlc, polarization_user, NULL)
+        change_safe ()
+    add_integer ("dvb-voltage", 13, " ", " ", true)
+        change_integer_range (0, 18)
+        change_private ()
         change_safe ()
+#ifdef __linux__
     add_bool ("dvb-high-voltage", false,
               HIGH_VOLTAGE_TEXT, HIGH_VOLTAGE_LONGTEXT, false)
-    add_integer ("dvb-tone", -1, TONE_TEXT, TONE_LONGTEXT, true)
-        change_integer_list (tone_vlc, auto_off_on)
-        change_safe ()
-    add_integer ("dvb-lnb-lof1", 0, LNB_LOF1_TEXT, LNB_LOF1_LONGTEXT, true)
+#endif
+    add_integer ("dvb-lnb-low", 0, LNB_LOW_TEXT, LNB_LONGTEXT, true)
         change_integer_range (0, 0x7fffffff)
-        change_safe ()
-    add_integer ("dvb-lnb-lof2", 0, LNB_LOF2_TEXT, LNB_LOF2_LONGTEXT, true)
+        add_deprecated_alias ("dvb-lnb-lof1")
+    add_integer ("dvb-lnb-high", 0, LNB_HIGH_TEXT, LNB_LONGTEXT, true)
         change_integer_range (0, 0x7fffffff)
-        change_safe ()
-    add_integer ("dvb-lnb-slof", 0, LNB_SLOF_TEXT, LNB_SLOF_LONGTEXT, true)
+        add_deprecated_alias ("dvb-lnb-lof2")
+    add_integer ("dvb-lnb-switch", 11700000,
+                 LNB_SWITCH_TEXT, LNB_SWITCH_LONGTEXT, true)
         change_integer_range (0, 0x7fffffff)
-        change_safe ()
+        add_deprecated_alias ("dvb-lnb-slof")
+#ifdef __linux
+    add_integer ("dvb-satno", 0, SATNO_TEXT, SATNO_LONGTEXT, true)
+        change_integer_list (satno_vlc, satno_user)
 #endif
+    add_integer ("dvb-tone", -1, TONE_TEXT, TONE_LONGTEXT, true)
+        change_integer_list (auto_off_on_vlc, auto_off_on_user)
 vlc_module_end ()
 
 struct access_sys_t
@@ -247,49 +304,15 @@ struct access_sys_t
 
 struct delsys
 {
-    int (*tune) (vlc_object_t *, dvb_device_t *, uint64_t freq);
+    int (*setup) (vlc_object_t *, dvb_device_t *, unsigned freq);
     /* TODO: scan stuff */
 };
 
 static block_t *Read (access_t *);
 static int Control (access_t *, int, va_list);
-
-static const delsys_t *GuessSystem (const char *scheme, dvb_device_t *dev)
-{
-    /* NOTE: We should guess the delivery system for the "cable", "satellite"
-     * and "terrestrial" shortcuts (i.e. DVB, ISDB, ATSC...). But there is
-     * seemingly no sane way to do get the info with Linux DVB version 5.2.
-     * In particular, the frontend infos distinguish only the modulator class
-     * (QPSK, QAM, OFDM or ATSC).
-     *
-     * Furthermore, if the demodulator supports 2G, we cannot guess whether
-     * 1G or 2G is intended. For backward compatibility, 1G is assumed
-     * (this is not a limitation of Linux DVB). We will probably need something
-     * smarter when 2G (semi automatic) scanning is implemented. */
-    if (!strcasecmp (scheme, "cable"))
-        scheme = "dvb-c";
-    else
-    if (!strcasecmp (scheme, "satellite"))
-        scheme = "dvb-s";
-    else
-    if (!strcasecmp (scheme, "terrestrial"))
-        scheme = "dvb-t";
-
-    if (!strcasecmp (scheme, "atsc"))
-        return &atsc;
-    if (!strcasecmp (scheme, "dvb-c"))
-        return &dvbc;
-    if (!strcasecmp (scheme, "dvb-s"))
-        return &dvbs;
-    if (!strcasecmp (scheme, "dvb-s2"))
-        return &dvbs2;
-    if (!strcasecmp (scheme, "dvb-t"))
-        return &dvbt;
-
-    return dvb_guess_system (dev);
-}
-
+static const delsys_t *GuessSystem (const char *, dvb_device_t *);
+static int Tune (vlc_object_t *, dvb_device_t *, const delsys_t *, unsigned);
+static unsigned var_InheritFrequency (vlc_object_t *);
 
 static int Open (vlc_object_t *obj)
 {
@@ -299,7 +322,7 @@ static int Open (vlc_object_t *obj)
         return VLC_ENOMEM;
 
     var_LocationParse (obj, access->psz_location, "dvb-");
-    uint64_t freq = var_InheritInteger (obj, "dvb-frequency");
+    unsigned freq = var_InheritFrequency (obj);
 
     dvb_device_t *dev = dvb_open (obj, freq != 0);
     if (dev == NULL)
@@ -314,12 +337,12 @@ static int Open (vlc_object_t *obj)
     if (freq != 0)
     {
         const delsys_t *delsys = GuessSystem (access->psz_access, dev);
-        if (delsys == NULL || delsys->tune (obj, dev, freq))
+        if (delsys == NULL || Tune (obj, dev, delsys, freq))
         {
-            msg_Err (obj, "tuning to %"PRIu64" Hz failed", freq);
+            msg_Err (obj, "tuning to %u kHz failed", freq);
             dialog_Fatal (obj, N_("Digital broadcasting"),
                           N_("The selected digital tuner does not support "
-                             "the specified parameters. "
+                             "the specified parameters.\n"
                              "Please check the preferences."));
             goto error;
         }
@@ -447,56 +470,236 @@ static int Control (access_t *access, int query, va_list args)
 }
 
 
-/*** ATSC ***/
-static int atsc_tune (vlc_object_t *obj, dvb_device_t *dev, uint64_t freq)
+/*** Generic tuning ***/
+
+/** Determines which delivery system to use. */
+static const delsys_t *GuessSystem (const char *scheme, dvb_device_t *dev)
+{
+    /* NOTE: We should guess the delivery system for the "cable", "satellite"
+     * and "terrestrial" shortcuts (i.e. DVB, ISDB, ATSC...). But there is
+     * seemingly no sane way to do get the info with Linux DVB version 5.2.
+     * In particular, the frontend infos distinguish only the modulator class
+     * (QPSK, QAM, OFDM or ATSC).
+     *
+     * Furthermore, if the demodulator supports 2G, we cannot guess whether
+     * 1G or 2G is intended. For backward compatibility, 1G is assumed
+     * (this is not a limitation of Linux DVB). We will probably need something
+     * smarter when 2G (semi automatic) scanning is implemented. */
+    if (!strcasecmp (scheme, "cable"))
+        scheme = "dvb-c";
+    else
+    if (!strcasecmp (scheme, "satellite"))
+        scheme = "dvb-s";
+    else
+    if (!strcasecmp (scheme, "terrestrial"))
+        scheme = "dvb-t";
+
+    if (!strcasecmp (scheme, "atsc"))
+        return &atsc;
+    if (!strcasecmp (scheme, "cqam"))
+        return &cqam;
+    if (!strcasecmp (scheme, "dvb-c"))
+        return &dvbc;
+    if (!strcasecmp (scheme, "dvb-s"))
+        return &dvbs;
+    if (!strcasecmp (scheme, "dvb-s2"))
+        return &dvbs2;
+    if (!strcasecmp (scheme, "dvb-t"))
+        return &dvbt;
+
+    return dvb_guess_system (dev);
+}
+
+/** Set parameters and tune the device */
+static int Tune (vlc_object_t *obj, dvb_device_t *dev, const delsys_t *delsys,
+                 unsigned freq)
+{
+    if (delsys->setup (obj, dev, freq)
+     || dvb_set_inversion (dev, var_InheritInteger (obj, "dvb-inversion"))
+     || dvb_tune (dev))
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+static unsigned var_InheritFrequency (vlc_object_t *obj)
+{
+    unsigned freq = var_InheritInteger (obj, "dvb-frequency");
+    if (freq >= 108000000)
+    {
+        msg_Err (obj, "%u kHz frequency is too high.", freq);
+        freq /= 1000;
+        msg_Info (obj, "Assuming %u kHz carrier frequency instead.", freq);
+    }
+    return freq;
+}
+
+static char *var_InheritCodeRate (vlc_object_t *obj)
+{
+    char *code_rate = var_InheritString (obj, "dvb-code-rate");
+    if (code_rate != NULL)
+        return code_rate;
+
+    /* Backward compatibility with VLC < 1.2 (= Linux DVBv3 enum) */
+    unsigned fec = var_InheritInteger (obj, "dvb-fec");
+    if (fec < 9)
+    {
+        static const char linux_dvb[9][5] = {
+            "none", "1/2", "2/3", "3/4", "4/5", "5/6", "6/7", "7/8" };
+        msg_Warn (obj, "\"fec=%u\" option is obsolete. "
+                       "Use \"code-rate=%s\" instead.", fec, linux_dvb[fec]);
+        return strdup (linux_dvb[fec]);
+    }
+    return NULL;
+}
+
+static char *var_InheritModulation (vlc_object_t *obj)
 {
     char *mod = var_InheritString (obj, "dvb-modulation");
+    if (mod == NULL)
+        return mod;
+
+    char *end;
+    unsigned long l = strtol (mod, &end, 0);
+    if (*end != '\0') /* not a number = not from VLC < 1.2 */
+        return mod;
+
+    /* Backward compatibility with VLC < 1.2 */
+    const char *str;
+    switch (l)
+    {
+        case -1:  str = "QPSK";   break;
+        case 0:   str = "QAM";    break;
+        case 8:   str = "8VSB";   break;
+        case 16:  str = "16QAM";  break;
+        case 32:  str = "32QAM";  break;
+        case 64:  str = "64QAM";  break;
+        case 128: str = "128QAM"; break;
+        case 256: str = "256QAM"; break;
+        default:  return mod;
+    }
+
+    msg_Warn (obj, "\"modulation=%ld\" option is obsolete. "
+                   "Use \"modulation=%s\" instead.", l, str);
+    return strdup (str);
+}
+
+
+/*** ATSC ***/
+static int atsc_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
+{
+    char *mod = var_InheritModulation (obj);
 
     int ret = dvb_set_atsc (dev, freq, mod);
     free (mod);
-    if (ret == 0)
-        ret = dvb_tune (dev);
     return ret;
 }
 
-const delsys_t atsc = { .tune = atsc_tune };
+const delsys_t atsc = { .setup = atsc_setup };
+
+static int cqam_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
+{
+    char *mod = var_InheritModulation (obj);
+
+    int ret = dvb_set_cqam (dev, freq, mod);
+    free (mod);
+    return ret;
+}
+
+const delsys_t cqam = { .setup = cqam_setup };
 
 
 /*** DVB-C ***/
-static int dvbc_tune (vlc_object_t *obj, dvb_device_t *dev, uint64_t freq)
+static int dvbc_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
 {
-    char *mod = var_InheritString (obj, "dvb-modulation");
-    char *fec = var_InheritString (obj, "dvb-code-rate");
+    char *mod = var_InheritModulation (obj);
+    char *fec = var_InheritCodeRate (obj);
     unsigned srate = var_InheritInteger (obj, "dvb-srate");
 
     int ret = dvb_set_dvbc (dev, freq, mod, srate, fec);
     free (fec);
     free (mod);
-    if (ret == 0)
-        ret = dvb_tune (dev);
     return ret;
 }
 
-const delsys_t dvbc = { .tune = dvbc_tune };
+const delsys_t dvbc = { .setup = dvbc_setup };
 
 
 /*** DVB-S ***/
-static int dvbs_tune (vlc_object_t *obj, dvb_device_t *dev, uint64_t freq)
+static char var_InheritPolarization (vlc_object_t *obj)
 {
-    (void) dev; (void) freq;
-    msg_Err (obj, "DVB-S not implemented");
-    return -1;
+    char pol;
+    char *polstr = var_InheritString (obj, "dvb-polarization");
+    if (polstr != NULL)
+    {
+        pol = *polstr;
+        free (polstr);
+        if (unlikely(pol >= 'a' && pol <= 'z'))
+            pol -= 'a' - 'A';
+        return pol;
+    }
+
+    /* Backward compatibility with VLC for Linux < 1.2 */
+    unsigned voltage = var_InheritInteger (obj, "dvb-voltage");
+    switch (voltage)
+    {
+        case 13:  pol = 'V'; break;
+        case 18:  pol = 'H'; break;
+        default:  return 0;
+    }
+
+    msg_Warn (obj, "\"voltage=%u\" option is obsolete. "
+                   "Use \"polarization=%c\" instead.", voltage, pol);
+    return pol;
+}
+
+static int sec_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
+{
+    char pol = var_InheritPolarization (obj);
+    unsigned lowf = var_InheritInteger (obj, "dvb-lnb-low");
+    unsigned highf = var_InheritInteger (obj, "dvb-lnb-high");
+    unsigned switchf = var_InheritInteger (obj, "dvb-lnb-switch");
+
+    return dvb_set_sec (dev, freq, pol, lowf, highf, switchf);
+}
+
+static int dvbs_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
+{
+    char *fec = var_InheritCodeRate (obj);
+    uint32_t srate = var_InheritInteger (obj, "dvb-srate");
+
+    /* FIXME: adjust frequency (offset) */
+    int ret = dvb_set_dvbs (dev, freq, srate, fec);
+    free (fec);
+    if (ret == 0)
+        ret = sec_setup (obj, dev, freq);
+    return ret;
 }
 
+static int dvbs2_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
+{
+    char *mod = var_InheritModulation (obj);
+    char *fec = var_InheritCodeRate (obj);
+    uint32_t srate = var_InheritInteger (obj, "dvb-srate");
+    int pilot = var_InheritInteger (obj, "dvb-pilot");
+    int rolloff = var_InheritInteger (obj, "dvb-rolloff");
+
+    /* FIXME: adjust frequency (offset)? */
+    int ret = dvb_set_dvbs2 (dev, freq, mod, srate, fec, pilot, rolloff);
+    free (fec);
+    free (mod);
+    if (ret == 0)
+        ret = sec_setup (obj, dev, freq);
+    return ret;
+}
 
-const delsys_t dvbs = { .tune = dvbs_tune };
-const delsys_t dvbs2 = { .tune = dvbs_tune };
+const delsys_t dvbs = { .setup = dvbs_setup };
+const delsys_t dvbs2 = { .setup = dvbs2_setup };
 
 
 /*** DVB-T ***/
-static int dvbt_tune (vlc_object_t *obj, dvb_device_t *dev, uint64_t freq)
+static int dvbt_setup (vlc_object_t *obj, dvb_device_t *dev, unsigned freq)
 {
-    char *mod = var_InheritString (obj, "dvb-modulation");
+    char *mod = var_InheritModulation (obj);
     char *fec_hp = var_InheritString (obj, "dvb-code-rate-hp");
     char *fec_lp = var_InheritString (obj, "dvb-code-rate-lp");
     char *guard = var_InheritString (obj, "dvb-guard");
@@ -509,9 +712,7 @@ static int dvbt_tune (vlc_object_t *obj, dvb_device_t *dev, uint64_t freq)
     free (fec_lp);
     free (fec_hp);
     free (mod);
-    if (ret == 0)
-        ret = dvb_tune (dev);
     return ret;
 }
 
-const delsys_t dvbt = { .tune = dvbt_tune };
+const delsys_t dvbt = { .setup = dvbt_setup };