]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
dvbcam: use stardard caclient interface
authorJaroslav Kysela <perex@perex.cz>
Mon, 16 Oct 2017 15:21:09 +0000 (17:21 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 16 Oct 2017 15:21:09 +0000 (17:21 +0200)
src/descrambler/caclient.c
src/descrambler/caclient.h
src/descrambler/cwc.c
src/descrambler/descrambler.c
src/descrambler/dvbcam.c
src/descrambler/dvbcam.h
src/webui/static/app/caclient.js

index edc3210c1022fc787c4030147a3f49099d5e1711..fe1c8f04353f09e9aafee20e0cc814507c26c57b 100644 (file)
@@ -21,6 +21,9 @@
 #include "caclient.h"
 
 const idclass_t *caclient_classes[] = {
+#if ENABLE_LINUXDVB_CA
+  &caclient_dvbcam_class,
+#endif
 #if ENABLE_CWC
   &caclient_cwc_class,
 #endif
@@ -97,6 +100,10 @@ caclient_create
     tvherror(LS_CACLIENT, "unknown class %s!", s);
     return NULL;
   }
+#if ENABLE_LINUXDVB_CA
+  if (c == &caclient_dvbcam_class)
+    cac = dvbcam_create();
+#endif
 #if ENABLE_CWC
   if (c == &caclient_cwc_class)
     cac = cwc_create();
@@ -359,6 +366,17 @@ caclient_get_status(caclient_t *cac)
   }
 }
 
+void
+caclient_foreach(void (*cb)(caclient_t *))
+{
+  caclient_t *cac;
+
+  pthread_mutex_lock(&caclients_mutex);
+  TAILQ_FOREACH(cac, &caclients, cac_link)
+    cb(cac);
+  pthread_mutex_unlock(&caclients_mutex);
+}
+
 /*
  *  Initialize
  */
@@ -387,6 +405,25 @@ caclient_init(void)
     caclient_create(f->hmf_name, e, 0);
   }
   htsmsg_destroy(c);
+
+#if ENABLE_LINUXDVB_CA
+  {
+    caclient_t *cac;
+    pthread_mutex_lock(&caclients_mutex);
+    TAILQ_FOREACH(cac, &caclients, cac_link)
+      if (idnode_is_instance(&cac->cac_id, &caclient_dvbcam_class))
+        break;
+    pthread_mutex_unlock(&caclients_mutex);
+    if (cac == NULL) {
+      c = htsmsg_create_map();
+      htsmsg_add_str(c, "class", "caclient_dvbcam");
+      htsmsg_add_bool(c, "enabled", 1);
+      htsmsg_add_str(c, "name", "Linux DVB CAM (CI/CI+)");
+      htsmsg_add_str(c, "comment", "Hardware descrambling using Linux DVB CAM devices");
+      caclient_create(NULL, c, 1);
+    }
+  }
+#endif
 }
 
 void
index cb1c275c654fd3ccd8a5d17c5e3733868c61d80c..927034bea0b856ee8c757d70acbffde81aa6b242 100644 (file)
@@ -25,6 +25,7 @@
 struct mpegts_mux;
 
 extern const idclass_t caclient_class;
+extern const idclass_t caclient_dvbcam_class;
 extern const idclass_t caclient_cwc_class;
 extern const idclass_t caclient_cccam_class;
 extern const idclass_t caclient_capmt_class;
@@ -75,6 +76,8 @@ void caclient_caid_update(struct mpegts_mux *mux,
 void caclient_set_status(caclient_t *cac, caclient_status_t status);
 const char *caclient_get_status(caclient_t *cac);
 
+void caclient_foreach(void (*cb)(caclient_t *));
+
 void caclient_init(void);
 void caclient_done(void);
 
@@ -83,6 +86,7 @@ void tsdebugcw_new_keys(struct service *t, int type, uint16_t pid, uint8_t *odd,
 void tsdebugcw_go(void);
 void tsdebugcw_init(void);
 
+caclient_t *dvbcam_create(void);
 caclient_t *cwc_create(void);
 caclient_t *cccam_create(void);
 caclient_t *capmt_create(void);
index 826be59613c6f332bf852c60b7b2735e736c5f1d..f6510d1da9a9493e2a859e0f6919119e32594157 100644 (file)
@@ -1637,7 +1637,7 @@ cwc_service_start(caclient_t *cac, service_t *t)
 
   LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
 
-  descrambler_change_keystate((th_descrambler_t *)td, DS_READY, 0);
+  descrambler_change_keystate(td, DS_READY, 0);
 
 add:
   i = 0;
index c8309705c091417af59f1f29fe6882201d27c67b..000a0a91443696e3fbf2f0613ebacd0ee29791a7 100644 (file)
@@ -273,9 +273,6 @@ descrambler_init ( void )
   ffdecsa_init();
 #endif
   caclient_init();
-#if ENABLE_LINUXDVB_CA
-  dvbcam_init();
-#endif
 
   if ((c = hts_settings_load("descrambler")) != NULL) {
     m = htsmsg_get_list(c, "caid");
@@ -402,10 +399,6 @@ descrambler_service_start ( service_t *t )
   if (t->s_dvb_forcecaid != 0xffff)
     caclient_start(t);
 
-#if ENABLE_LINUXDVB_CA
-  dvbcam_service_start(t);
-#endif
-
   if (t->s_dvb_forcecaid == 0xffff) {
     pthread_mutex_lock(&t->s_stream_mutex);
     descrambler_external(t, 1);
@@ -423,10 +416,6 @@ descrambler_service_stop ( service_t *t )
   void *p;
   int i;
 
-#if ENABLE_LINUXDVB_CA
-  dvbcam_service_stop(t);
-#endif
-
   while ((td = LIST_FIRST(&t->s_descramblers)) != NULL)
     td->td_stop(td);
   t->s_descramble = NULL;
index c071e952953259668f47481c06d656b361378a89..4495cfcaa85483a99c0118608cd4529b90ac9f6b 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  tvheadend, DVB CAM interface
  *  Copyright (C) 2014 Damjan Marion
+ *  Copyright (C) 2017 Jaroslav Kysela
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 
 #define CAIDS_PER_CA_SLOT      16
 
-typedef struct dvbcam_active_service {
-  TAILQ_ENTRY(dvbcam_active_service) link;
-  service_t            *t;
-  uint8_t              *last_pmt;
-  int                  last_pmt_len;
-  linuxdvb_ca_t        *ca;
-  uint8_t              slot;
-} dvbcam_active_service_t;
-
 typedef struct dvbcam_active_cam {
-  TAILQ_ENTRY(dvbcam_active_cam) link;
+  TAILQ_ENTRY(dvbcam_active_cam) global_link;
   uint16_t             caids[CAIDS_PER_CA_SLOT];
   int                  num_caids;
   linuxdvb_ca_t       *ca;
@@ -48,16 +40,57 @@ typedef struct dvbcam_active_cam {
   int                  active_programs;
 } dvbcam_active_cam_t;
 
+typedef struct dvbcam_active_service {
+  th_descrambler_t;
+  TAILQ_ENTRY(dvbcam_active_service) global_link;
+  LIST_ENTRY(dvbcam_active_service)  dvbcam_link;
+  uint8_t             *last_pmt;
+  int                  last_pmt_len;
+  uint16_t             caid;
+  dvbcam_active_cam_t *ac;
+} dvbcam_active_service_t;
+
+typedef struct dvbcam {
+  caclient_t;
+  LIST_HEAD(,dvbcam_active_service) services;
+  int limit;
+} dvbcam_t;
+
 TAILQ_HEAD(,dvbcam_active_service) dvbcam_active_services;
 TAILQ_HEAD(,dvbcam_active_cam) dvbcam_active_cams;
 
 pthread_mutex_t dvbcam_mutex;
 
+/*
+ *
+ */
+static void
+dvbcam_status_update0(caclient_t *cac)
+{
+  int status = CACLIENT_STATUS_NONE;
+
+  if (TAILQ_FIRST(&dvbcam_active_cams))
+    status = CACLIENT_STATUS_CONNECTED;
+  caclient_set_status(cac, status);
+}
+
+/*
+ *
+ */
+static void
+dvbcam_status_update(void)
+{
+  caclient_foreach(dvbcam_status_update0);
+}
+
+/*
+ *
+ */
 void
 dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids,
                     int num_caids)
 {
-  dvbcam_active_cam_t *ac;
+  dvbcam_active_cam_t *ac, *ac_first;
 
   tvhtrace(LS_DVBCAM, "register cam ca %p slot %u num_caids %u",
            lca, slot, num_caids);
@@ -74,70 +107,108 @@ dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids,
 
   pthread_mutex_lock(&dvbcam_mutex);
 
-  TAILQ_INSERT_TAIL(&dvbcam_active_cams, ac, link);
+  ac_first = TAILQ_FIRST(&dvbcam_active_cams);
+  TAILQ_INSERT_TAIL(&dvbcam_active_cams, ac, global_link);
+
+  if (ac_first == NULL)
+    dvbcam_status_update();
 
   pthread_mutex_unlock(&dvbcam_mutex);
+
 }
 
+/*
+ *
+ */
 void
 dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot)
 {
-  dvbcam_active_cam_t *ac, *ac_tmp;
+  dvbcam_active_cam_t *ac, *ac_next;
   dvbcam_active_service_t *as;
 
   tvhtrace(LS_DVBCAM, "unregister cam lca %p slot %u", lca, slot);
 
   pthread_mutex_lock(&dvbcam_mutex);
 
-  /* remove pointer to this CAM in all active services */
-  TAILQ_FOREACH(as, &dvbcam_active_services, link)
-    if (as->ca == lca && as->slot == slot)
-      as->ca = NULL;
-
   /* delete entry */
-  for (ac = TAILQ_FIRST(&dvbcam_active_cams); ac != NULL; ac = ac_tmp) {
-    ac_tmp = TAILQ_NEXT(ac, link);
-    if(ac && ac->ca == lca && ac->slot == slot) {
-      TAILQ_REMOVE(&dvbcam_active_cams, ac, link);
+  for (ac = TAILQ_FIRST(&dvbcam_active_cams); ac != NULL; ac = ac_next) {
+    ac_next = TAILQ_NEXT(ac, global_link);
+    if (ac->ca == lca && ac->slot == slot) {
+      TAILQ_REMOVE(&dvbcam_active_cams, ac, global_link);
+      /* remove pointer to this CAM in all active services */
+      TAILQ_FOREACH(as, &dvbcam_active_services, global_link)
+        if (as->ac == ac)
+          as->ac = NULL;
       free(ac);
     }
   }
 
+  if (TAILQ_EMPTY(&dvbcam_active_cams))
+    dvbcam_status_update();
+
   pthread_mutex_unlock(&dvbcam_mutex);
 }
 
+/*
+ *
+ */
+static int
+dvbcam_ca_lookup(dvbcam_active_cam_t *ac, mpegts_input_t *input, uint16_t caid)
+{
+  extern const idclass_t linuxdvb_frontend_class;
+  linuxdvb_frontend_t *lfe = NULL;
+  int i;
+
+  if (ac->ca == NULL)
+    return 0;
+
+  if (idnode_is_instance(&input->ti_id, &linuxdvb_frontend_class))
+    lfe = (linuxdvb_frontend_t*)input;
+
+  if (lfe == NULL || ac->ca->lca_adapter != lfe->lfe_adapter)
+    return 0;
+
+  for (i = 0; i < ac->num_caids; i++)
+    if (ac->caids[i] == caid)
+      return 1;
+
+  return 0;
+}
+
+/*
+ *
+ */
 void
 dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len)
 {
-  linuxdvb_frontend_t *lfe;
-  dvbcam_active_cam_t *ac = NULL, *ac2;
-  dvbcam_active_service_t *as = NULL, *as2;
-  elementary_stream_t *st;
-  caid_t *c;
+  dvbcam_active_cam_t *ac;
+  dvbcam_active_service_t *as;
   uint8_t list_mgmt;
   int is_update = 0;
-  int i;
-
-  lfe = (linuxdvb_frontend_t*) s->s_dvb_active_input;
-
-  if (!lfe)
-    return;
 
+  pthread_mutex_lock(&s->s_stream_mutex);
   pthread_mutex_lock(&dvbcam_mutex);
 
   /* find this service in the list of active services */
-  TAILQ_FOREACH(as2, &dvbcam_active_services, link)
-    if (as2->t == (service_t *) s) {
-      as = as2;
+  TAILQ_FOREACH(as, &dvbcam_active_services, global_link)
+    if (as->td_service == (service_t *)s)
       break;
-    }
 
-  if (!as) {
+  if (as == NULL) {
     tvhtrace(LS_DVBCAM, "cannot find active service entry");
-       goto done;
+    goto done;
+  }
+
+  ac = as->ac;
+  if (!ac) {
+    tvhtrace(LS_DVBCAM, "cannot find active cam entry");
+    goto done;
   }
 
-  if(as->last_pmt) {
+  if (as->last_pmt) {
+    /* exact match - do nothing */
+    if (as->last_pmt_len == len && memcmp(as->last_pmt, ptr, len) == 0)
+      goto done;
     free(as->last_pmt);
     is_update = 1;
   }
@@ -146,118 +217,178 @@ dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len)
   memcpy(as->last_pmt, ptr, len);
   as->last_pmt_len = len;
 
-  /*if this is update just send updated CAPMT to CAM */
+  /* if this is update just send updated CAPMT to CAM */
   if (is_update) {
-
     tvhtrace(LS_DVBCAM, "CAPMT sent to CAM (update)");
     list_mgmt = CA_LIST_MANAGEMENT_UPDATE;
-    goto enqueue;
+  } else {
+    list_mgmt = ac->active_programs ? CA_LIST_MANAGEMENT_ADD :
+                                    CA_LIST_MANAGEMENT_ONLY;
+    ac->active_programs++;
   }
 
-  /* check all ellementary streams for CAIDs and find CAM */
-  TAILQ_FOREACH(st, &s->s_components, es_link) {
-    LIST_FOREACH(c, &st->es_caids, link) {
-      TAILQ_FOREACH(ac2, &dvbcam_active_cams, link) {
-        for(i=0;i<ac2->num_caids;i++) {
-          if(ac2->ca && ac2->ca->lca_adapter == lfe->lfe_adapter &&
-             ac2->caids[i] == c->caid)
-          {
-            as->ca = ac2->ca;
-            as->slot = ac2->slot;
-            ac = ac2;
-            goto end_of_search_for_cam;
-          }
-        }
-      }
-    }
-  }
+  descrambler_external((service_t *)s, 1);
+  linuxdvb_ca_enqueue_capmt(ac->ca, ac->slot, as->last_pmt, as->last_pmt_len,
+                            list_mgmt, CA_PMT_CMD_ID_OK_DESCRAMBLING);
+done:
+  pthread_mutex_unlock(&dvbcam_mutex);
+  pthread_mutex_unlock(&s->s_stream_mutex);
+}
 
-end_of_search_for_cam:
+static void
+dvbcam_service_destroy(th_descrambler_t *td)
+{
+  dvbcam_active_service_t *as = (dvbcam_active_service_t *)td;
+  dvbcam_active_cam_t *ac;
+  int do_active_programs = 0;
 
-  if (!ac) {
-    tvhtrace(LS_DVBCAM, "cannot find active cam entry");
-    goto done;
+  pthread_mutex_lock(&dvbcam_mutex);
+  ac = as->ac;
+  if (as->last_pmt) {
+    linuxdvb_ca_enqueue_capmt(ac->ca, ac->slot, as->last_pmt,
+                              as->last_pmt_len,
+                              CA_LIST_MANAGEMENT_UPDATE,
+                              CA_PMT_CMD_ID_NOT_SELECTED);
+    free(as->last_pmt);
+    do_active_programs = 1;
   }
-  tvhtrace(LS_DVBCAM, "found active cam entry");
-
-  if (ac->active_programs++)
-    list_mgmt = CA_LIST_MANAGEMENT_ADD;
-  else
-    list_mgmt = CA_LIST_MANAGEMENT_ONLY;
-
-enqueue:
-  if (as->ca) {
-    pthread_mutex_lock(&s->s_stream_mutex);
-    descrambler_external((service_t *)s, 1);
-    pthread_mutex_unlock(&s->s_stream_mutex);
-    linuxdvb_ca_enqueue_capmt(as->ca, as->slot, as->last_pmt, as->last_pmt_len,
-                              list_mgmt, CA_PMT_CMD_ID_OK_DESCRAMBLING);
+  LIST_REMOVE(as, dvbcam_link);
+  LIST_REMOVE(td, td_service_link);
+  TAILQ_REMOVE(&dvbcam_active_services, as, global_link);
+  if (do_active_programs) {
+    TAILQ_FOREACH(ac, &dvbcam_active_cams, global_link)
+      if (as->ac == ac)
+        ac->active_programs--;
   }
-done:
   pthread_mutex_unlock(&dvbcam_mutex);
 }
 
-void
-dvbcam_service_start(service_t *t)
+static void
+dvbcam_service_start(caclient_t *cac, service_t *t)
 {
+  dvbcam_t *dc = (dvbcam_t *)cac;
   dvbcam_active_service_t *as;
+  dvbcam_active_cam_t *ac = NULL;
+  th_descrambler_t *td;
+  elementary_stream_t *st;
+  caid_t *c;
+  int count = 0;
+  char buf[128];
+
+  if (!cac->cac_enabled)
+    return;
 
   tvhtrace(LS_DVBCAM, "start service %p", t);
 
-  TAILQ_FOREACH(as, &dvbcam_active_services, link)
-    if (as->t == t)
-      return;
+  pthread_mutex_lock(&t->s_stream_mutex);
+  pthread_mutex_lock(&dvbcam_mutex);
+
+  TAILQ_FOREACH(as, &dvbcam_active_services, global_link) {
+    if (as->td_service == t)
+      goto end;
+    count++;
+  }
+
+  if (dc->limit <= count)
+    goto end;
+
+  /* check all elementary streams for CAIDs and find CAM */
+  TAILQ_FOREACH(st, &t->s_filt_components, es_link)
+    LIST_FOREACH(c, &st->es_caids, link)
+      if (c->use)
+        TAILQ_FOREACH(ac, &dvbcam_active_cams, global_link)
+          if (dvbcam_ca_lookup(ac, ((mpegts_service_t *)t)->s_dvb_active_input, c->caid))
+            break;
+
+  if (ac == NULL)
+    goto end;
 
   if ((as = calloc(1, sizeof(*as))) == NULL)
-    return;
+    goto end;
 
-  as->t = t;
-  as->last_pmt = NULL;
-  as->last_pmt_len = 0;
+  as->ac = ac;
+  as->caid = c->caid;
 
-  pthread_mutex_lock(&dvbcam_mutex);
-  TAILQ_INSERT_TAIL(&dvbcam_active_services, as, link);
+  td = (th_descrambler_t *)as;
+  snprintf(buf, sizeof(buf), "dvbcam-%i-%i-%04X",
+           ac->ca->lca_number, (int)ac->slot, (int)as->caid);
+  td->td_nicename = strdup(buf);
+  td->td_service = t;
+  td->td_stop = dvbcam_service_destroy;
+  descrambler_change_keystate(td, DS_READY, 0);
+
+  LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
+
+  LIST_INSERT_HEAD(&dc->services, as, dvbcam_link);
+  TAILQ_INSERT_TAIL(&dvbcam_active_services, as, global_link);
+
+end:
   pthread_mutex_unlock(&dvbcam_mutex);
+  pthread_mutex_unlock(&t->s_stream_mutex);
 }
 
-void
-dvbcam_service_stop(service_t *t)
+/*
+ *
+ */
+static void
+dvbcam_free(caclient_t *cac)
 {
-  dvbcam_active_service_t *as, *as_tmp;
-  linuxdvb_ca_t *ca = NULL;
-  dvbcam_active_cam_t *ac2;
-  uint8_t slot = -1;
+}
 
-  tvhtrace(LS_DVBCAM, "stop service %p", t);
+/*
+ *
+ */
+static void
+dvbcam_caid_update(caclient_t *cac, mpegts_mux_t *mux, uint16_t caid, uint16_t pid, int valid)
+{
 
-  pthread_mutex_lock(&dvbcam_mutex);
+}
 
-  for (as = TAILQ_FIRST(&dvbcam_active_services); as != NULL; as = as_tmp) {
-    as_tmp = TAILQ_NEXT(as, link);
-    if(as && as->t == t) {
-      if(as->last_pmt) {
-        linuxdvb_ca_enqueue_capmt(as->ca, as->slot, as->last_pmt,
-                                  as->last_pmt_len,
-                                  CA_LIST_MANAGEMENT_UPDATE,
-                                  CA_PMT_CMD_ID_NOT_SELECTED);
-        free(as->last_pmt);
-      }
-      slot = as->slot;
-      ca = as->ca;
-      TAILQ_REMOVE(&dvbcam_active_services, as, link);
-      free(as);
-    }
+/**
+ *
+ */
+static void
+dvbcam_conf_changed(caclient_t *cac)
+{
+}
+
+/*
+ *
+ */
+const idclass_t caclient_dvbcam_class =
+{
+  .ic_super      = &caclient_class,
+  .ic_class      = "caclient_dvbcam",
+  .ic_caption    = N_("Linux DVB CAM Client"),
+  .ic_properties = (const property_t[]){
+    {
+      .type     = PT_INT,
+      .id       = "limit",
+      .name     = N_("Service limit"),
+      .desc     = N_("Limit of concurrent descrambled services (total)."),
+      .off      = offsetof(dvbcam_t, limit),
+    },
+    {}
   }
+};
 
-  if (ca)
-    TAILQ_FOREACH(ac2, &dvbcam_active_cams, link)
-      if (ac2->slot == slot && ac2->ca == ca) {
-        ac2->active_programs--;
-      }
+/*
+ *
+ */
+caclient_t *dvbcam_create(void)
+{
+  dvbcam_t *dc = calloc(1, sizeof(*dc));
 
-  pthread_mutex_unlock(&dvbcam_mutex);
+  dc->cac_free         = dvbcam_free;
+  dc->cac_start        = dvbcam_service_start;
+  dc->cac_conf_changed = dvbcam_conf_changed;
+  dc->cac_caid_update  = dvbcam_caid_update;
+  return (caclient_t *)dc;
 }
 
+/*
+ *
+ */
 void
 dvbcam_init(void)
 {
index 158635a1359caccea151c68f71cd3d184f299c73..79d04b04c6f18118c6cf3c0b971fe87cec516f8d 100644 (file)
@@ -27,8 +27,6 @@ struct elementary_stream;
 struct linuxdvb_ca;
 
 void dvbcam_init(void);
-void dvbcam_service_start(struct service *t);
-void dvbcam_service_stop(struct service *t);
 void dvbcam_register_cam(struct linuxdvb_ca *lca, uint8_t slot, uint16_t * caids, int num_caids);
 void dvbcam_unregister_cam(struct linuxdvb_ca *lca, uint8_t slot);
 void dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len);
index ff3a26a1f6fd816fd337f0004cf1fe7948388b8a..3ee293146e1fd1c9f5bfa32756391074a5328a0c 100644 (file)
@@ -24,7 +24,8 @@ tvheadend.caclient = function(panel, index) {
 
     var list = 'enabled,name,username,password,hostname,mode,camdfilename,' +
                'port,cwmode,deskey,emm,emmex,caid,providerid,tsid,sid,' +
-               'key_even,key_odd,keepalive_interval,comment,nodeid,version';
+               'key_even,key_odd,keepalive_interval,comment,nodeid,version,' +
+               'limit';
 
     tvheadend.idnode_form_grid(panel, {
         clazz: 'caclient',