From: Damjan Marion Date: Mon, 18 May 2015 11:16:20 +0000 (+0200) Subject: linuxdvb_ca: improve CAPMT sending to CAM, support multiple services X-Git-Tag: v4.2.1~2524 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=61eeb3869ca1478a2b085ca454e6a78c108c5a14;p=thirdparty%2Ftvheadend.git linuxdvb_ca: improve CAPMT sending to CAM, support multiple services --- diff --git a/src/descrambler/dvbcam.c b/src/descrambler/dvbcam.c index 03eaa4978..360e87c58 100644 --- a/src/descrambler/dvbcam.c +++ b/src/descrambler/dvbcam.c @@ -38,16 +38,17 @@ typedef struct dvbcam_active_service { uint8_t slot; } dvbcam_active_service_t; -typedef struct dvbcam_active_caid { - TAILQ_ENTRY(dvbcam_active_caid) link; +typedef struct dvbcam_active_cam { + TAILQ_ENTRY(dvbcam_active_cam) link; uint16_t caids[CAIDS_PER_CA_SLOT]; int num_caids; linuxdvb_ca_t *ca; uint8_t slot; -} dvbcam_active_caid_t; + int active_programs; +} dvbcam_active_cam_t; TAILQ_HEAD(,dvbcam_active_service) dvbcam_active_services; -TAILQ_HEAD(,dvbcam_active_caid) dvbcam_active_caids; +TAILQ_HEAD(,dvbcam_active_cam) dvbcam_active_cams; pthread_mutex_t dvbcam_mutex; @@ -55,11 +56,14 @@ void dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids, int num_caids) { - dvbcam_active_caid_t *ac; + dvbcam_active_cam_t *ac; + + tvhtrace("dvbcam", "register cam ca %p slot %u num_caids %u", + lca, slot, num_caids); num_caids = MIN(CAIDS_PER_CA_SLOT, num_caids); - if ((ac = malloc(sizeof(*ac))) == NULL) + if ((ac = calloc(1, sizeof(*ac))) == NULL) return; ac->ca = lca; @@ -69,7 +73,7 @@ dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids, pthread_mutex_lock(&dvbcam_mutex); - TAILQ_INSERT_TAIL(&dvbcam_active_caids, ac, link); + TAILQ_INSERT_TAIL(&dvbcam_active_cams, ac, link); pthread_mutex_unlock(&dvbcam_mutex); } @@ -77,9 +81,11 @@ dvbcam_register_cam(linuxdvb_ca_t * lca, uint8_t slot, uint16_t * caids, void dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot) { - dvbcam_active_caid_t *ac, *ac_tmp; + dvbcam_active_cam_t *ac, *ac_tmp; dvbcam_active_service_t *as; + tvhtrace("dvbcam", "unregister cam lca %p slot %u", lca, slot); + pthread_mutex_lock(&dvbcam_mutex); /* remove pointer to this CAM in all active services */ @@ -88,10 +94,10 @@ dvbcam_unregister_cam(linuxdvb_ca_t * lca, uint8_t slot) as->ca = NULL; /* delete entry */ - for (ac = TAILQ_FIRST(&dvbcam_active_caids); ac != NULL; ac = ac_tmp) { + 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_caids, ac, link); + TAILQ_REMOVE(&dvbcam_active_cams, ac, link); free(ac); } } @@ -103,64 +109,87 @@ void dvbcam_pmt_data(mpegts_service_t *s, const uint8_t *ptr, int len) { linuxdvb_frontend_t *lfe; - dvbcam_active_caid_t *ac; + dvbcam_active_cam_t *ac = NULL, *ac2; dvbcam_active_service_t *as = NULL, *as2; elementary_stream_t *st; caid_t *c; + uint8_t list_mgmt; + int is_update = 0; int i; - lfe = (linuxdvb_frontend_t*) s->s_dvb_active_input; + lfe = (linuxdvb_frontend_t*) s->s_dvb_active_input; if (!lfe) return; 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; break; } - pthread_mutex_unlock(&dvbcam_mutex); - - if (!as) - return; + if (!as) { + tvhtrace("dvbcam", "cannot find active service entry"); + goto done; + } - if(as->last_pmt) + if(as->last_pmt) { free(as->last_pmt); + is_update = 1; + } as->last_pmt = malloc(len + 3); memcpy(as->last_pmt, ptr-3, len + 3); as->last_pmt_len = len + 3; - as->ca = NULL; - pthread_mutex_lock(&dvbcam_mutex); + /*if this is update just send updated CAPMT to CAM */ + if (is_update) { - /* check all ellementary streams for CAIDs, if any send PMT to CAM */ + tvhtrace("dvbcam", "CAPMT sent to CAM (update)"); + list_mgmt = CA_LIST_MANAGEMENT_UPDATE; + goto enqueue; + } + + /* 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(ac, &dvbcam_active_caids, link) { - for(i=0;inum_caids;i++) { - if(ac->ca && ac->ca->lca_adapter == lfe->lfe_adapter && - ac->caids[i] == c->caid) + TAILQ_FOREACH(ac2, &dvbcam_active_cams, link) { + for(i=0;inum_caids;i++) { + if(ac2->ca && ac2->ca->lca_adapter == lfe->lfe_adapter && + ac2->caids[i] == c->caid) { - as->ca = ac->ca; - as->slot = ac->slot; - break; + as->ca = ac2->ca; + as->slot = ac2->slot; + ac = ac2; + goto end_of_search_for_cam; } } } } } - pthread_mutex_unlock(&dvbcam_mutex); - - /* this service doesn't have assigned CAM */ - if (!as->ca) - return; +end_of_search_for_cam: - linuxdvb_ca_send_capmt(as->ca, as->slot, as->last_pmt, as->last_pmt_len); + if (!ac) { + tvhtrace("dvbcam", "cannot find active cam entry"); + goto done; + } + tvhtrace("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) + linuxdvb_ca_enqueue_capmt(as->ca, as->slot, as->last_pmt, as->last_pmt_len, + list_mgmt, CA_PMT_CMD_ID_OK_DESCRAMBLING); +done: + pthread_mutex_unlock(&dvbcam_mutex); } void @@ -168,11 +197,13 @@ dvbcam_service_start(service_t *t) { dvbcam_active_service_t *as; + tvhtrace("dvbcam", "start service %p", t); + TAILQ_FOREACH(as, &dvbcam_active_services, link) if (as->t == t) return; - if ((as = malloc(sizeof(*as))) == NULL) + if ((as = calloc(1, sizeof(*as))) == NULL) return; as->t = t; @@ -188,19 +219,37 @@ void dvbcam_service_stop(service_t *t) { dvbcam_active_service_t *as, *as_tmp; + linuxdvb_ca_t *ca = NULL; + dvbcam_active_cam_t *ac2; + uint8_t slot; + + tvhtrace("dvbcam", "stop service %p", t); 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) { - TAILQ_REMOVE(&dvbcam_active_services, as, link); - if(as->last_pmt) + 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); } } + if (ca) + TAILQ_FOREACH(ac2, &dvbcam_active_cams, link) + if (ac2->slot == slot && ac2->ca == ca) { + ac2->active_programs--; + } + pthread_mutex_unlock(&dvbcam_mutex); } @@ -209,7 +258,7 @@ dvbcam_init(void) { pthread_mutex_init(&dvbcam_mutex, NULL); TAILQ_INIT(&dvbcam_active_services); - TAILQ_INIT(&dvbcam_active_caids); + TAILQ_INIT(&dvbcam_active_cams); } #endif /* ENABLE_LINUXDVB_CA */ diff --git a/src/input/mpegts/linuxdvb/linuxdvb_ca.c b/src/input/mpegts/linuxdvb/linuxdvb_ca.c index 4b65d4ebe..bb6a54788 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_ca.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_ca.c @@ -53,6 +53,41 @@ ca_slot_state2str(ca_slot_state_t v) return "UNKNOWN"; } +const static char * +ca_pmt_list_mgmt2str(uint8_t v) +{ + switch(v) { + case CA_LIST_MANAGEMENT_MORE: return "more"; + case CA_LIST_MANAGEMENT_FIRST: return "first"; + case CA_LIST_MANAGEMENT_LAST: return "last"; + case CA_LIST_MANAGEMENT_ONLY: return "only"; + case CA_LIST_MANAGEMENT_ADD: return "add"; + case CA_LIST_MANAGEMENT_UPDATE: return "update"; + } + return "UNKNOWN"; +} + +const static char * +ca_pmt_cmd_id2str(uint8_t v) +{ + switch(v) { + case CA_PMT_CMD_ID_OK_DESCRAMBLING: return "ok_descrambling"; + case CA_PMT_CMD_ID_OK_MMI: return "ok_mmi"; + case CA_PMT_CMD_ID_QUERY: return "query"; + case CA_PMT_CMD_ID_NOT_SELECTED: return "not_selected"; + } + return "UNKNOWN"; +} + +struct linuxdvb_ca_capmt { + TAILQ_ENTRY(linuxdvb_ca_capmt) lcc_link; + int len; + uint8_t *data; + uint8_t slot; + uint8_t list_mgmt; + uint8_t cmd_id; +}; + /* * ts101699 and CI+ 1.3 definitions */ @@ -733,48 +768,46 @@ linuxdvb_ca_create lca->lca_adapter = la; LIST_INSERT_HEAD(&la->la_ca_devices, lca, lca_link); + TAILQ_INIT(&lca->lca_capmt_queue); + gtimer_arm_ms(&lca->lca_monitor_timer, linuxdvb_ca_monitor, lca, 250); return lca; } -void -linuxdvb_ca_send_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr, int len) +static void +linuxdvb_ca_process_capmt_queue ( void *aux ) { + linuxdvb_ca_t *lca = aux; + linuxdvb_ca_capmt_t *lcc; struct section *section; struct section_ext *result; struct mpeg_pmt_section *pmt; - uint8_t *buffer; uint8_t capmt[4096]; int size; - buffer = malloc(len); - if (!buffer) - return; + lcc = TAILQ_FIRST(&lca->lca_capmt_queue); - memcpy(buffer, ptr, len); + if (!lcc) + return; - section = section_codec(buffer, len); - if (!section){ + if (!(section = section_codec(lcc->data, lcc->len))){ tvherror("en50221", "failed to decode PMT section"); - goto fail; + goto done; } - result = section_ext_decode(section, 0); - if (!result){ + if (!(result = section_ext_decode(section, 0))){ tvherror("en50221", "failed to decode PMT ext_section"); - goto fail; + goto done; } - pmt = mpeg_pmt_section_codec(result); - if (!pmt){ + if (!(pmt = mpeg_pmt_section_codec(result))){ tvherror("en50221", "failed to decode PMT"); - goto fail; + goto done; } - size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), 1, - CA_LIST_MANAGEMENT_ONLY, - CA_PMT_CMD_ID_OK_DESCRAMBLING); + size = en50221_ca_format_pmt(pmt, capmt, sizeof(capmt), 0, + lcc->list_mgmt, lcc->cmd_id); if (size < 0) { tvherror("en50221", "Failed to format CAPMT"); @@ -785,11 +818,51 @@ linuxdvb_ca_send_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr, int tvherror("en50221", "Failed to send CAPMT"); } - tvhtrace("en50221", "OK Descrambling CAPMT sent"); + tvhtrace("en50221", "%s CAPMT sent (%s)", ca_pmt_cmd_id2str(lcc->cmd_id), + ca_pmt_list_mgmt2str(lcc->list_mgmt)); tvhlog_hexdump("en50221", capmt, size); -fail: - free(buffer); +done: + + TAILQ_REMOVE(&lca->lca_capmt_queue, lcc, lcc_link); + + free(lcc->data); + free(lcc); + + if (!TAILQ_EMPTY(&lca->lca_capmt_queue)) { + gtimer_arm_ms(&lca->lca_capmt_queue_timer, + linuxdvb_ca_process_capmt_queue, lca, 1000); + } +} + +void +linuxdvb_ca_enqueue_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr, + int len, uint8_t list_mgmt, uint8_t cmd_id) +{ + linuxdvb_ca_capmt_t *lcc; + + if (!lca) + return; + + lcc = calloc(1, sizeof(*lcc)); + + if (!lcc) + return; + + lcc->data = malloc(len); + lcc->len = len; + lcc->slot = slot; + lcc->list_mgmt = list_mgmt; + lcc->cmd_id = cmd_id; + memcpy(lcc->data, ptr, len); + + TAILQ_INSERT_TAIL(&lca->lca_capmt_queue, lcc, lcc_link); + + tvhtrace("en50221", "%s CAPMT enqueued (%s)", ca_pmt_cmd_id2str(lcc->cmd_id), + ca_pmt_list_mgmt2str(lcc->list_mgmt)); + + gtimer_arm_ms(&lca->lca_capmt_queue_timer, + linuxdvb_ca_process_capmt_queue, lca, 50); } void linuxdvb_ca_save( linuxdvb_ca_t *lca, htsmsg_t *msg ) diff --git a/src/input/mpegts/linuxdvb/linuxdvb_private.h b/src/input/mpegts/linuxdvb/linuxdvb_private.h index bed7fe4ad..b88ae94ce 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_private.h +++ b/src/input/mpegts/linuxdvb/linuxdvb_private.h @@ -53,6 +53,7 @@ typedef struct linuxdvb_adapter linuxdvb_adapter_t; typedef struct linuxdvb_frontend linuxdvb_frontend_t; #if ENABLE_LINUXDVB_CA typedef struct linuxdvb_ca linuxdvb_ca_t; +typedef struct linuxdvb_ca_capmt linuxdvb_ca_capmt_t; #endif typedef struct linuxdvb_satconf linuxdvb_satconf_t; typedef struct linuxdvb_satconf_ele linuxdvb_satconf_ele_t; @@ -63,6 +64,9 @@ typedef struct linuxdvb_en50494 linuxdvb_en50494_t; typedef LIST_HEAD(,linuxdvb_hardware) linuxdvb_hardware_list_t; typedef TAILQ_HEAD(linuxdvb_satconf_ele_list,linuxdvb_satconf_ele) linuxdvb_satconf_ele_list_t; +#if ENABLE_LINUXDVB_CA +typedef TAILQ_HEAD(linuxdvb_ca_capmt_queue,linuxdvb_ca_capmt) linuxdvb_ca_capmt_queue_t; +#endif struct linuxdvb_adapter { @@ -172,6 +176,7 @@ struct linuxdvb_ca int lca_enabled; int lca_high_bitrate_mode; gtimer_t lca_monitor_timer; + gtimer_t lca_capmt_queue_timer; pthread_t lca_en50221_thread; int lca_en50221_thread_running; @@ -199,6 +204,7 @@ struct linuxdvb_ca char *lca_ca_path; int lca_state; const char *lca_state_str; + linuxdvb_ca_capmt_queue_t lca_capmt_queue; /* * CAM module info */ @@ -374,7 +380,8 @@ linuxdvb_ca_create void linuxdvb_ca_save( linuxdvb_ca_t *lca, htsmsg_t *m ); void -linuxdvb_ca_send_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr, int len); +linuxdvb_ca_enqueue_capmt(linuxdvb_ca_t *lca, uint8_t slot, const uint8_t *ptr, + int len, uint8_t list_mgmt, uint8_t cmd_id); #endif