From d6a86e338e617033887eabd78e07b00b990d94de Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 16 Oct 2017 17:21:09 +0200 Subject: [PATCH] dvbcam: use stardard caclient interface --- src/descrambler/caclient.c | 37 ++++ src/descrambler/caclient.h | 4 + src/descrambler/cwc.c | 2 +- src/descrambler/descrambler.c | 11 - src/descrambler/dvbcam.c | 369 +++++++++++++++++++++---------- src/descrambler/dvbcam.h | 2 - src/webui/static/app/caclient.js | 3 +- 7 files changed, 294 insertions(+), 134 deletions(-) diff --git a/src/descrambler/caclient.c b/src/descrambler/caclient.c index edc3210c1..fe1c8f043 100644 --- a/src/descrambler/caclient.c +++ b/src/descrambler/caclient.c @@ -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 diff --git a/src/descrambler/caclient.h b/src/descrambler/caclient.h index cb1c275c6..927034bea 100644 --- a/src/descrambler/caclient.h +++ b/src/descrambler/caclient.h @@ -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); diff --git a/src/descrambler/cwc.c b/src/descrambler/cwc.c index 826be5961..f6510d1da 100644 --- a/src/descrambler/cwc.c +++ b/src/descrambler/cwc.c @@ -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; diff --git a/src/descrambler/descrambler.c b/src/descrambler/descrambler.c index c8309705c..000a0a914 100644 --- a/src/descrambler/descrambler.c +++ b/src/descrambler/descrambler.c @@ -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; diff --git a/src/descrambler/dvbcam.c b/src/descrambler/dvbcam.c index c071e9529..4495cfcaa 100644 --- a/src/descrambler/dvbcam.c +++ b/src/descrambler/dvbcam.c @@ -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 @@ -30,17 +31,8 @@ #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;inum_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) { diff --git a/src/descrambler/dvbcam.h b/src/descrambler/dvbcam.h index 158635a13..79d04b04c 100644 --- a/src/descrambler/dvbcam.h +++ b/src/descrambler/dvbcam.h @@ -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); diff --git a/src/webui/static/app/caclient.js b/src/webui/static/app/caclient.js index ff3a26a1f..3ee293146 100644 --- a/src/webui/static/app/caclient.js +++ b/src/webui/static/app/caclient.js @@ -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', -- 2.47.3