]> git.sesse.net Git - vlc/blobdiff - modules/access/dtv/linux.c
Linux DVB: always open demux/dvr0 (and ca0)
[vlc] / modules / access / dtv / linux.c
index 090b9608ae4ef58d610feef319eab0c61a3470d1..4ff14a75ca77fc1c9f0e5debd8cddf443b72fda0 100644 (file)
@@ -37,6 +37,9 @@
 #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
@@ -59,13 +62,13 @@ static int dvb_open_adapter (uint8_t adapter)
 }
 
 /** 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;
@@ -140,24 +143,26 @@ static int dvb_parse_modulation (const char *str, int def)
     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);
 }
 
@@ -165,19 +170,20 @@ static int dvb_parse_fec (const char *str)
 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;
@@ -188,38 +194,38 @@ struct dvb_device
  */
 dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
 {
+    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;
        }
 
@@ -244,17 +250,14 @@ dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
     }
     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
@@ -262,7 +265,7 @@ dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
 
     if (tune)
     {
-        d->frontend = dvb_open_node (dirfd, device, "frontend", O_RDWR);
+        d->frontend = dvb_open_node (d->dir, "frontend", device, O_RDWR);
         if (d->frontend == -1)
         {
             msg_Err (obj, "cannot access frontend %"PRIu8
@@ -279,17 +282,30 @@ dvb_device_t *dvb_open (vlc_object_t *obj, bool tune)
         msg_Dbg (obj, "using frontend: %s", d->info.name);
         msg_Dbg (obj, " type %u, capabilities 0x%08X", d->info.type,
                  d->info.caps);
-
-        d->ca = dvb_open_node (dirfd, device, "ca", O_RDWR);
-        if (d->ca == -1)
+        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);
+
+#ifdef HAVE_DVBPSI
+        int ca = dvb_open_node (d->dir, "ca", 0, O_RDWR);
+        if (ca != -1)
+        {
+            d->cam = en50221_Init (obj, ca);
+            if (d->cam == NULL)
+                close (ca);
+        }
+        else
             msg_Dbg (obj, "conditional access module not available (%m)");
-
+#endif
     }
-    close (dirfd);
     return d;
 
 error:
-    close (dirfd);
     dvb_close (d);
     return NULL;
 }
@@ -299,17 +315,19 @@ void dvb_close (dvb_device_t *d)
 #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);
 }
 
@@ -322,6 +340,11 @@ ssize_t dvb_read (dvb_device_t *d, void *buf, size_t len)
     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)
@@ -388,7 +411,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, d->dev_id, "demux", O_RDONLY);
+        int fd = dvb_open_node (d->dir, "demux", 0, O_RDONLY);
         if (fd == -1)
             goto error;
 
@@ -471,6 +494,14 @@ float dvb_get_snr (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;
@@ -531,10 +562,10 @@ int dvb_tune (dvb_device_t *d)
 
 /*** 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, 6, DTV_CLEAR, 0,
                           DTV_DELIVERY_SYSTEM, SYS_DVBC_ANNEX_AC,
@@ -655,10 +686,9 @@ known:
     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, const char *fecstr)
+int dvb_set_dvbs (dvb_device_t *d, uint32_t freq, uint32_t srate, uint32_t fec)
 {
-    unsigned fec = dvb_parse_fec (fecstr);
+    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,
@@ -666,10 +696,10 @@ int dvb_set_dvbs (dvb_device_t *d, uint32_t freq,
 }
 
 int dvb_set_dvbs2 (dvb_device_t *d, uint32_t freq, const char *modstr,
-                   uint32_t srate, const char *fecstr, int pilot, int rolloff)
+                   uint32_t srate, uint32_t fec, int pilot, int rolloff)
 {
     unsigned mod = dvb_parse_modulation (modstr, QPSK);
-    unsigned fec = dvb_parse_fec (fecstr);
+    fec = dvb_parse_fec (fec);
 
     switch (pilot)
     {
@@ -698,12 +728,15 @@ static int dvb_parse_transmit_mode (int i)
 {
     static const dvb_int_map_t tab[] = {
         { -1, TRANSMISSION_MODE_AUTO },
+#if DVBv5(3)
+        {  1, TRANSMISSION_MODE_1K   },
+#endif
         {  2, TRANSMISSION_MODE_2K   },
 #if DVBv5(1)
         {  4, TRANSMISSION_MODE_4K   },
 #endif
         {  8, TRANSMISSION_MODE_8K   },
-#if 0
+#if DVBv5(3)
         { 16, TRANSMISSION_MODE_16K  },
         { 32, TRANSMISSION_MODE_32K  },
 #endif
@@ -712,19 +745,21 @@ static int dvb_parse_transmit_mode (int i)
                           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,4),    GUARD_INTERVAL_1_4 },
+        { 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 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 },
+#endif
+        { 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);
 }
 
@@ -742,26 +777,51 @@ static int dvb_parse_hierarchy (int i)
 }
 
 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 * 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);
 }
 
+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)
+{
+#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);
+
+    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;
+    return -1;
+#endif
+}
+
 
 /*** ATSC ***/
 int dvb_set_atsc (dvb_device_t *d, uint32_t freq, const char *modstr)