CI/CA Patch - please test

Florian Lohoff flo at rfc822.org
Fri Dec 19 10:17:08 CET 2008


Hi,
probably someone is able to test this - i refactored a CI patch i got to
use more internal infrastructure and be more like my preferred coding
style. Its against current gits head and needs some libs from the libdvb
family - In Debian its installing dvb-utils:


diff --git a/Makefile b/Makefile
index 345a635..1a6a6f6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CC=gcc
-CFLAGS=-O0 -g -Wall  -I. -I/usr/include/glib-2.0/ -I/usr/lib/glib-2.0/include/
-LDFLAGS=-levent -lglib-2.0 -lpthread
-OBJ-getstream=getstream.o fe.o crc32.o \
+CFLAGS=-O0 -g -Wall  -I. -I/usr/include/glib-2.0/ -I/usr/lib/glib-2.0/include/ -D_GNU_SOURCE
+LDFLAGS=-levent -lglib-2.0 -lpthread -ldvben50221 -ldvbapi -lucsi
+OBJ-getstream=getstream.o ca.o fe.o crc32.o \
 	libhttp.o libconf.o config.o util.o logging.o \
 	stream.o input.o \
 	output.o output_http.o output_udp.o output_pipe.o output_rtp.o \
diff --git a/ca.c b/ca.c
new file mode 100644
index 0000000..ebbff8b
--- /dev/null
+++ b/ca.c
@@ -0,0 +1,370 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <poll.h>
+#include <errno.h>
+
+#include <libdvben50221/en50221_stdcam.h>
+
+#include <libucsi/section.h>
+#include <libucsi/mpeg/section.h>
+#include <libucsi/dvb/section.h>
+
+#include <libdvbapi/dvbdemux.h>
+#include <libdvbapi/dvbca.h>
+
+#include <pthread.h>
+#include <sys/types.h>
+
+#include <sys/syscall.h>
+#include <sched.h>
+
+#include <glib.h>
+
+#include "getstream.h"
+#include "ca.h"
+
+static void    *ca_monitor(void *arg);
+
+/* TODO: old fashioned callbacks, need some work */
+void send_mmi_menu_answ(void *value) {
+	/*
+	   int val = (int)value;
+	   intca_t *this = (intca_t*)ui.mmimenu->ca;
+	   en50221_app_mmi_menu_answ(this->stdcam->mmi_resource,
+	   ui.mmimenu->session_number,
+	   val);
+	 */
+	/* FIXME: IMPLEMENT ME! */
+
+}
+
+void send_mmi_answ(uint8_t * value, int length) {
+	/*
+	   intca_t *this = (intca_t*)ui.pin_query->ca;
+	   en50221_app_mmi_answ(this->stdcam->mmi_resource,
+	   this->stdcam->mmi_session_number,
+	   MMI_ANSW_ID_ANSWER, value,
+	   length);
+	 */
+
+	/* FIXME: IMPELEMENT ME! */
+}
+
+void open_mmi_menu(void *ca) {
+	struct intca_s	*intca=ca;
+	int		ret;
+
+	ret=en50221_app_ai_entermenu(intca->stdcam->ai_resource,
+					 intca->stdcam->ai_session_number);
+
+	logwrite(LOG_INFO, "Enter CI-Menu! %d, %d, %d", ret,
+		 intca->stdcam->ai_session_number,
+		 intca->stdcam->mmi_session_number);
+}
+
+/* ends here */
+
+static int ai_callback(void *arg, uint8_t slot_id, uint16_t session_number,
+		       uint8_t application_type,
+		       uint16_t application_manufacturer,
+		       uint16_t manufacturer_code, uint8_t menu_string_length,
+		       uint8_t * menu_string) {
+
+	logwrite(LOG_INFO, "CAM %d Application type: %02x", slot_id,
+		 application_type);
+	logwrite(LOG_INFO, "CAM %d Application manufacturer: %04x", slot_id,
+		 application_manufacturer);
+	logwrite(LOG_INFO, "CAM %d Manufacturer code: %04x", slot_id,
+		 manufacturer_code);
+	logwrite(LOG_INFO, "CAM %d Menu string: %.*s", slot_id,
+		 menu_string_length, menu_string);
+
+	return 0;
+}
+
+static int ca_info_callback(void *arg, uint8_t slot_id,
+			    uint16_t session_number, uint32_t ca_id_count,
+			    uint16_t *ca_ids) {
+
+	struct intca_s	*intca=arg;
+	int		i;
+
+	logwrite(LOG_INFO, "CAM %d supports the following ca system ids:",
+		 slot_id);
+
+	for(i=0;i<ca_id_count;i++)
+		logwrite(LOG_INFO, "  0x%04x\n", ca_ids[i]);
+
+	intca->ca_resource_connected = 1;
+	return 0;
+}
+
+static int mmi_close_callback(void *arg, uint8_t slot_id,
+			      uint16_t session_number, uint8_t cmd_id,
+			      uint8_t delay) {
+
+	/*
+	 * FIXME close some menu
+	 * note: not entirely correct as its supposed to delay if asked
+	 */
+	return 0;
+}
+
+static int mmi_display_control_callback(void *arg, uint8_t slot_id,
+					uint16_t session_number,
+					uint8_t cmd_id, uint8_t mmi_mode) {
+
+	struct en50221_app_mmi_display_reply_details reply;
+	struct intca_s	*intca=arg;
+
+	/* don't support any commands but set mode */
+
+	if(cmd_id != MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
+
+		en50221_app_mmi_display_reply(intca->stdcam->mmi_resource,
+					      session_number,
+					      MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID,
+					      &reply);
+
+		return 0;
+	}
+
+	/* we only support high level mode */
+	if(mmi_mode != MMI_MODE_HIGH_LEVEL) {
+
+		en50221_app_mmi_display_reply(intca->stdcam->mmi_resource,
+					      session_number,
+					      MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE,
+					      &reply);
+
+		return 0;
+	}
+
+	/* ack the high level open */
+	reply.u.mode_ack.mmi_mode=mmi_mode;
+	en50221_app_mmi_display_reply(intca->stdcam->mmi_resource,
+				      session_number,
+				      MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK,
+				      &reply);
+
+	return 0;
+}
+
+static int mmi_enq_callback(void *arg, uint8_t slot_id,
+			    uint16_t session_number, uint8_t blind_answer,
+			    uint8_t expected_answer_length, uint8_t * text,
+			    uint32_t text_size) {
+	struct intca_s	*intca=arg;
+
+	en50221_app_mmi_answ(intca->stdcam->mmi_resource,
+			     intca->stdcam->mmi_session_number,
+			     MMI_ANSW_ID_CANCEL, NULL, 0);
+
+	/*
+	 * FIXME handle the enquiry somehow
+	 * probably best solution would be calling a user-configurable
+	 * script, so that pins can be handled
+	 */
+
+	return 0;
+}
+
+
+static int mmi_menu_callback(void *arg, uint8_t slot_id,
+			     uint16_t session_number,
+			     struct en50221_app_mmi_text *title,
+			     struct en50221_app_mmi_text *sub_title,
+			     struct en50221_app_mmi_text *bottom,
+			     uint32_t item_count,
+			     struct en50221_app_mmi_text *items,
+			     uint32_t item_raw_length, uint8_t * items_raw) {
+
+	struct intca_s	*intca=arg;
+
+	logwrite(LOG_INFO, "MMI menu callback %d", session_number);
+
+	/* FIXME handle the menu - for now just answer with 0x01 */
+	if(1) {
+		en50221_app_mmi_menu_answ(intca->stdcam->mmi_resource,
+					  intca->stdcam->mmi_session_number,
+					  0x01);
+
+		return 0;
+	}
+
+	return 0;
+}
+
+static void *ca_monitor(void *arg) {
+	struct intca_s *intca=arg;
+
+	while(!intca->stop_thread)
+		intca->stdcam->poll(intca->stdcam);
+
+	return (void *) 0;
+}
+
+static void ca_pmt_push_cam(struct intca_s *intca, struct psisec_s *psisec) {
+	struct section		*section;
+	struct section_ext	*section_ext;
+	struct mpeg_pmt_section *pmt;
+	int			size;
+	uint8_t			capmt[4096];
+
+	section=section_codec(psisec->data, psisec->len);
+	if (!section)
+		return;
+
+	section_ext=section_ext_decode(section, 0);
+	if (!section_ext)
+		return;
+
+	pmt=mpeg_pmt_section_codec(section_ext);
+	if (!pmt)
+		return;
+
+	if(!intca->ca_resource_connected)
+		return;
+
+	logwrite(LOG_INFO, "Received new PMT, send it to CAM");
+
+
+	size=en50221_ca_format_pmt(pmt, capmt, sizeof(capmt),
+				intca->moveca, CA_LIST_MANAGEMENT_ADD,
+				CA_PMT_CMD_ID_OK_DESCRAMBLING);
+
+	if (size<0) {
+		logwrite(LOG_ERROR, "en50221_ca_format_pmt returned size %d", size);
+		return;
+	}
+
+	if(en50221_app_ca_pmt(intca->stdcam->ca_resource,
+			intca->stdcam->ca_session_number,
+			capmt, size)) {
+
+			logwrite(LOG_ERROR, "Failed to send PMT");
+			return;
+	}
+
+	return;
+}
+
+/*
+ * This threads waits for the PAT/PMT forwarding to pass in a PMT section
+ * via the async queue. g_async_queue_pop will put the thread to sleep until
+ * there is one.
+ *
+ */
+static void *ca_pmt_monitor(void *arg) {
+	struct intca_s		*intca=arg;
+	struct psisec_s		*section;
+
+	g_async_queue_ref(intca->pmtqueue);
+
+	while(42) {
+		section=g_async_queue_pop(intca->pmtqueue);
+		ca_pmt_push_cam(intca, section);
+		g_free(section);
+	}
+}
+
+void ca_pmt_section_push(struct intca_s *intca, struct psisec_s *section) {
+	struct psisec_s	*sectionclone;
+
+	/* malloc + copy in the hot path? */
+	sectionclone=g_memdup(section, sizeof(struct psisec_s));
+
+	/* Push it into the threads pmt queue */
+	g_async_queue_push(intca->pmtqueue, sectionclone);
+
+	return;
+}
+
+struct intca_s *new_ca(int adapter, int demuxer, int caslot, int moveca,
+		       GList *streams) {
+	struct intca_s	*intca;
+
+	logwrite(LOG_INFO, "Initialize common interface...");
+
+	intca=malloc(sizeof(struct intca_s));
+
+	intca->pmtqueue=g_async_queue_new();
+
+	intca->ca_resource_connected = 0;
+
+	intca->stop_thread = 0;
+
+	intca->adapter = adapter;
+	intca->demuxer = demuxer;
+	intca->caslot = caslot;
+	intca->moveca = moveca;
+
+	intca->inputs = 0;
+
+	/* create transportlayer */
+	intca->tl = en50221_tl_create(1, 16);
+	if(intca->tl == NULL) {
+		logwrite(LOG_ERROR, "Failed to create transport layer");
+		return NULL;
+	}
+
+	/* create session layer */
+	intca->sl = en50221_sl_create(intca->tl, 16);
+	if(intca->sl == NULL) {
+		logwrite(LOG_ERROR, "Failed to create session layer");
+		en50221_tl_destroy(intca->tl);
+		return NULL;
+	}
+
+	/* create stdcam interface */
+	intca->stdcam = en50221_stdcam_create(intca->adapter, intca->caslot,
+				intca->tl, intca->sl);
+
+	if(intca->stdcam == NULL) {
+		en50221_sl_destroy(intca->sl);
+		en50221_tl_destroy(intca->tl);
+		logwrite(LOG_ERROR, "Failed to create stdcam");
+		return NULL;
+	}
+
+	/* hook up the AI callbacks */
+	if(intca->stdcam->ai_resource) {
+		en50221_app_ai_register_callback(intca->stdcam->ai_resource,
+						 ai_callback, intca);
+	}
+
+	/* hook up the CA callbacks */
+	if(intca->stdcam->ca_resource) {
+		en50221_app_ca_register_info_callback(intca->stdcam->ca_resource,
+						      ca_info_callback, intca);
+	}
+
+	/* hook up the MMI callbacks */
+	if(intca->stdcam->mmi_resource) {
+		en50221_app_mmi_register_close_callback(intca->
+							stdcam->mmi_resource,
+							mmi_close_callback,
+							intca);
+
+		en50221_app_mmi_register_display_control_callback(intca->stdcam->mmi_resource,
+								  mmi_display_control_callback,
+								  intca);
+
+		en50221_app_mmi_register_enq_callback(intca->stdcam->mmi_resource,
+						      mmi_enq_callback, intca);
+
+		en50221_app_mmi_register_menu_callback(intca->stdcam->mmi_resource,
+						       mmi_menu_callback, intca);
+
+		en50221_app_mmi_register_list_callback(intca->stdcam->mmi_resource,
+						       mmi_menu_callback, intca);
+	}
+
+	pthread_create(&intca->cathread, NULL, ca_monitor, intca);
+	pthread_create(&intca->pmtthread, NULL, ca_pmt_monitor, intca);
+
+	intca->state = ca_running;
+
+	return intca;
+}
diff --git a/ca.h b/ca.h
new file mode 100644
index 0000000..ce35e5b
--- /dev/null
+++ b/ca.h
@@ -0,0 +1,91 @@
+/* ca interface header */
+
+#ifndef CA_H_
+
+#define CA_H_
+
+#include <stdint.h>
+#include <glib.h>
+#include <sys/poll.h>
+
+#include "psi.h"
+
+#define MAX_PROGRAMS 50
+
+enum ca_state {
+	ca_stopped = 0x00,
+	ca_running = 0x01,
+	ca_paused = 0x02
+};
+
+struct program {
+	int	pnr;
+	int	pmt_version;
+};
+
+struct intca_s {
+	GAsyncQueue	*pmtqueue;
+
+	struct en50221_stdcam *stdcam;
+	struct en50221_transport_layer *tl;
+	struct en50221_session_layer *sl;
+
+	int	adapter;
+	int	demuxer;
+	int	caslot;
+	int	moveca;
+
+	int	pat_version;
+	int	pmt_version;
+
+	int	inputs;
+
+	int	ca_resource_connected;
+
+	GList	*streams;
+
+	enum ca_state   state;
+
+	int	stop_thread;
+	pthread_t       cathread;
+	pthread_t       pmtthread;
+};
+
+/**
+ * constructor for the CI module
+ * @param adapter adapter number
+ * @param demuxer demuxer number
+ * @param caslot caslot number
+ * @param moveca use ca movement
+ * @param streams list of streams to control
+ * @return newly created ca objected
+ */
+struct intca_s *new_ca(int adapter, int demuxer, int caslot, int moveca,
+		       GList * streams);
+
+
+/**
+ * Sends a mmi answer to the mmi menu currently linked
+ * to ui.mmimenu
+ * @param value the answer value
+ */
+void            send_mmi_menu_answ(void *value);
+
+/**
+ * opens a mmi menu in the ui.mmimenu
+ * @param this_gen ca object to use
+ */
+void            open_mmi_menu(void *ca);
+
+
+/**
+ * Send a mmi app answer to mmi session in ui.pin_query
+ * @param value the answer value
+ * @param length length of value
+ */
+void            send_mmi_answ(uint8_t * value, int length);
+
+void		ca_pmt_section_push(struct intca_s *intca, struct psisec_s *section);
+
+
+#endif
diff --git a/getstream.c b/getstream.c
index ce834f7..b7d600d 100644
--- a/getstream.c
+++ b/getstream.c
@@ -1,4 +1,3 @@
-
 #include <sys/poll.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -13,6 +12,9 @@
 #include <pthread.h>
 #include <signal.h>
 
+#include <sys/syscall.h>
+#include <sched.h>
+
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
@@ -152,6 +154,9 @@ int main(int argc, char **argv) {
 		struct adapter_s	*a=al->data;
 		GList			*sl=g_list_first(a->streams);
 
+		/* Start CI-Interface */
+		a->ca = new_ca(a->no, 0, 0, 1, a->streams);
+
 		/* Tune to the one and only transponder */
 		fe_tune_init(a);
 
diff --git a/getstream.h b/getstream.h
index a9139c6..ceca8f0 100644
--- a/getstream.h
+++ b/getstream.h
@@ -14,6 +14,7 @@
 
 #include "sap.h"
 #include "psi.h"
+#include "ca.h"
 
 #if (DVB_API_VERSION==3) && (DVB_API_VERSION_MINOR>=3)
 
@@ -239,6 +240,9 @@ struct adapter_s {
 	int			budgetmode;
 	GList			*streams;
 
+	/* CI */
+	struct intca_s		*ca;
+
 	/* fe.c */
 	struct {
 		int				fd;
diff --git a/pmt.c b/pmt.c
index fa0dabd..064b741 100644
--- a/pmt.c
+++ b/pmt.c
@@ -5,6 +5,7 @@
 
 #include "getstream.h"
 #include "psi.h"
+#include "ca.h"
 
 struct programcb_s {
 	void		(*callback)(void *data, void *arg);
@@ -337,8 +338,6 @@ static void pmt_dvr_pmt_cb(void *data, void *arg) {
 	logwrite(LOG_XTREME, "pmt: dvr section callback");
 	dump_hex(LOG_XTREME, "pmt: PMT ", section->data, section->valid);
 
-	if (!psi_currentnext(section))
-		return;
 
 	if (psi_tableid(section) != PMT_TABLE_ID) {
 		logwrite(LOG_INFO, "pmt: received PMT with broken table id on pid %d", prog->pmtpid);
@@ -351,9 +350,20 @@ static void pmt_dvr_pmt_cb(void *data, void *arg) {
 		return;
 	}
 
+	if (!psi_currentnext(section))
+		return;
+
+	/*
+	 * Did something change in the PMT tables? e.g. did we get
+	 * an updated section (version number changed) or a section
+	 * which we didnt have before
+	 */
 	if (!psi_update_table(&prog->psi, section))
 		return;
 
+	if (prog->adapter->ca)
+		ca_pmt_section_push(prog->adapter->ca, section);
+
 	pmt=pmt_parse(prog);
 
 	if (prog->pmtlast)

-- 
Florian Lohoff                  flo at rfc822.org             +49-171-2280134
	Those who would give up a little freedom to get a little 
          security shall soon have neither - Benjamin Franklin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://gt.owl.de/pipermail/getstream/attachments/20081219/1a5f6cae/attachment.pgp 


More information about the Getstream mailing list