]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
cccam: implement card remove operation
authorJaroslav Kysela <perex@perex.cz>
Thu, 4 Jan 2018 12:44:00 +0000 (13:44 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 4 Jan 2018 14:03:22 +0000 (15:03 +0100)
src/descrambler/cccam.c
src/descrambler/cclient.c
src/descrambler/cclient.h
src/descrambler/cwc.c

index 1f753d307bce26265bb234769604183801d477eb..71545c5852f48b1dfc1130bc3eec8cc1b588c5a0 100644 (file)
@@ -343,9 +343,10 @@ cccam_decode_card_data_reply(cccam_t *cccam, uint8_t *msg)
 
   caclient_set_status((caclient_t *)cccam, CACLIENT_STATUS_CONNECTED);
   cccam_set_ua(ua, msg + 16);
-  pcard = cc_new_card((cclient_t *)cccam, (msg[12] << 8) | msg[13], ua, nprov, pid, psa);
+  pcard = cc_new_card((cclient_t *)cccam, (msg[12] << 8) | msg[13],
+                      (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7],
+                      ua, nprov, pid, psa);
   if (pcard) {
-    pcard->cccam.cs_id = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
     pcard->cccam.cs_remote_id = (msg[8] << 24) | (msg[9] << 16) | (msg[10] << 8) | msg[11];
     pcard->cccam.cs_hop = msg[14];
     pcard->cccam.cs_reshare = msg[15];
@@ -364,7 +365,7 @@ cccam_handle_keys(cccam_t *cccam, cc_service_t *ct, cc_ecm_section_t *es,
   uint8_t *dcw_even, *dcw_odd, _dcw[16];
 
   if (!cccam->cccam_extended) {
-    cccam_decrypt_cw(cccam->cccam_nodeid, es->cccam.es_card_id, buf + 4);
+    cccam_decrypt_cw(cccam->cccam_nodeid, es->es_card_id, buf + 4);
     memcpy(_dcw, buf + 4, 16);
     cccam_decrypt(&cccam->recvblock, buf + 4, len - 4);
   } else {
@@ -412,6 +413,7 @@ cccam_running_reply(cccam_t *cccam, uint8_t *buf, int len)
 {
   cc_service_t *ct;
   cc_ecm_section_t *es;
+  uint32_t cardid;
   uint8_t seq;
 
   if (len < 4)
@@ -428,7 +430,11 @@ cccam_running_reply(cccam_t *cccam, uint8_t *buf, int len)
       cccam_decode_card_data_reply(cccam, buf);
       break;
     case MSG_CARD_REMOVED:
-      tvhtrace(cccam->cc_subsys, "%s: del card message received", cccam->cc_name);
+      if (len >= 8) {
+        cardid = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+        tvhtrace(cccam->cc_subsys, "%s: del card %08X message received", cccam->cc_name, cardid);
+        cc_remove_card_by_id((cclient_t *)cccam, cardid);
+      }
       break;
     case MSG_KEEPALIVE:
       tvhtrace(cccam->cc_subsys, "%s: keepalive", cccam->cc_name);
@@ -766,8 +772,8 @@ cccam_send_ecm(void *cc, cc_service_t *ct, cc_ecm_section_t *es,
   seq = atomic_add(&cccam->cc_seq, 1);
   caid = es->es_caid;
   provid = es->es_provid;
-  card_id = pcard->cccam.cs_id;
-  es->cccam.es_card_id = card_id;
+  card_id = pcard->cs_id;
+  es->es_card_id = card_id;
   sid = t->s_dvb_service_id;
 
   buf = alloca(len + 13);
@@ -816,7 +822,7 @@ cccam_send_emm(void *cc, cc_service_t *ct, cc_card_data_t *pcard,
 
   seq = atomic_add(&cccam->cc_seq, 1);
   caid = pcard->cs_ra.caid;
-  card_id = pcard->cccam.cs_id;  
+  card_id = pcard->cs_id;
 
   buf = alloca(len + 12);
   buf[ 0] = caid >> 8;
index b0921feb34cd0576875ef8c051cd0575329e654a..cb8bbb40c0e55c5f10f0b7d44fe310ec3a58760d 100644 (file)
@@ -25,9 +25,7 @@
 #include "cclient.h"
 #include "tvhpoll.h"
 
-static void cc_service_pid_free(cc_service_t *ct);
-
-/**
+/*
  *
  */
 static int
@@ -40,26 +38,100 @@ cc_check_empty(const uint8_t *str, int len)
   return 1;
 }
 
+/**
+ *
+ */
+static void
+cc_free_ecm_section(cc_ecm_section_t *es)
+{
+  LIST_REMOVE(es, es_link);
+  free(es);
+}
+
+/**
+ *
+ */
+static void
+cc_free_ecm_pid(cc_ecm_pid_t *ep)
+{
+  cc_ecm_section_t *es;
+  while ((es = LIST_FIRST(&ep->ep_sections)) != NULL)
+    cc_free_ecm_section(es);
+  LIST_REMOVE(ep, ep_link);
+  free(ep);
+}
+
+/**
+ *
+ */
+static void
+cc_service_ecm_pid_free(cc_service_t *ct)
+{
+  cc_ecm_pid_t *ep;
+
+  while((ep = LIST_FIRST(&ct->cs_ecm_pids)) != NULL)
+    cc_free_ecm_pid(ep);
+}
+
+/**
+ *
+ */
+static void
+cc_free_card(cc_card_data_t *cd)
+{
+  LIST_REMOVE(cd, cs_card);
+  descrambler_close_emm(cd->cs_mux, cd, cd->cs_ra.caid);
+  emm_reass_done(&cd->cs_ra);
+  free(cd);
+}
+
+/**
+ *
+ */
+static void
+cc_free_cards(cclient_t *cc)
+{
+  cc_card_data_t *cd;
+
+  while((cd = LIST_FIRST(&cc->cc_cards)) != NULL)
+    cc_free_card(cd);
+}
+
+/**
+ *
+ */
+char *
+cc_get_card_name(cc_card_data_t *pcard, char *buf, size_t buflen)
+{
+  snprintf(buf, buflen, "ID:%08x CAID:%04x with %d provider%s",
+           pcard->cs_id, pcard->cs_ra.caid, pcard->cs_ra.providers_count,
+           pcard->cs_ra.providers_count != 1 ? "s" : "");
+  return buf;
+}
+
 /**
  *
  */
 cc_card_data_t *
-cc_new_card(cclient_t *cc, uint16_t caid, uint8_t *ua,
+cc_new_card(cclient_t *cc, uint16_t caid, uint32_t cardid, uint8_t *ua,
             int pcount, uint8_t **pid, uint8_t **psa)
 {
   cc_card_data_t *pcard = NULL;
   emm_provider_t *ep;
   const char *n;
   const uint8_t *id, *sa;
-  int i, allocated = 0;
+  int i, j, c, allocated = 0;
+  char buf[256];
 
   LIST_FOREACH(pcard, &cc->cc_cards, cs_card)
-    if (pcard->cs_ra.caid == caid)
+    if (pcard->cs_ra.caid == caid &&
+        pcard->cs_id == cardid)
       break;
 
   if (pcard == NULL) {
     pcard = calloc(1, sizeof(cc_card_data_t));
     emm_reass_init(&pcard->cs_ra, caid);
+    pcard->cs_id = cardid;
     allocated = 1;
   }
 
@@ -87,30 +159,44 @@ cc_new_card(cclient_t *cc, uint16_t caid, uint8_t *ua,
 
   if (ua) {
     tvhinfo(cc->cc_subsys, "%s: Connected as user %s "
-            "to a %s-card [0x%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] "
+            "to a %s-card-%08x [CAID:%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] "
             "with %d provider%s",
-            cc->cc_name, cc->cc_username, n, caid,
+            cc->cc_name, cc->cc_username, n, cardid, caid,
             ua[0], ua[1], ua[2], ua[3], ua[4], ua[5], ua[6], ua[7],
-            pcount, pcount > 1 ? "s" : "");
+            pcount, pcount != 1 ? "s" : "");
   } else {
     tvhinfo(cc->cc_subsys, "%s: Connected as user %s "
-            "to a %s-card [0x%04x] with %d provider%s",
-            cc->cc_name, cc->cc_username, n, caid,
-            pcount, pcount > 1 ? "s" : "");
+            "to a %s-card-%08x [CAID:%04x] with %d provider%s",
+            cc->cc_name, cc->cc_username, n, cardid, caid,
+            pcount, pcount != 1 ? "s" : "");
   }
 
-  for (i = 0, ep = pcard->cs_ra.providers; i < pcount; i++, ep++) {
+  buf[0] = '\0';
+  for (i = j = c = 0, ep = pcard->cs_ra.providers; i < pcount; i++, ep++) {
     if (psa && !cc_check_empty(ep->sa, 8)) {
       sa = ep->sa;
-      tvhinfo(cc->cc_subsys, "%s: Provider ID #%d: 0x%04x:0x%06x %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
-              cc->cc_name, i + 1, caid, ep->id,
-              sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7]);
+      if (buf[0]) {
+        tvhdebug(cc->cc_subsys, "%s: Providers: ID:%08x CAID:%04X:[%s]",
+                 cc->cc_name, cardid, caid, buf);
+        buf[0] = '\0';
+        c = 0;
+      }
+      tvhdebug(cc->cc_subsys, "%s: Provider ID #%d: 0x%04x:0x%06x %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
+               cc->cc_name, i + 1, caid, ep->id,
+               sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7]);
     } else {
-      tvhinfo(cc->cc_subsys, "%s: Provider ID #%d: 0x%04x:0x%06x",
-              cc->cc_name, i + 1, caid, ep->id);
+      tvh_strlcatf(buf, sizeof(buf), c, "%s0x%06x", c > 0 ? "," : "", ep->id);
+      if (++j > 5) {
+        tvhdebug(cc->cc_subsys, "%s: Providers: ID:%08x CAID:%04X:[%s]",
+                 cc->cc_name, cardid, caid, buf);
+        buf[0] = '\0';
+        c = j = 0;
+      }
     }
   }
-
+  if (j > 0)
+    tvhdebug(cc->cc_subsys, "%s: Providers: ID:%08x CAID:%04X:[%s]",
+             cc->cc_name, cardid, caid, buf);
   if (cc->cc_emm && ua) {
     ua = pcard->cs_ra.ua;
     i = ua[0] || ua[1] || ua[2] || ua[3] ||
@@ -125,6 +211,61 @@ cc_new_card(cclient_t *cc, uint16_t caid, uint8_t *ua,
   return pcard;
 }
 
+/**
+ *
+ */
+void
+cc_remove_card(cclient_t *cc, cc_card_data_t *pcard)
+{
+  cc_service_t *ct;
+  cc_ecm_pid_t *ep, *epn;
+  cc_ecm_section_t *es, *esn;
+  emm_provider_t *emmp;
+  char buf[256];
+  int i;
+
+  tvhinfo(cc->cc_subsys, "%s: card %s removed", cc->cc_name,
+          cc_get_card_name(pcard, buf, sizeof(buf)));
+
+  /* invalidate all requests */
+  LIST_FOREACH(ct, &cc->cc_services, cs_link) {
+    for (ep = LIST_FIRST(&ct->cs_ecm_pids); ep; ep = epn) {
+      epn = LIST_NEXT(ep, ep_link);
+      for (es = LIST_FIRST(&ep->ep_sections); es; es = esn) {
+        esn = LIST_NEXT(es, es_link);
+        if (es->es_caid == pcard->cs_ra.caid) {
+          emmp = pcard->cs_ra.providers;
+          for (i = 0; i < pcard->cs_ra.providers_count; i++, emmp++)
+            if (emmp->id == es->es_provid) {
+              cc_free_ecm_section(es);
+              break;
+            }
+          }
+        }
+        es = esn;
+      }
+      if (LIST_EMPTY(&ep->ep_sections))
+        cc_free_ecm_pid(ep);
+    }
+
+  cc_free_card(pcard);
+}
+
+/**
+ *
+ */
+void
+cc_remove_card_by_id(cclient_t *cc, uint32_t id)
+{
+  cc_card_data_t *pcard;
+
+  LIST_FOREACH(pcard, &cc->cc_cards, cs_card)
+    if (pcard->cs_id == id) {
+      cc_remove_card(cc, pcard);
+      break;
+    }
+}
+
 /**
  *
  */
@@ -138,7 +279,7 @@ cc_ecm_reset(th_descrambler_t *th)
 
   pthread_mutex_lock(&cc->cc_mutex);
   descrambler_change_keystate(th, DS_READY, 1);
-  LIST_FOREACH(ep, &ct->cs_pids, ep_link)
+  LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link)
     LIST_FOREACH(es, &ep->ep_sections, es_link)
       es->es_keystate = ES_UNKNOWN;
   ct->ecm_state = ECM_RESET;
@@ -158,7 +299,7 @@ cc_ecm_idle(th_descrambler_t *th)
   cc_ecm_section_t *es;
 
   pthread_mutex_lock(&cc->cc_mutex);
-  LIST_FOREACH(ep, &ct->cs_pids, ep_link)
+  LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link)
     LIST_FOREACH(es, &ep->ep_sections, es_link)
       es->es_keystate = ES_IDLE;
   ct->ecm_state = ECM_RESET;
@@ -219,7 +360,7 @@ cc_ecm_reply(cc_service_t *ct, cc_ecm_section_t *es,
 
 forbid:
     i = 0;
-    LIST_FOREACH(ep, &ct->cs_pids, ep_link)
+    LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link)
       LIST_FOREACH(es2, &ep->ep_sections, es_link)
         if(es2 && es2 != es && es2->es_nok == 0) {
           if (es2->es_pending)
@@ -230,7 +371,7 @@ forbid:
       return;
     
     es->es_keystate = ES_FORBIDDEN;
-    LIST_FOREACH(ep, &ct->cs_pids, ep_link) {
+    LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link) {
       LIST_FOREACH(es2, &ep->ep_sections, es_link)
         if (es2->es_keystate == ES_UNKNOWN ||
             es2->es_keystate == ES_RESOLVED)
@@ -305,7 +446,7 @@ cc_find_pending_section(cclient_t *cc, uint32_t seq, cc_service_t **_ct)
 
   if (_ct) *_ct = NULL;
   LIST_FOREACH(ct, &cc->cc_services, cs_link)
-    LIST_FOREACH(ep, &ct->cs_pids, ep_link)
+    LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link)
       LIST_FOREACH(es, &ep->ep_sections, es_link)
         if(es->es_seq == seq) {
           if (es->es_resolved) {
@@ -340,22 +481,6 @@ cc_invalidate_cards(cclient_t *cc)
     cd->cs_running = 0;
 }
 
-/**
- *
- */
-static void
-cc_free_cards(cclient_t *cc)
-{
-  cc_card_data_t *cd;
-
-  while((cd = LIST_FIRST(&cc->cc_cards)) != NULL) {
-    LIST_REMOVE(cd, cs_card);
-    descrambler_close_emm(cd->cs_mux, cd, cd->cs_ra.caid);
-    emm_reass_done(&cd->cs_ra);
-    free(cd);
-  }
-}
-
 /**
  *
  */
@@ -549,7 +674,7 @@ cc_thread(void *aux)
         break;
       }
 
-      tvhinfo(cc->cc_subsys, "%s: Connected", cc->cc_name);
+      tvhdebug(cc->cc_subsys, "%s: Connected", cc->cc_name);
       attempts = 0;
 
       cc->cc_fd = fd;
@@ -718,7 +843,7 @@ cc_table_input(void *opaque, int pid, const uint8_t *data, int len, int emm)
 
   if (ct->ecm_state == ECM_RESET) {
     /* clean all */
-    cc_service_pid_free(ct);
+    cc_service_ecm_pid_free(ct);
     /* move to init state */
     ct->ecm_state = ECM_INIT;
     ct->cs_capid = 0xffff;
@@ -727,7 +852,7 @@ cc_table_input(void *opaque, int pid, const uint8_t *data, int len, int emm)
              cc->cc_name, t->s_dvb_svcname);
   }
 
-  LIST_FOREACH(ep, &ct->cs_pids, ep_link)
+  LIST_FOREACH(ep, &ct->cs_ecm_pids, ep_link)
     if(ep->ep_capid == pid) break;
 
   if(ep == NULL) {
@@ -754,7 +879,7 @@ prefcapid_ok:
          t->s_dvb_prefcapid_lock == PREFCAPID_OFF) {
         ep = calloc(1, sizeof(cc_ecm_pid_t));
         ep->ep_capid = pid;
-        LIST_INSERT_HEAD(&ct->cs_pids, ep, ep_link);
+        LIST_INSERT_HEAD(&ct->cs_ecm_pids, ep, ep_link);
         tvhdebug(cc->cc_subsys, "%s: Insert %s ECM (PID %d) for service \"%s\"",
                  cc->cc_name, t->s_dvb_prefcapid ? "preferred" : "new", pid, t->s_dvb_svcname);
       }
@@ -838,7 +963,7 @@ found:
                cc->cc_name, pcard->cs_ra.caid, provid,
                t->s_dvb_svcname);
       tvhlog_hexdump(cc->cc_subsys, data, len);
-      cc->cc_send_emm(cc, ct, pcard, provid, data, len);
+      emm_filter(&pcard->cs_ra, data, len, t->s_dvb_mux, cc_emm_send, pcard);
     }
   }
 
@@ -847,25 +972,6 @@ end:
   pthread_mutex_unlock(&cc->cc_mutex);
 }
 
-/**
- * cc_mutex is held
- */
-static void
-cc_service_pid_free(cc_service_t *ct)
-{
-  cc_ecm_pid_t *ep;
-  cc_ecm_section_t *es;
-
-  while((ep = LIST_FIRST(&ct->cs_pids)) != NULL) {
-    while ((es = LIST_FIRST(&ep->ep_sections)) != NULL) {
-      LIST_REMOVE(es, es_link);
-      free(es);
-    }
-    LIST_REMOVE(ep, ep_link);
-    free(ep);
-  }
-}
-
 /**
  * cc_mutex is held
  */
@@ -879,7 +985,7 @@ cc_service_destroy0(th_descrambler_t *td)
     descrambler_close_pid(ct->cs_mux, ct, ct->cs_epids.pids[i].pid);
   mpegts_pid_done(&ct->cs_epids);
 
-  cc_service_pid_free(ct);
+  cc_service_ecm_pid_free(ct);
 
   LIST_REMOVE(td, td_service_link);
 
index 389d0a31b3947f7054adf99645e96ed9348d2fe9..5674339b9face153ce6a55fef5cd14103b817d5c 100644 (file)
@@ -44,6 +44,7 @@ typedef struct cc_ecm_section {
 
   int      es_section;
 
+  uint32_t es_card_id;
   uint16_t es_capid;
   uint16_t es_caid;
   uint32_t es_provid;
@@ -54,12 +55,6 @@ typedef struct cc_ecm_section {
   uint8_t  es_resolved;
   int64_t  es_time;  // time request was sent
 
-  union {
-    struct {
-      uint32_t es_card_id;
-    } cccam;
-  };
-
 } cc_ecm_section_t;
 
 /**
@@ -98,7 +93,7 @@ typedef struct cc_service {
     ECM_RESET
   } ecm_state;
 
-  LIST_HEAD(, cc_ecm_pid) cs_pids;
+  LIST_HEAD(, cc_ecm_pid) cs_ecm_pids;
 
 } cc_service_t;
 
@@ -116,13 +111,13 @@ typedef struct cc_message {
  */
 typedef struct cc_card_data {
   LIST_ENTRY(cc_card_data) cs_card;
+  uint32_t      cs_id;
   emm_reass_t   cs_ra;
   void         *cs_client;
   mpegts_mux_t *cs_mux;
   uint8_t       cs_running;
   union {
     struct {
-      uint32_t  cs_id;
       uint32_t  cs_remote_id;
       uint8_t   cs_hop;
       uint8_t   cs_reshare;
@@ -195,9 +190,13 @@ typedef struct cclient {
 static inline int cc_must_break(cclient_t *cc)
  { return !cc->cc_running || !cc->cac_enabled || cc->cc_reconfigure; }
 
+char *cc_get_card_name(cc_card_data_t *pcard, char *buf, size_t buflen);
 cc_card_data_t *cc_new_card
-  (cclient_t *cc, uint16_t caid, uint8_t *ua, int pcount, uint8_t **pid, uint8_t **psa);
+  (cclient_t *cc, uint16_t caid, uint32_t cardid,
+   uint8_t *ua, int pcount, uint8_t **pid, uint8_t **psa);
 void cc_emm_set_allowed(cclient_t *cc, int emm_allowed);
+void cc_remove_card(cclient_t *cc, cc_card_data_t *pcard);
+void cc_remove_card_by_id(cclient_t *cc, uint32_t card_id);
 
 void cc_ecm_reply
   (cc_service_t *ct, cc_ecm_section_t *es, int key_type,
index 917f40a8089f6d70e111e8872a7cd3a56c772075..b28854a146d1add3c7a811e6eff2f325aa71065e 100644 (file)
@@ -343,7 +343,7 @@ cwc_decode_card_data_reply(cwc_t *cwc, uint8_t *msg, int len)
 
   caclient_set_status((caclient_t *)cwc, CACLIENT_STATUS_CONNECTED);
   cc_new_card((cclient_t *)cwc, (msg[4] << 8) | msg[5],
-               msg + 6, nprov, pid, psa);
+               0, msg + 6, nprov, pid, psa);
 
   return 0;
 }
@@ -460,7 +460,7 @@ cwc_running_reply(cwc_t *cwc, uint8_t msgtype, uint8_t *msg, int len)
 
         caclient_set_status((caclient_t *)cwc, CACLIENT_STATUS_CONNECTED);
         u8 = &msg[8];
-        cc_new_card((cclient_t *)cwc, caid, NULL, 1, &u8, NULL);
+        cc_new_card((cclient_t *)cwc, caid, 0, NULL, 1, &u8, NULL);
       }
   }
   return 0;