]> git.sesse.net Git - vlc/blobdiff - modules/access/dtv/linux.c
DTV: do not assume __linux__ == HAVE_LINUX_DVB
[vlc] / modules / access / dtv / linux.c
index 2a07d62441aa08b5df50baf3b3276c53d5321e2b..84d78a5d818877825de263b56110f40bd98f7e26 100644 (file)
 #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 vlc_open (dir, O_SEARCH|O_DIRECTORY);
-}
-
-/** 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%u", type, dev);
-    fd = vlc_openat (dir, path, flags);
-    if (fd != -1)
-        fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK);
-    return fd;
-}
 
 typedef struct
 {
@@ -185,11 +160,33 @@ struct dvb_device
 #ifdef HAVE_DVBPSI
     cam_t *cam;
 #endif
-    unsigned systems;
+    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
  */
@@ -202,6 +199,7 @@ dvb_device_t *dvb_open (vlc_object_t *obj)
     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)
@@ -220,7 +218,7 @@ dvb_device_t *dvb_open (vlc_object_t *obj)
     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");
@@ -252,7 +250,7 @@ dvb_device_t *dvb_open (vlc_object_t *obj)
     {
         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");
@@ -264,7 +262,7 @@ dvb_device_t *dvb_open (vlc_object_t *obj)
     }
 
 #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);
@@ -382,7 +380,7 @@ int dvb_add_pid (dvb_device_t *d, uint16_t pid)
         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;
 
@@ -431,9 +429,31 @@ void dvb_remove_pid (dvb_device_t *d, uint16_t pid)
 #endif
 }
 
-/** Enumerates the systems supported by one frontend */
-static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
+/** Finds a frontend of the correct type */
+static int dvb_open_frontend (dvb_device_t *d)
+{
+    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 },
@@ -444,10 +464,10 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
         .props = prop
     };
 
-    if (ioctl (fd, FE_GET_PROPERTY, &props) < 0)
+    if (ioctl (d->frontend, FE_GET_PROPERTY, &props) < 0)
     {
          msg_Err (d->obj, "cannot enumerate frontend systems: %m");
-         return 0;
+         goto legacy;
     }
 
     static const unsigned systab[] = {
@@ -489,6 +509,10 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
         msg_Dbg (d->obj, " system %u", sys);
         systems |= systab[sys];
     }
+
+    return systems;
+legacy:
+    props.num = 1;
 #else
     struct dtv_property prop[1] = {
         { .cmd = DTV_API_VERSION },
@@ -497,8 +521,9 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
         .num = 1,
         .props = prop
     };
-
-    if (ioctl (fd, FE_GET_PROPERTY, &props) < 0)
+    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;
@@ -507,8 +532,17 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
     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 (fd, FE_GET_INFO, &info) < 0)
+    if (ioctl (d->frontend, FE_GET_INFO, &info) < 0)
     {
         msg_Err (d->obj, "cannot get frontend info: %m");
         return 0;
@@ -523,8 +557,6 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
              info.symbol_rate_min, info.symbol_rate_max);
     msg_Dbg (d->obj, " (%"PRIu32" tolerance)", info.symbol_rate_tolerance);
 
-    unsigned systems;
-
     /* DVB first generation and ATSC */
     switch (info.type)
     {
@@ -533,7 +565,6 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
         case FE_OFDM: systems = DVB_T; break;
         case FE_ATSC: systems = ATSC | CQAM; break;
         default:
-            systems = 0;
             msg_Err (d->obj, "unknown frontend type %u", info.type);
     }
 
@@ -552,71 +583,7 @@ static unsigned dvb_probe_frontend (dvb_device_t *d, int fd)
     /* ISDB (only terrestrial before DVBv5.5)  */
     if (info.type == FE_OFDM)
         systems |= ISDB_T;
-#endif
-    return systems;
-}
-
-/** Finds a frontend of the correct type */
-static int dvb_find_frontend (dvb_device_t *d, unsigned system)
-{
-    if (d->frontend != -1)
-    {
-        if (d->systems & system)
-            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;
-        }
-
-        d->systems = dvb_probe_frontend (d, fd);
-        if (d->systems & system)
-        {
-            msg_Dbg (d->obj, "selected frontend %u", n);
-            d->frontend = fd;
-            return 0;
-        }
-
-        msg_Dbg (d->obj, "skipping frontend %u: wrong type", n);
-        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++)
-    {
-        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;
-        }
-
-        systems |= dvb_probe_frontend (d, fd);
-        close (fd);
-    }
     return systems;
 }
 
@@ -624,7 +591,8 @@ 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.;
 }
@@ -633,7 +601,7 @@ float dvb_get_snr (dvb_device_t *d)
 {
     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.;
 }
@@ -653,7 +621,7 @@ static int dvb_vset_props (dvb_device_t *d, size_t n, va_list ap)
     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)
     {
@@ -804,20 +772,56 @@ known:
     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");
@@ -903,9 +907,7 @@ static int dvb_parse_transmit_mode (int i)
         {  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  },
@@ -972,7 +974,7 @@ int dvb_set_dvbt (dvb_device_t *d, uint32_t freq, const char *modstr,
 
 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);
@@ -987,7 +989,8 @@ int dvb_set_dvbt2 (dvb_device_t *d, uint32_t freq, const char *modstr,
                           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");
@@ -1022,7 +1025,6 @@ int dvb_set_isdbc (dvb_device_t *d, uint32_t freq, const char *modstr,
 /*** 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, ISDB_S))
@@ -1030,17 +1032,10 @@ int dvb_set_isdbs (dvb_device_t *d, uint64_t freq_Hz, uint16_t ts_id)
     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)
 {
@@ -1057,13 +1052,11 @@ static int dvb_set_isdbt_layer (dvb_device_t *d, unsigned num,
                           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);
@@ -1078,13 +1071,6 @@ int dvb_set_isdbt (dvb_device_t *d, uint32_t freq, uint32_t 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
 }