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;
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;
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);
}
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 */
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);
}
}
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;i<ac->num_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;i<ac2->num_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
{
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;
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);
}
{
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 */
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
*/
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");
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 )