]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Added DDCI detection and creation
authorJasmin Jessich <jasmin@anw.at>
Sun, 12 Nov 2017 16:50:33 +0000 (17:50 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 16 Nov 2017 07:27:40 +0000 (08:27 +0100)
- Check if a device ciX or secX exists.
- Create also the DD CI structure, if one of the ci pathes have been found.
- Open and close the DD CI in linuxdvb_ca_class_enabled_notify.

Signed-off-by: Jasmin Jessich <jasmin@anw.at>
src/input/mpegts/linuxdvb/linuxdvb_adapter.c
src/input/mpegts/linuxdvb/linuxdvb_ca.c
src/input/mpegts/linuxdvb/linuxdvb_private.h

index f13d70c031ef86c78e85a2cc40e857900821abb0..e13784fe38ea7e7b87f36eb2577ad364a2e7d446 100644 (file)
 #define CA_PATH  "%s/ca%d"
 #define DVR_PATH "%s/dvr%d"
 #define DMX_PATH "%s/demux%d"
+#define CI_PATH  "%s/ci%d"
+#define SEC_PATH "%s/sec%d"
+
+#define MAX_DEV_OPEN_ATTEMPTS 20
 
 /* ***************************************************************************
  * DVB Adapter
@@ -299,19 +303,55 @@ linuxdvb_get_systems(int fd, struct dtv_property *_cmd)
 }
 #endif
 
+#if ENABLE_DDCI
+/* ret:  0 .. DDCI found and usable
+ *      -1 .. DDCI found but not usable
+ *      -2 .. DDCI not found
+ */
+static int
+linuxdvb_check_ddci ( const char *ci_path )
+{
+  int j, fd, ret = -2;
+
+  tvhtrace(LS_DDCI, "checking for DDCI %s", ci_path);
+
+  /* check existence */
+  if (!access(ci_path, R_OK | W_OK)) {
+    for (j = 0; j < MAX_DEV_OPEN_ATTEMPTS; j++) {
+      if ((fd = tvh_open(ci_path, O_WRONLY, 0)) >= 0) break;
+      tvh_safe_usleep(100000);
+    }
+    if (fd >= 0) {
+      close(fd);
+      tvhinfo(LS_DDCI, "DDCI found %s", ci_path);
+      ret = 0;
+    }
+    else {
+      ret = -1;
+      tvherror(LS_DDCI, "unable to open %s", ci_path);
+    }
+  }
+  return ret;
+}
+#endif /* ENABLE_DDCI */
+
 /*
  * Add adapter by path
  */
 static void
 linuxdvb_adapter_add ( const char *path )
 {
-#define MAX_DEV_OPEN_ATTEMPTS 20
   extern int linuxdvb_adapter_mask;
   int a, i, j, r, fd;
   char fe_path[512], dmx_path[512], dvr_path[512], name[132];
 #if ENABLE_LINUXDVB_CA
   char ca_path[512];
   htsmsg_t *caconf = NULL;
+  const char *ci_found = NULL;
+#if ENABLE_DDCI
+  linuxdvb_adapter_t *la_fe = NULL;
+  char ci_path[512];
+#endif
 #endif
   linuxdvb_adapter_t *la = NULL;
   struct dvb_frontend_info dfi;
@@ -325,6 +365,8 @@ linuxdvb_adapter_add ( const char *path )
   linuxdvb_frontend_t *lfe;
 #endif
 
+  tvhtrace(LS_LINUXDVB, "scanning adapter %s", path);
+
   /* Validate the path */
   if (sscanf(path, "/dev/dvb/adapter%d", &a) != 1)
     return;
@@ -437,6 +479,36 @@ linuxdvb_adapter_add ( const char *path )
 
   /* Process each CA device */
 #if ENABLE_LINUXDVB_CA
+  /* A normal DVB card with hard wired CI interface creates the caX device in
+   * the same adapter directory than the frontendX device.
+   * The Digital Device CI interfaces do the same, when the driver is started
+   * with adapter_alloc=3. This parameter is used together with the redirect
+   * feature of the DD CI to inform user mode applications, that the caX device
+   * is hard wired to the DVB tuner. This means the special DDCI feature must
+   * not be activated, when the caX and the frontedX device are present in the
+   * same adapter directory.
+   *
+   * The normal use case for the DD CI is a stand alone CI interface, which can
+   * be used by any tuner in the system, which is not limited to Digital Devices
+   * hardware.
+   * This is the default mode of the driver or started with adapter_alloc=0
+   * parameter. In this mode the caX device is created in a dedicated adapter
+   * directory.
+   *
+   * In both modes also a secX or ciX device is create additionally. In the
+   * first mode (adapter_alloc=3 and redirect) this shall be ignored. In the
+   * second mode it needs to be associated with the caX device and later on
+   * used to send/receive the crypted/decrypted TS stream to/from the CAM.
+   *
+   */
+
+#if ENABLE_DDCI
+  /* remember, if la exists already, which means DD CI is hard wired to the
+   * tuner
+   */
+  la_fe = la;
+#endif
+
   for (i = 0; i < 32; i++) {
     snprintf(ca_path, sizeof(ca_path), CA_PATH, path, i);
     if (access(ca_path, R_OK | W_OK)) continue;
@@ -457,6 +529,34 @@ linuxdvb_adapter_add ( const char *path )
       continue;
     }
 
+#if ENABLE_DDCI
+    /* check for DD CI only, if no frontend was found (stand alone mode) */
+    if (!la_fe) {
+      int ddci_ret;
+
+      /* DD driver uses ciX */
+      snprintf(ci_path, sizeof(ci_path), CI_PATH, path, i);
+      ddci_ret = linuxdvb_check_ddci ( ci_path );
+      if (ddci_ret == -2 ) {
+        /* Mainline driver uses secX */
+        snprintf(ci_path, sizeof(ci_path), SEC_PATH, path, i);
+        ddci_ret = linuxdvb_check_ddci ( ci_path );
+      }
+
+      /* The DD CI device have not been found, or was not usable, so we
+       * ignore the whole caX device also, because we are in DD CI stand alone
+       * mode and this requires a working ciX/secX device.
+       * It would be possible to check for -1 so that it get ignored only in
+       * case of an open error.
+       */
+      if (ddci_ret) {
+        tvherror(LS_LINUXDVB, "ignoring DDCI %s", ca_path);
+        continue;
+      }
+      ci_found = ci_path;
+    }
+#endif /* ENABLE_DDCI */
+
     pthread_mutex_lock(&global_lock);
 
     if (!la) {
@@ -471,10 +571,10 @@ linuxdvb_adapter_add ( const char *path )
     if (conf)
       caconf = htsmsg_get_map(conf, "ca_devices");
 
-    linuxdvb_ca_create(caconf, la, i, ca_path);
+    linuxdvb_ca_create(caconf, la, i, ca_path, ci_found);
     pthread_mutex_unlock(&global_lock);
   }
-#endif
+#endif /* ENABLE_LINUXDVB_CA */
 
   /* Cleanup */
   if (conf)
index b941d3dae2455130c9956ed42a3e6b51b2e3a1f5..3382aba69c83ef59f1a0ec5fb335b2240ea72a5e 100644 (file)
@@ -130,8 +130,13 @@ linuxdvb_ca_class_enabled_notify ( void *p, const char *lang )
       lca->lca_ca_fd = tvh_open(lca->lca_ca_path, O_RDWR | O_NONBLOCK, 0);
       tvhtrace(LS_LINUXDVB, "opening ca%u %s (fd %d)",
                lca->lca_number, lca->lca_ca_path, lca->lca_ca_fd);
-      if (lca->lca_ca_fd >= 0)
+      if (lca->lca_ca_fd >= 0) {
+#if ENABLE_DDCI
+        if (lca->lddci)
+          linuxdvb_ddci_open(lca->lddci);
+#endif
         mtimer_arm_rel(&lca->lca_monitor_timer, linuxdvb_ca_monitor, lca, ms2mono(250));
+      }
     }
   } else {
     tvhtrace(LS_LINUXDVB, "closing ca%u %s (fd %d)",
@@ -151,6 +156,10 @@ linuxdvb_ca_class_enabled_notify ( void *p, const char *lang )
 
       close(lca->lca_ca_fd);
       lca->lca_ca_fd = -1;
+#if ENABLE_DDCI
+      if (lca->lddci)
+        linuxdvb_ddci_close(lca->lddci);
+#endif
     }
 
     idnode_notify_title_changed(&lca->lca_id, lang);
@@ -790,7 +799,8 @@ linuxdvb_ca_monitor ( void *aux )
 
 linuxdvb_ca_t *
 linuxdvb_ca_create
-  ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *ca_path)
+  ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *ca_path,
+    const char *ci_path)
 {
   linuxdvb_ca_t *lca;
   char id[6];
@@ -804,6 +814,11 @@ linuxdvb_ca_create
   lca->lca_capmt_interval = 100;
   lca->lca_capmt_query_interval = 1200;
 
+#if ENABLE_DDCI
+  if (ci_path)
+    lca->lddci = linuxdvb_ddci_create(lca, ci_path);
+#endif
+
   /* Internal config ID */
   snprintf(id, sizeof(id), "ca%u", number);
 
index ee9d7bbb0ca5072154d1cab1a1db8055d42fd20c..efbc7be1a5564ab0a60649a450d677bc97e55ae7 100644 (file)
@@ -435,7 +435,8 @@ int linuxdvb2tvh_delsys ( int delsys );
 
 linuxdvb_ca_t *
 linuxdvb_ca_create
-  ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *ca_path);
+  ( htsmsg_t *conf, linuxdvb_adapter_t *la, int number, const char *ca_path,
+    const char *ci_path );
 
 void linuxdvb_ca_save( linuxdvb_ca_t *lca, htsmsg_t *m );