aboutsummaryrefslogtreecommitdiffstats
path: root/lib/libdvben50221
diff options
context:
space:
mode:
authoretobi <git@e-tobi.net>2013-09-03 09:48:41 +0200
committeretobi <git@e-tobi.net>2013-09-03 09:48:41 +0200
commitab959d7b4194715870128e616b8e29d4a101e488 (patch)
tree61a746231d30817be73416a7d67763fd677a1042 /lib/libdvben50221
parent6b350466c4902c5b137e0efaf1d189128a7f18f5 (diff)
downloadlinux-dvb-apps-upstream/1.1.1+rev1207.tar.gz
Imported Upstream version 1.1.1+rev1207upstream/1.1.1+rev1207
Diffstat (limited to 'lib/libdvben50221')
-rw-r--r--lib/libdvben50221/Makefile49
-rw-r--r--lib/libdvben50221/asn_1.c83
-rw-r--r--lib/libdvben50221/asn_1.h41
-rw-r--r--lib/libdvben50221/en50221_app_ai.c191
-rw-r--r--lib/libdvben50221/en50221_app_ai.h136
-rw-r--r--lib/libdvben50221/en50221_app_auth.c180
-rw-r--r--lib/libdvben50221/en50221_app_auth.h123
-rw-r--r--lib/libdvben50221/en50221_app_ca.c631
-rw-r--r--lib/libdvben50221/en50221_app_ca.h264
-rw-r--r--lib/libdvben50221/en50221_app_datetime.c173
-rw-r--r--lib/libdvben50221/en50221_app_datetime.h119
-rw-r--r--lib/libdvben50221/en50221_app_dvb.c282
-rw-r--r--lib/libdvben50221/en50221_app_dvb.h176
-rw-r--r--lib/libdvben50221/en50221_app_epg.c167
-rw-r--r--lib/libdvben50221/en50221_app_epg.h138
-rw-r--r--lib/libdvben50221/en50221_app_lowspeed.c533
-rw-r--r--lib/libdvben50221/en50221_app_lowspeed.h219
-rw-r--r--lib/libdvben50221/en50221_app_mmi.c1397
-rw-r--r--lib/libdvben50221/en50221_app_mmi.h618
-rw-r--r--lib/libdvben50221/en50221_app_rm.c307
-rw-r--r--lib/libdvben50221/en50221_app_rm.h187
-rw-r--r--lib/libdvben50221/en50221_app_smartcard.c296
-rw-r--r--lib/libdvben50221/en50221_app_smartcard.h200
-rw-r--r--lib/libdvben50221/en50221_app_tags.h104
-rw-r--r--lib/libdvben50221/en50221_app_teletext.c141
-rw-r--r--lib/libdvben50221/en50221_app_teletext.h107
-rw-r--r--lib/libdvben50221/en50221_app_utils.c38
-rw-r--r--lib/libdvben50221/en50221_app_utils.h112
-rw-r--r--lib/libdvben50221/en50221_errno.h49
-rw-r--r--lib/libdvben50221/en50221_session.c1055
-rw-r--r--lib/libdvben50221/en50221_session.h232
-rw-r--r--lib/libdvben50221/en50221_stdcam.c54
-rw-r--r--lib/libdvben50221/en50221_stdcam.h102
-rw-r--r--lib/libdvben50221/en50221_stdcam_hlci.c216
-rw-r--r--lib/libdvben50221/en50221_stdcam_llci.c437
-rw-r--r--lib/libdvben50221/en50221_transport.c1296
-rw-r--r--lib/libdvben50221/en50221_transport.h234
37 files changed, 10687 insertions, 0 deletions
diff --git a/lib/libdvben50221/Makefile b/lib/libdvben50221/Makefile
new file mode 100644
index 0000000..797ea2a
--- /dev/null
+++ b/lib/libdvben50221/Makefile
@@ -0,0 +1,49 @@
+# Makefile for linuxtv.org dvb-apps/lib/libdvben50221
+
+includes = asn_1.h \
+ en50221_app_ai.h \
+ en50221_app_auth.h \
+ en50221_app_ca.h \
+ en50221_app_datetime.h \
+ en50221_app_dvb.h \
+ en50221_app_epg.h \
+ en50221_app_lowspeed.h \
+ en50221_app_mmi.h \
+ en50221_app_rm.h \
+ en50221_app_smartcard.h \
+ en50221_app_tags.h \
+ en50221_app_teletext.h \
+ en50221_app_utils.h \
+ en50221_errno.h \
+ en50221_session.h \
+ en50221_stdcam.h \
+ en50221_transport.h
+
+objects = asn_1.o \
+ en50221_app_ai.o \
+ en50221_app_auth.o \
+ en50221_app_ca.o \
+ en50221_app_datetime.o \
+ en50221_app_dvb.o \
+ en50221_app_epg.o \
+ en50221_app_lowspeed.o \
+ en50221_app_mmi.o \
+ en50221_app_rm.o \
+ en50221_app_smartcard.o \
+ en50221_app_teletext.o \
+ en50221_app_utils.o \
+ en50221_session.o \
+ en50221_stdcam.o \
+ en50221_stdcam_hlci.o \
+ en50221_stdcam_llci.o \
+ en50221_transport.o
+
+lib_name = libdvben50221
+
+CPPFLAGS += -I../../lib -DLOG_LEVEL=1 # FIXME
+
+.PHONY: all
+
+all: library
+
+include ../../Make.rules
diff --git a/lib/libdvben50221/asn_1.c b/lib/libdvben50221/asn_1.c
new file mode 100644
index 0000000..803eb60
--- /dev/null
+++ b/lib/libdvben50221/asn_1.c
@@ -0,0 +1,83 @@
+/*
+ ASN.1 routines, implementation for libdvben50221
+ an implementation for the High Level Common Interface
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include "asn_1.h"
+
+int asn_1_decode(uint16_t * length, uint8_t * asn_1_array,
+ uint32_t asn_1_array_len)
+{
+ uint8_t length_field;
+
+ if (asn_1_array_len < 1)
+ return -1;
+ length_field = asn_1_array[0];
+
+ if (length_field < 0x80) {
+ // there is only one word
+ *length = length_field & 0x7f;
+ return 1;
+ } else if (length_field == 0x81) {
+ if (asn_1_array_len < 2)
+ return -1;
+
+ *length = asn_1_array[1];
+ return 2;
+ } else if (length_field == 0x82) {
+ if (asn_1_array_len < 3)
+ return -1;
+
+ *length = (asn_1_array[1] << 8) | asn_1_array[2];
+ return 3;
+ }
+
+ return -1;
+}
+
+int asn_1_encode(uint16_t length, uint8_t * asn_1_array,
+ uint32_t asn_1_array_len)
+{
+ if (length < 0x80) {
+ if (asn_1_array_len < 1)
+ return -1;
+
+ asn_1_array[0] = length & 0x7f;
+ return 1;
+ } else if (length < 0x100) {
+ if (asn_1_array_len < 2)
+ return -1;
+
+ asn_1_array[0] = 0x81;
+ asn_1_array[1] = length;
+ return 2;
+ } else {
+ if (asn_1_array_len < 3)
+ return -1;
+
+ asn_1_array[0] = 0x82;
+ asn_1_array[1] = length >> 8;
+ asn_1_array[2] = length;
+ return 3;
+ }
+
+ // never reached
+}
diff --git a/lib/libdvben50221/asn_1.h b/lib/libdvben50221/asn_1.h
new file mode 100644
index 0000000..c8774db
--- /dev/null
+++ b/lib/libdvben50221/asn_1.h
@@ -0,0 +1,41 @@
+/*
+ ASN.1 routines, implementation for libdvben50221
+ an implementation for the High Level Common Interface
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __ASN_1_H__
+#define __ASN_1_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+
+int asn_1_decode(uint16_t * length, uint8_t * asn_1_array,
+ uint32_t asn_1_array_len);
+int asn_1_encode(uint16_t length, uint8_t * asn_1_array,
+ uint32_t asn_1_array_len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_ai.c b/lib/libdvben50221/en50221_app_ai.c
new file mode 100644
index 0000000..b7ded66
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_ai.c
@@ -0,0 +1,191 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_ai.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_ai {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_ai_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_ai_parse_app_info(struct en50221_app_ai *ai,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+struct en50221_app_ai *en50221_app_ai_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_ai *ai = NULL;
+
+ // create structure and set it up
+ ai = malloc(sizeof(struct en50221_app_ai));
+ if (ai == NULL) {
+ return NULL;
+ }
+ ai->funcs = funcs;
+ ai->callback = NULL;
+
+ pthread_mutex_init(&ai->lock, NULL);
+
+ // done
+ return ai;
+}
+
+void en50221_app_ai_destroy(struct en50221_app_ai *ai)
+{
+ pthread_mutex_destroy(&ai->lock);
+ free(ai);
+}
+
+void en50221_app_ai_register_callback(struct en50221_app_ai *ai,
+ en50221_app_ai_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&ai->lock);
+ ai->callback = callback;
+ ai->callback_arg = arg;
+ pthread_mutex_unlock(&ai->lock);
+}
+
+int en50221_app_ai_enquiry(struct en50221_app_ai *ai,
+ uint16_t session_number)
+{
+ uint8_t data[4];
+
+ data[0] = (TAG_APP_INFO_ENQUIRY >> 16) & 0xFF;
+ data[1] = (TAG_APP_INFO_ENQUIRY >> 8) & 0xFF;
+ data[2] = TAG_APP_INFO_ENQUIRY & 0xFF;
+ data[3] = 0;
+
+ return ai->funcs->send_data(ai->funcs->arg, session_number, data, 4);
+}
+
+int en50221_app_ai_entermenu(struct en50221_app_ai *ai,
+ uint16_t session_number)
+{
+ uint8_t data[4];
+
+ data[0] = (TAG_ENTER_MENU >> 16) & 0xFF;
+ data[1] = (TAG_ENTER_MENU >> 8) & 0xFF;
+ data[2] = TAG_ENTER_MENU & 0xFF;
+ data[3] = 0;
+
+ return ai->funcs->send_data(ai->funcs->arg, session_number, data, 4);
+}
+
+int en50221_app_ai_message(struct en50221_app_ai *ai,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_APP_INFO:
+ return en50221_app_ai_parse_app_info(ai, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+
+
+
+
+static int en50221_app_ai_parse_app_info(struct en50221_app_ai *ai,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // parse the length field
+ int length_field_len;
+ uint16_t asn_data_length;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return -1;
+ }
+ // check it
+ if (asn_data_length < 6) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *app_info = data + length_field_len;
+
+ // parse the fields
+ uint8_t application_type = app_info[0];
+ uint16_t application_manufacturer = (app_info[1] << 8) | app_info[2];
+ uint16_t manufacturer_code = (app_info[3] << 8) | app_info[4];
+ uint8_t menu_string_length = app_info[5];
+ uint8_t *menu_string = app_info + 6;
+
+ // check the menu_string_length
+ if (menu_string_length > (asn_data_length - 6)) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received bad menu string length - adjusting\n");
+ menu_string_length = asn_data_length - 6;
+ }
+ // tell the app
+ pthread_mutex_lock(&ai->lock);
+ en50221_app_ai_callback cb = ai->callback;
+ void *cb_arg = ai->callback_arg;
+ pthread_mutex_unlock(&ai->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ application_type, application_manufacturer,
+ manufacturer_code, menu_string_length,
+ menu_string);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_ai.h b/lib/libdvben50221/en50221_app_ai.h
new file mode 100644
index 0000000..18b5cd2
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_ai.h
@@ -0,0 +1,136 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_AI_H__
+#define __EN50221_APPLICATION_AI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_AI_RESOURCEID MKRID(2,1,1)
+
+#define APPLICATION_TYPE_CA 0x01
+#define APPLICATION_TYPE_EPG 0x02
+
+/**
+ * Type definition for application callback function - called when we receive
+ * an application info object.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Resource id concerned.
+ * @param application_type Type of application.
+ * @param application_manufacturer Manufacturer of application.
+ * @param manufacturer_code Manufacturer specific code.
+ * @param menu_string_length Length of menu string.
+ * @param menu_string The menu string itself.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_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);
+
+/**
+ * Opaque type representing an application information resource.
+ */
+struct en50221_app_ai;
+
+/**
+ * Create an instance of an application information resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_ai *en50221_app_ai_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of an application information resource.
+ *
+ * @param ai Instance to destroy.
+ */
+extern void en50221_app_ai_destroy(struct en50221_app_ai *ai);
+
+/**
+ * Register a callback for reception of application_info objects.
+ *
+ * @param ai Application information instance.
+ * @param callback Callback function.
+ * @param arg Private argument passed during calls to the callback.
+ */
+extern void en50221_app_ai_register_callback(struct en50221_app_ai *ai,
+ en50221_app_ai_callback,
+ void *arg);
+
+/**
+ * send a enquiry for the app_info provided by a module
+ *
+ * @param ai Application information instance.
+ * @param session_number Session to send on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ai_enquiry(struct en50221_app_ai *ai,
+ uint16_t session_number);
+
+/**
+ * send a enter_menu tag, this will make the application
+ * open a new MMI session to provide a Menu, or so.
+ *
+ * @param ai Application information instance.
+ * @param session_number Session to send on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ai_entermenu(struct en50221_app_ai *ai,
+ uint16_t session_number);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param ai Application information instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ai_message(struct en50221_app_ai *ai,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_auth.c b/lib/libdvben50221/en50221_app_auth.c
new file mode 100644
index 0000000..a8902c1
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_auth.c
@@ -0,0 +1,180 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_auth.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_auth {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_auth_request_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_auth_parse_request(struct en50221_app_auth *private,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+struct en50221_app_auth *en50221_app_auth_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_auth *auth = NULL;
+
+ // create structure and set it up
+ auth = malloc(sizeof(struct en50221_app_auth));
+ if (auth == NULL) {
+ return NULL;
+ }
+ auth->funcs = funcs;
+ auth->callback = NULL;
+
+ pthread_mutex_init(&auth->lock, NULL);
+
+ // done
+ return auth;
+}
+
+void en50221_app_auth_destroy(struct en50221_app_auth *auth)
+{
+ pthread_mutex_destroy(&auth->lock);
+ free(auth);
+}
+
+void en50221_app_auth_register_request_callback(struct en50221_app_auth *auth,
+ en50221_app_auth_request_callback callback, void *arg)
+{
+ pthread_mutex_lock(&auth->lock);
+ auth->callback = callback;
+ auth->callback_arg = arg;
+ pthread_mutex_unlock(&auth->lock);
+}
+
+int en50221_app_auth_send(struct en50221_app_auth *auth,
+ uint16_t session_number,
+ uint16_t auth_protocol_id, uint8_t * auth_data,
+ uint32_t auth_data_length)
+{
+ uint8_t buf[10];
+
+ // the header
+ buf[0] = (TAG_AUTH_RESP >> 16) & 0xFF;
+ buf[1] = (TAG_AUTH_RESP >> 8) & 0xFF;
+ buf[2] = TAG_AUTH_RESP & 0xFF;
+
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(auth_data_length + 2, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ // the phase_id
+ buf[3 + length_field_len] = auth_protocol_id >> 8;
+ buf[3 + length_field_len + 1] = auth_protocol_id;
+
+ // build the iovecs
+ struct iovec iov[2];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len + 2;
+ iov[1].iov_base = auth_data;
+ iov[1].iov_len = auth_data_length;
+
+ // sendit
+ return auth->funcs->send_datav(auth->funcs->arg, session_number,
+ iov, 2);
+}
+
+int en50221_app_auth_message(struct en50221_app_auth *auth,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_AUTH_REQ:
+ return en50221_app_auth_parse_request(auth, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+static int en50221_app_auth_parse_request(struct en50221_app_auth *auth,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *auth_data = data + length_field_len;
+
+ // process it
+ uint16_t auth_protocol_id = (auth_data[0] << 8) | auth_data[1];
+
+ // tell the app
+ pthread_mutex_lock(&auth->lock);
+ en50221_app_auth_request_callback cb = auth->callback;
+ void *cb_arg = auth->callback_arg;
+ pthread_mutex_unlock(&auth->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ auth_protocol_id, auth_data + 2,
+ asn_data_length - 2);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_auth.h b/lib/libdvben50221/en50221_app_auth.h
new file mode 100644
index 0000000..2b1d2e7
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_auth.h
@@ -0,0 +1,123 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_auth_H__
+#define __EN50221_APPLICATION_auth_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_AUTH_RESOURCEID MKRID(16,1,1)
+
+/**
+ * Type definition for request - called when we receive a auth request from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param auth_protocol_id Auth protocol id.
+ * @param auth_data Data for the request.
+ * @param auth_data_lenghth Number of bytes.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_auth_request_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint16_t auth_protcol_id,
+ uint8_t *auth_data,
+ uint32_t auth_data_length);
+
+/**
+ * Opaque type representing a auth resource.
+ */
+struct en50221_app_auth;
+
+/**
+ * Create an instance of the auth resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_auth *en50221_app_auth_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the auth resource.
+ *
+ * @param auth Instance to destroy.
+ */
+extern void en50221_app_auth_destroy(struct en50221_app_auth *auth);
+
+/**
+ * Register the callback for when we receive a request.
+ *
+ * @param auth auth resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_auth_register_request_callback(struct en50221_app_auth *auth,
+ en50221_app_auth_request_callback callback,
+ void *arg);
+
+/**
+ * Send an auth response to the CAM.
+ *
+ * @param auth auth resource instance.
+ * @param session_number Session number to send it on.
+ * @param auth_protocol_id Auth protocol id.
+ * @param auth_data Auth data.
+ * @param auth_data_length Number of bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_auth_send(struct en50221_app_auth *auth,
+ uint16_t session_number,
+ uint16_t auth_protocol_id,
+ uint8_t *auth_data,
+ uint32_t auth_data_length);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param auth Authentication instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_auth_message(struct en50221_app_auth *auth,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_ca.c b/lib/libdvben50221/en50221_app_ca.c
new file mode 100644
index 0000000..22d4499
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_ca.c
@@ -0,0 +1,631 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include <libucsi/mpeg/descriptor.h>
+#include "en50221_app_ca.h"
+#include "asn_1.h"
+
+// tags supported by this resource
+#define TAG_CA_INFO_ENQUIRY 0x9f8030
+#define TAG_CA_INFO 0x9f8031
+#define TAG_CA_PMT 0x9f8032
+#define TAG_CA_PMT_REPLY 0x9f8033
+
+struct en50221_app_ca {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_ca_info_callback ca_info_callback;
+ void *ca_info_callback_arg;
+
+ en50221_app_ca_pmt_reply_callback ca_pmt_reply_callback;
+ void *ca_pmt_reply_callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+struct ca_pmt_descriptor {
+ uint8_t *descriptor;
+ uint16_t length;
+
+ struct ca_pmt_descriptor *next;
+};
+
+struct ca_pmt_stream {
+ uint8_t stream_type;
+ uint16_t pid;
+ struct ca_pmt_descriptor *descriptors;
+ uint32_t descriptors_length;
+ uint32_t descriptors_count;
+
+ struct ca_pmt_stream *next;
+};
+
+static int en50221_ca_extract_pmt_descriptors(struct mpeg_pmt_section *pmt,
+ struct ca_pmt_descriptor **outdescriptors);
+static int en50221_ca_extract_streams(struct mpeg_pmt_section *pmt,
+ struct ca_pmt_stream **outstreams);
+static void en50221_ca_try_move_pmt_descriptors(struct ca_pmt_descriptor **pmt_descriptors,
+ struct ca_pmt_stream **pmt_streams);
+static uint32_t en50221_ca_calculate_length(struct ca_pmt_descriptor *pmt_descriptors,
+ uint32_t *pmt_descriptors_length,
+ struct ca_pmt_stream *pmt_streams);
+static int en50221_app_ca_parse_info(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length);
+static int en50221_app_ca_parse_reply(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_ca *en50221_app_ca_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_ca *ca = NULL;
+
+ // create structure and set it up
+ ca = malloc(sizeof(struct en50221_app_ca));
+ if (ca == NULL) {
+ return NULL;
+ }
+ ca->funcs = funcs;
+ ca->ca_info_callback = NULL;
+ ca->ca_pmt_reply_callback = NULL;
+
+ pthread_mutex_init(&ca->lock, NULL);
+
+ // done
+ return ca;
+}
+
+void en50221_app_ca_destroy(struct en50221_app_ca *ca)
+{
+ pthread_mutex_destroy(&ca->lock);
+ free(ca);
+}
+
+void en50221_app_ca_register_info_callback(struct en50221_app_ca *ca,
+ en50221_app_ca_info_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&ca->lock);
+ ca->ca_info_callback = callback;
+ ca->ca_info_callback_arg = arg;
+ pthread_mutex_unlock(&ca->lock);
+}
+
+void en50221_app_ca_register_pmt_reply_callback(struct en50221_app_ca *ca,
+ en50221_app_ca_pmt_reply_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&ca->lock);
+ ca->ca_pmt_reply_callback = callback;
+ ca->ca_pmt_reply_callback_arg = arg;
+ pthread_mutex_unlock(&ca->lock);
+}
+
+int en50221_app_ca_info_enq(struct en50221_app_ca *ca,
+ uint16_t session_number)
+{
+ uint8_t data[4];
+
+ data[0] = (TAG_CA_INFO_ENQUIRY >> 16) & 0xFF;
+ data[1] = (TAG_CA_INFO_ENQUIRY >> 8) & 0xFF;
+ data[2] = TAG_CA_INFO_ENQUIRY & 0xFF;
+ data[3] = 0;
+ return ca->funcs->send_data(ca->funcs->arg, session_number, data, 4);
+}
+
+int en50221_app_ca_pmt(struct en50221_app_ca *ca,
+ uint16_t session_number,
+ uint8_t * ca_pmt, uint32_t ca_pmt_length)
+{
+ uint8_t buf[10];
+
+ // set up the tag
+ buf[0] = (TAG_CA_PMT >> 16) & 0xFF;
+ buf[1] = (TAG_CA_PMT >> 8) & 0xFF;
+ buf[2] = TAG_CA_PMT & 0xFF;
+
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(ca_pmt_length, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ // build the iovecs
+ struct iovec iov[2];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len;
+ iov[1].iov_base = ca_pmt;
+ iov[1].iov_len = ca_pmt_length;
+
+ // create the data and send it
+ return ca->funcs->send_datav(ca->funcs->arg, session_number, iov, 2);
+}
+
+int en50221_app_ca_message(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_CA_INFO:
+ return en50221_app_ca_parse_info(ca, slot_id,
+ session_number, data + 3,
+ data_length - 3);
+ case TAG_CA_PMT_REPLY:
+ return en50221_app_ca_parse_reply(ca, slot_id,
+ session_number, data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+int en50221_ca_format_pmt(struct mpeg_pmt_section *pmt, uint8_t * data,
+ uint32_t data_length, int move_ca_descriptors,
+ uint8_t ca_pmt_list_management,
+ uint8_t ca_pmt_cmd_id)
+{
+ struct ca_pmt_descriptor *pmt_descriptors = NULL;
+ uint32_t pmt_descriptors_length = 0;
+ struct ca_pmt_stream *pmt_streams = NULL;
+ uint32_t total_required_length = 0;
+ struct ca_pmt_descriptor *cur_d;
+ struct ca_pmt_stream *cur_s;
+ int result = -1;
+
+ // extract the descriptors and streams
+ if (en50221_ca_extract_pmt_descriptors(pmt, &pmt_descriptors))
+ goto cleanup;
+ if (en50221_ca_extract_streams(pmt, &pmt_streams))
+ goto cleanup;
+
+ // try and merge them if we have no PMT descriptors
+ if ((pmt_descriptors == NULL) && move_ca_descriptors) {
+ en50221_ca_try_move_pmt_descriptors(&pmt_descriptors,
+ &pmt_streams);
+ }
+ // calculate the length of all descriptors/streams and the total length required
+ total_required_length =
+ en50221_ca_calculate_length(pmt_descriptors,
+ &pmt_descriptors_length,
+ pmt_streams);
+
+ // ensure we were supplied with enough data
+ if (total_required_length > data_length) {
+ goto cleanup;
+ }
+ // format the start of the PMT
+ uint32_t data_pos = 0;
+ data[data_pos++] = ca_pmt_list_management;
+ data[data_pos++] = mpeg_pmt_section_program_number(pmt) >> 8;
+ data[data_pos++] = mpeg_pmt_section_program_number(pmt);
+ data[data_pos++] =
+ (pmt->head.version_number << 1) | pmt->head.
+ current_next_indicator;
+ data[data_pos++] = (pmt_descriptors_length >> 8) & 0x0f;
+ data[data_pos++] = pmt_descriptors_length;
+
+ // append the PMT descriptors
+ if (pmt_descriptors_length) {
+ data[data_pos++] = ca_pmt_cmd_id;
+ cur_d = pmt_descriptors;
+ while (cur_d) {
+ memcpy(data + data_pos, cur_d->descriptor,
+ cur_d->length);
+ data_pos += cur_d->length;
+ cur_d = cur_d->next;
+ }
+ }
+ // now, append the streams
+ cur_s = pmt_streams;
+ while (cur_s) {
+ data[data_pos++] = cur_s->stream_type;
+ data[data_pos++] = (cur_s->pid >> 8) & 0x1f;
+ data[data_pos++] = cur_s->pid;
+ data[data_pos++] = (cur_s->descriptors_length >> 8) & 0x0f;
+ data[data_pos++] = cur_s->descriptors_length;
+
+ // append the stream descriptors
+ if (cur_s->descriptors_length) {
+ data[data_pos++] = ca_pmt_cmd_id;
+ cur_d = cur_s->descriptors;
+ while (cur_d) {
+ memcpy(data + data_pos, cur_d->descriptor,
+ cur_d->length);
+ data_pos += cur_d->length;
+ cur_d = cur_d->next;
+ }
+ }
+ cur_s = cur_s->next;
+ }
+ result = data_pos;
+
+
+ cleanup:
+ // free the PMT descriptors
+ cur_d = pmt_descriptors;
+ while (cur_d) {
+ struct ca_pmt_descriptor *next = cur_d->next;
+ free(cur_d);
+ cur_d = next;
+ }
+
+ // free the streams
+ cur_s = pmt_streams;
+ while (cur_s) {
+ struct ca_pmt_stream *next_s = cur_s->next;
+
+ // free the stream descriptors
+ cur_d = cur_s->descriptors;
+ while (cur_d) {
+ struct ca_pmt_descriptor *next_d = cur_d->next;
+ free(cur_d);
+ cur_d = next_d;
+ }
+
+ free(cur_s);
+ cur_s = next_s;
+ }
+ return result;
+}
+
+
+
+
+
+
+
+static int en50221_ca_extract_pmt_descriptors(struct mpeg_pmt_section *pmt,
+ struct ca_pmt_descriptor **outdescriptors)
+{
+ struct ca_pmt_descriptor *descriptors = NULL;
+ struct ca_pmt_descriptor *descriptors_tail = NULL;
+ struct ca_pmt_descriptor *cur_d;
+
+ struct descriptor *cur_descriptor;
+ mpeg_pmt_section_descriptors_for_each(pmt, cur_descriptor) {
+ if (cur_descriptor->tag == dtag_mpeg_ca) {
+ // create a new structure for this one
+ struct ca_pmt_descriptor *new_d =
+ malloc(sizeof(struct ca_pmt_descriptor));
+ if (new_d == NULL) {
+ goto error_exit;
+ }
+ new_d->descriptor = (uint8_t *) cur_descriptor;
+ new_d->length = cur_descriptor->len + 2;
+ new_d->next = NULL;
+
+ // append it to the list
+ if (descriptors == NULL) {
+ descriptors = new_d;
+ } else {
+ descriptors_tail->next = new_d;
+ }
+ descriptors_tail = new_d;
+ }
+ }
+ *outdescriptors = descriptors;
+ return 0;
+
+error_exit:
+ cur_d = descriptors;
+ while (cur_d) {
+ struct ca_pmt_descriptor *next = cur_d->next;
+ free(cur_d);
+ cur_d = next;
+ }
+ return -1;
+}
+
+static int en50221_ca_extract_streams(struct mpeg_pmt_section *pmt,
+ struct ca_pmt_stream **outstreams)
+{
+ struct ca_pmt_stream *streams = NULL;
+ struct ca_pmt_stream *streams_tail = NULL;
+ struct mpeg_pmt_stream *cur_stream;
+ struct descriptor *cur_descriptor;
+ struct ca_pmt_stream *cur_s;
+
+ mpeg_pmt_section_streams_for_each(pmt, cur_stream) {
+ struct ca_pmt_descriptor *descriptors_tail = NULL;
+
+ // create a new structure
+ struct ca_pmt_stream *new_s =
+ malloc(sizeof(struct ca_pmt_stream));
+ if (new_s == NULL) {
+ goto exit_cleanup;
+ }
+ new_s->stream_type = cur_stream->stream_type;
+ new_s->pid = cur_stream->pid;
+ new_s->descriptors = NULL;
+ new_s->next = NULL;
+ new_s->descriptors_count = 0;
+
+ // append it to the list
+ if (streams == NULL) {
+ streams = new_s;
+ } else {
+ streams_tail->next = new_s;
+ }
+ streams_tail = new_s;
+
+ // now process the descriptors
+ mpeg_pmt_stream_descriptors_for_each(cur_stream,
+ cur_descriptor) {
+ if (cur_descriptor->tag == dtag_mpeg_ca) {
+ // create a new structure
+ struct ca_pmt_descriptor *new_d =
+ malloc(sizeof(struct ca_pmt_descriptor));
+ if (new_d == NULL) {
+ goto exit_cleanup;
+ }
+ new_d->descriptor =
+ (uint8_t *) cur_descriptor;
+ new_d->length = cur_descriptor->len + 2;
+ new_d->next = NULL;
+
+ // append it to the list
+ if (new_s->descriptors == NULL) {
+ new_s->descriptors = new_d;
+ } else {
+ descriptors_tail->next = new_d;
+ }
+ descriptors_tail = new_d;
+ new_s->descriptors_count++;
+ }
+ }
+ }
+ *outstreams = streams;
+ return 0;
+
+exit_cleanup:
+ // free the streams
+ cur_s = streams;
+ while (cur_s) {
+ struct ca_pmt_stream *next_s = cur_s->next;
+
+ // free the stream descriptors
+ struct ca_pmt_descriptor *cur_d = cur_s->descriptors;
+ while (cur_d) {
+ struct ca_pmt_descriptor *next_d = cur_d->next;
+ free(cur_d);
+ cur_d = next_d;
+ }
+
+ free(cur_s);
+ cur_s = next_s;
+ }
+ return -1;
+}
+
+static void en50221_ca_try_move_pmt_descriptors(struct ca_pmt_descriptor **pmt_descriptors,
+ struct ca_pmt_stream **pmt_streams)
+{
+ // get the first stream
+ struct ca_pmt_stream *first_stream = *pmt_streams;
+ if (first_stream == NULL)
+ return;
+
+ // Check that all the other streams with CA descriptors have exactly the same CA descriptors
+ struct ca_pmt_stream *cur_stream = first_stream->next;
+ while (cur_stream) {
+ // if there are differing numbers of descriptors, exit right now
+ if (cur_stream->descriptors_count != first_stream->descriptors_count)
+ return;
+
+ // now verify the descriptors match
+ struct ca_pmt_descriptor *cur_descriptor = cur_stream->descriptors;
+ struct ca_pmt_descriptor *first_cur_descriptor = first_stream->descriptors;
+ while (cur_descriptor) {
+ // check the descriptors are the same length
+ if (cur_descriptor->length != first_cur_descriptor->length)
+ return;
+
+ // check their contents match
+ if (memcmp(cur_descriptor->descriptor,
+ first_cur_descriptor->descriptor,
+ cur_descriptor->length)) {
+ return;
+ }
+ // move to next
+ cur_descriptor = cur_descriptor->next;
+ first_cur_descriptor = first_cur_descriptor->next;
+ }
+
+ // move to next
+ cur_stream = cur_stream->next;
+ }
+
+ // if we end up here, all descriptors in all streams matched
+
+ // hook the first stream's descriptors into the PMT's
+ *pmt_descriptors = first_stream->descriptors;
+ first_stream->descriptors = NULL;
+ first_stream->descriptors_count = 0;
+
+ // now free up all the descriptors in the other streams
+ cur_stream = first_stream->next;
+ while (cur_stream) {
+ struct ca_pmt_descriptor *cur_descriptor = cur_stream->descriptors;
+ while (cur_descriptor) {
+ struct ca_pmt_descriptor *next = cur_descriptor->next;
+ free(cur_descriptor);
+ cur_descriptor = next;
+ }
+ cur_stream->descriptors = NULL;
+ cur_stream->descriptors_count = 0;
+ cur_stream = cur_stream->next;
+ }
+}
+
+static uint32_t en50221_ca_calculate_length(struct ca_pmt_descriptor *pmt_descriptors,
+ uint32_t *pmt_descriptors_length,
+ struct ca_pmt_stream *pmt_streams)
+{
+ uint32_t total_required_length = 6; // header
+ struct ca_pmt_stream *cur_s;
+
+ // calcuate the PMT descriptors length
+ (*pmt_descriptors_length) = 0;
+ struct ca_pmt_descriptor *cur_d = pmt_descriptors;
+ while (cur_d) {
+ (*pmt_descriptors_length) += cur_d->length;
+ cur_d = cur_d->next;
+ }
+
+ // add on 1 byte for the ca_pmt_cmd_id if we have some descriptors.
+ if (*pmt_descriptors_length)
+ (*pmt_descriptors_length)++;
+
+ // update the total required length
+ total_required_length += *pmt_descriptors_length;
+
+ // calculate the length of descriptors in the streams
+ cur_s = pmt_streams;
+ while (cur_s) {
+ // calculate the size of descriptors in this stream
+ cur_s->descriptors_length = 0;
+ cur_d = cur_s->descriptors;
+ while (cur_d) {
+ cur_s->descriptors_length += cur_d->length;
+ cur_d = cur_d->next;
+ }
+
+ // add on 1 byte for the ca_pmt_cmd_id if we have some descriptors.
+ if (cur_s->descriptors_length)
+ cur_s->descriptors_length++;
+
+ // update the total required length;
+ total_required_length += 5 + cur_s->descriptors_length;
+
+ cur_s = cur_s->next;
+ }
+
+ // done
+ return total_required_length;
+}
+
+static int en50221_app_ca_parse_info(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ data += length_field_len;
+
+ // parse
+ uint32_t ca_id_count = asn_data_length / 2;
+
+ // byteswap the IDs
+ uint16_t *ids = (uint16_t *) data;
+ uint32_t i;
+ for (i = 0; i < ca_id_count; i++) {
+ bswap16(data);
+ data += 2;
+ }
+
+ // tell the app
+ pthread_mutex_lock(&ca->lock);
+ en50221_app_ca_info_callback cb = ca->ca_info_callback;
+ void *cb_arg = ca->ca_info_callback_arg;
+ pthread_mutex_unlock(&ca->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, ca_id_count,
+ ids);
+ }
+ return 0;
+}
+
+static int en50221_app_ca_parse_reply(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length < 4) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ data += length_field_len;
+ data_length -= length_field_len;
+
+ // process the reply table to fix endian issues
+ uint32_t pos = 4;
+ bswap16(data);
+ while (pos < asn_data_length) {
+ bswap16(data + pos);
+ pos += 3;
+ }
+
+ // tell the app
+ pthread_mutex_lock(&ca->lock);
+ en50221_app_ca_pmt_reply_callback cb = ca->ca_pmt_reply_callback;
+ void *cb_arg = ca->ca_pmt_reply_callback_arg;
+ pthread_mutex_unlock(&ca->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ (struct en50221_app_pmt_reply *) data,
+ asn_data_length);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_ca.h b/lib/libdvben50221/en50221_app_ca.h
new file mode 100644
index 0000000..7405b06
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_ca.h
@@ -0,0 +1,264 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_ca_H__
+#define __EN50221_APPLICATION_ca_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+#include <libucsi/mpeg/pmt_section.h>
+#include <libucsi/dvb/descriptor.h>
+
+#define CA_LIST_MANAGEMENT_MORE 0x00
+#define CA_LIST_MANAGEMENT_FIRST 0x01
+#define CA_LIST_MANAGEMENT_LAST 0x02
+#define CA_LIST_MANAGEMENT_ONLY 0x03
+#define CA_LIST_MANAGEMENT_ADD 0x04
+#define CA_LIST_MANAGEMENT_UPDATE 0x05
+
+#define CA_PMT_CMD_ID_OK_DESCRAMBLING 0x01
+#define CA_PMT_CMD_ID_OK_MMI 0x02
+#define CA_PMT_CMD_ID_QUERY 0x03
+#define CA_PMT_CMD_ID_NOT_SELECTED 0x04
+
+#define CA_ENABLE_DESCRAMBLING_POSSIBLE 0x01
+#define CA_ENABLE_DESCRAMBLING_POSSIBLE_PURCHASE 0x02
+#define CA_ENABLE_DESCRAMBLING_POSSIBLE_TECHNICAL 0x03
+#define CA_ENABLE_DESCRAMBLING_NOT_POSSIBLE_NO_ENTITLEMENT 0x71
+#define CA_ENABLE_DESCRAMBLING_NOT_POSSIBLE_TECHNICAL 0x73
+
+
+#define EN50221_APP_CA_RESOURCEID MKRID(3,1,1)
+
+/**
+ * PMT reply structure.
+ */
+struct en50221_app_pmt_reply {
+ uint16_t program_number;
+ EBIT3(uint8_t reserved_1 : 2;,
+ uint8_t version_number : 5;,
+ uint8_t current_next_indicator : 1;);
+ EBIT2(uint8_t CA_enable_flag : 1;,
+ uint8_t CA_enable : 7;);
+ /* struct en50221_app_pmt_stream streams[] */
+} __attribute__ ((packed));
+
+/**
+ * A stream within a pmt reply structure.
+ */
+struct en50221_app_pmt_stream {
+ EBIT2(uint16_t reserved_1 : 3;,
+ uint16_t es_pid :13;);
+ EBIT2(uint8_t CA_enable_flag : 1;,
+ uint8_t CA_enable : 7;);
+} __attribute__ ((packed));
+
+/**
+ * Convenience iterator for the streams field of the en50221_app_pmt_reply structure.
+ *
+ * @param pmt Pointer to the en50221_app_pmt_reply structure.
+ * @param pos Variable holding a pointer to the current en50221_app_pmt_stream.
+ * @param size Total size of the PMT reply.
+ */
+#define en50221_app_pmt_reply_streams_for_each(pmt, pos, size) \
+ for ((pos) = en50221_app_pmt_reply_streams_first(pmt, size); \
+ (pos); \
+ (pos) = en50221_app_pmt_reply_streams_next(pmt, pos, size))
+
+
+/**
+ * Type definition for command - called when we receive a ca info response.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param ca_id_count Number of ca_system_ids.
+ * @param ca_ids Pointer to list of ca_system_ids.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_ca_info_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t ca_id_count,
+ uint16_t * ca_ids);
+
+/**
+ * Type definition for pmt_reply - called when we receive a pmt_reply.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param reply Pointer to a struct en50221_app_pmt_reply.
+ * @param reply_size Total size of the struct en50221_app_pmt_reply in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_ca_pmt_reply_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ struct en50221_app_pmt_reply *reply,
+ uint32_t reply_size);
+
+/**
+ * Opaque type representing a ca resource.
+ */
+struct en50221_app_ca;
+
+/**
+ * Create an instance of the ca resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_ca *en50221_app_ca_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the ca resource.
+ *
+ * @param ca Instance to destroy.
+ */
+extern void en50221_app_ca_destroy(struct en50221_app_ca *ca);
+
+/**
+ * Register the callback for when we receive a ca info.
+ *
+ * @param ca ca resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_ca_register_info_callback(struct en50221_app_ca *ca,
+ en50221_app_ca_info_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a pmt_reply.
+ *
+ * @param ca ca resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_ca_register_pmt_reply_callback(struct en50221_app_ca *ca,
+ en50221_app_ca_pmt_reply_callback callback,
+ void *arg);
+
+/**
+ * Send a ca_info_req to the CAM.
+ *
+ * @param ca ca resource instance.
+ * @param session_number Session number to send it on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ca_info_enq(struct en50221_app_ca *ca,
+ uint16_t session_number);
+
+/**
+ * Send a ca_pmt structure to the CAM.
+ *
+ * @param ca ca resource instance.
+ * @param session_number Session number to send it on.
+ * @param ca_pmt A ca_pmt structure formatted with the en50221_ca_format_pmt() function.
+ * @param ca_pmt_length Length of ca_pmt structure in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ca_pmt(struct en50221_app_ca *ca,
+ uint16_t session_number,
+ uint8_t * ca_pmt,
+ uint32_t ca_pmt_length);
+
+/**
+ * Transform a libucsi PMT into a binary structure for sending to a CAM.
+ *
+ * @param pmt The source PMT structure.
+ * @param data Pointer to data buffer to write it to.
+ * @param data_length Number of bytes available in data buffer.
+ * @param move_ca_descriptors If non-zero, will attempt to move CA descriptors
+ * in order to reduce the size of the formatted CAPMT.
+ * @param ca_pmt_list_management One of the CA_LIST_MANAGEMENT_*.
+ * @param ca_pmt_cmd_id One of the CA_PMT_CMD_ID_*.
+ * @return Number of bytes used, or -1 on error.
+ */
+extern int en50221_ca_format_pmt(struct mpeg_pmt_section *pmt,
+ uint8_t * data,
+ uint32_t data_length,
+ int move_ca_descriptors,
+ uint8_t ca_pmt_list_management,
+ uint8_t ca_pmt_cmd_id);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param ca CA instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_ca_message(struct en50221_app_ca *ca,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+
+
+
+static inline struct en50221_app_pmt_stream *
+ en50221_app_pmt_reply_streams_first(struct en50221_app_pmt_reply *reply,
+ uint32_t reply_size)
+{
+ uint32_t pos = sizeof(struct en50221_app_pmt_reply);
+
+ if (pos >= reply_size)
+ return NULL;
+
+ return (struct en50221_app_pmt_stream *) ((uint8_t *) reply + pos);
+}
+
+static inline struct en50221_app_pmt_stream *
+ en50221_app_pmt_reply_streams_next(struct en50221_app_pmt_reply *reply,
+ struct en50221_app_pmt_stream *pos,
+ uint32_t reply_size)
+{
+ uint8_t *end = (uint8_t *) reply + reply_size;
+ uint8_t *next =
+ (uint8_t *) pos +
+ sizeof(struct en50221_app_pmt_stream);
+
+ if (next >= end)
+ return NULL;
+
+ return (struct en50221_app_pmt_stream *) next;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/libdvben50221/en50221_app_datetime.c b/lib/libdvben50221/en50221_app_datetime.c
new file mode 100644
index 0000000..6777003
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_datetime.c
@@ -0,0 +1,173 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include <libucsi/dvb/types.h>
+#include "en50221_app_datetime.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_datetime {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_datetime_enquiry_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_datetime_parse_enquiry(struct en50221_app_datetime *datetime,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_datetime *en50221_app_datetime_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_datetime *datetime = NULL;
+
+ // create structure and set it up
+ datetime = malloc(sizeof(struct en50221_app_datetime));
+ if (datetime == NULL) {
+ return NULL;
+ }
+ datetime->funcs = funcs;
+ datetime->callback = NULL;
+
+ pthread_mutex_init(&datetime->lock, NULL);
+
+ // done
+ return datetime;
+}
+
+void en50221_app_datetime_destroy(struct en50221_app_datetime *datetime)
+{
+ pthread_mutex_destroy(&datetime->lock);
+ free(datetime);
+}
+
+void en50221_app_datetime_register_enquiry_callback(struct en50221_app_datetime *datetime,
+ en50221_app_datetime_enquiry_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&datetime->lock);
+ datetime->callback = callback;
+ datetime->callback_arg = arg;
+ pthread_mutex_unlock(&datetime->lock);
+}
+
+int en50221_app_datetime_send(struct en50221_app_datetime *datetime,
+ uint16_t session_number,
+ time_t utc_time, int time_offset)
+{
+ uint8_t data[11];
+ int data_length;
+
+ data[0] = (TAG_DATE_TIME >> 16) & 0xFF;
+ data[1] = (TAG_DATE_TIME >> 8) & 0xFF;
+ data[2] = TAG_DATE_TIME & 0xFF;
+ if (time_offset != -1) {
+ data[3] = 7;
+ unixtime_to_dvbdate(utc_time, data + 4);
+ data[9] = time_offset >> 8;
+ data[10] = time_offset;
+ data_length = 11;
+ } else {
+ data[3] = 5;
+ unixtime_to_dvbdate(utc_time, data + 4);
+ data_length = 9;
+ }
+ return datetime->funcs->send_data(datetime->funcs->arg,
+ session_number, data,
+ data_length);
+}
+
+int en50221_app_datetime_message(struct en50221_app_datetime *datetime,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_DATE_TIME_ENQUIRY:
+ return en50221_app_datetime_parse_enquiry(datetime,
+ slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+
+
+
+
+
+
+
+static int en50221_app_datetime_parse_enquiry(struct en50221_app_datetime *datetime,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t response_interval = data[1];
+
+ // tell the app
+ pthread_mutex_lock(&datetime->lock);
+ en50221_app_datetime_enquiry_callback cb = datetime->callback;
+ void *cb_arg = datetime->callback_arg;
+ pthread_mutex_unlock(&datetime->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ response_interval);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_datetime.h b/lib/libdvben50221/en50221_app_datetime.h
new file mode 100644
index 0000000..4660630
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_datetime.h
@@ -0,0 +1,119 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_DATETIME_H__
+#define __EN50221_APPLICATION_DATETIME_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_DATETIME_RESOURCEID MKRID(36,1,1)
+
+/**
+ * Type definition for enquiry - called when we receive a date/time enquiry from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param response_interval Response interval requested by CAM.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_datetime_enquiry_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t response_interval);
+
+/**
+ * Opaque type representing a datetime resource.
+ */
+struct en50221_app_datetime;
+
+/**
+ * Create an instance of the datetime resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_datetime
+ *en50221_app_datetime_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the datetime resource.
+ *
+ * @param datetime Instance to destroy.
+ */
+extern void en50221_app_datetime_destroy(struct en50221_app_datetime *datetime);
+
+/**
+ * Register the callback for when we receive a enquiry request.
+ *
+ * @param datetime datetime resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_datetime_register_enquiry_callback(struct en50221_app_datetime *datetime,
+ en50221_app_datetime_enquiry_callback callback,
+ void *arg);
+
+/**
+ * Send the time to the CAM.
+ *
+ * @param datetime datetime resource instance.
+ * @param session_number Session number to send it on.
+ * @param utc_time UTC time in unix time format.
+ * @param time_offset If -1, the field will not be transmitted, otherwise it is the offset between
+ * UTC and local time in minutes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_datetime_send(struct en50221_app_datetime *datetime,
+ uint16_t session_number,
+ time_t utc_time,
+ int time_offset);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param datetime datetime instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_datetime_message(struct en50221_app_datetime *datetime,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_dvb.c b/lib/libdvben50221/en50221_app_dvb.c
new file mode 100644
index 0000000..21b2bec
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_dvb.c
@@ -0,0 +1,282 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_dvb.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_dvb {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_dvb_tune_callback tune_callback;
+ void *tune_callback_arg;
+
+ en50221_app_dvb_replace_callback replace_callback;
+ void *replace_callback_arg;
+
+ en50221_app_dvb_clear_replace_callback clear_replace_callback;
+ void *clear_replace_callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_dvb_parse_tune(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+static int en50221_app_dvb_parse_replace(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+static int en50221_app_dvb_parse_clear_replace(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_dvb *en50221_app_dvb_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_dvb *dvb = NULL;
+
+ // create structure and set it up
+ dvb = malloc(sizeof(struct en50221_app_dvb));
+ if (dvb == NULL) {
+ return NULL;
+ }
+ dvb->funcs = funcs;
+ dvb->tune_callback = NULL;
+ dvb->replace_callback = NULL;
+ dvb->clear_replace_callback = NULL;
+
+ pthread_mutex_init(&dvb->lock, NULL);
+
+ // done
+ return dvb;
+}
+
+void en50221_app_dvb_destroy(struct en50221_app_dvb *dvb)
+{
+ pthread_mutex_destroy(&dvb->lock);
+ free(dvb);
+}
+
+void en50221_app_dvb_register_tune_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_tune_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&dvb->lock);
+ dvb->tune_callback = callback;
+ dvb->tune_callback_arg = arg;
+ pthread_mutex_unlock(&dvb->lock);
+}
+
+void en50221_app_dvb_register_replace_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_replace_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&dvb->lock);
+ dvb->replace_callback = callback;
+ dvb->replace_callback_arg = arg;
+ pthread_mutex_unlock(&dvb->lock);
+}
+
+void en50221_app_dvb_register_clear_replace_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_clear_replace_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&dvb->lock);
+ dvb->clear_replace_callback = callback;
+ dvb->clear_replace_callback_arg = arg;
+ pthread_mutex_unlock(&dvb->lock);
+}
+
+int en50221_app_dvb_ask_release(struct en50221_app_dvb *dvb,
+ uint16_t session_number)
+{
+ uint8_t data[4];
+
+ data[0] = (TAG_ASK_RELEASE >> 16) & 0xFF;
+ data[1] = (TAG_ASK_RELEASE >> 8) & 0xFF;
+ data[2] = TAG_ASK_RELEASE & 0xFF;
+ data[3] = 0;
+
+ return dvb->funcs->send_data(dvb->funcs->arg, session_number, data, 4);
+}
+
+int en50221_app_dvb_message(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_TUNE:
+ return en50221_app_dvb_parse_tune(dvb, slot_id,
+ session_number, data + 3,
+ data_length - 3);
+ case TAG_REPLACE:
+ return en50221_app_dvb_parse_replace(dvb, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_CLEAR_REPLACE:
+ return en50221_app_dvb_parse_clear_replace(dvb, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+
+
+
+
+
+
+
+static int en50221_app_dvb_parse_tune(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length)
+{
+ // validate data
+ if (data_length < 9) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 8) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *tune_data = data + 1;
+
+ // parse it
+ uint16_t network_id = (tune_data[0] << 8) | tune_data[1];
+ uint16_t original_network_id = (tune_data[2] << 8) | tune_data[3];
+ uint16_t transport_stream_id = (tune_data[4] << 8) | tune_data[5];
+ uint16_t service_id = (tune_data[6] << 8) | tune_data[7];
+
+ // tell the app
+ pthread_mutex_lock(&dvb->lock);
+ en50221_app_dvb_tune_callback cb = dvb->tune_callback;
+ void *cb_arg = dvb->tune_callback_arg;
+ pthread_mutex_unlock(&dvb->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, network_id,
+ original_network_id, transport_stream_id,
+ service_id);
+ }
+ return 0;
+}
+
+static int en50221_app_dvb_parse_replace(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length < 6) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 5) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *replace_data = data + 1;
+
+ // parse it
+ uint8_t replacement_ref = replace_data[0];
+ uint16_t replace_pid =
+ ((replace_data[1] & 0x1f) << 8) | replace_data[2];
+ uint16_t replacement_pid =
+ ((replace_data[3] & 0x1f) << 8) | replace_data[4];
+
+ // tell the app
+ pthread_mutex_lock(&dvb->lock);
+ en50221_app_dvb_replace_callback cb = dvb->replace_callback;
+ void *cb_arg = dvb->replace_callback_arg;
+ pthread_mutex_unlock(&dvb->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, replacement_ref,
+ replace_pid, replacement_pid);
+ }
+ return 0;
+}
+
+static int en50221_app_dvb_parse_clear_replace(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *replace_data = data + 1;
+
+ // parse it
+ uint8_t replacement_ref = replace_data[0];
+
+ // tell the app
+ pthread_mutex_lock(&dvb->lock);
+ en50221_app_dvb_clear_replace_callback cb =
+ dvb->clear_replace_callback;
+ void *cb_arg = dvb->clear_replace_callback_arg;
+ pthread_mutex_unlock(&dvb->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ replacement_ref);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_dvb.h b/lib/libdvben50221/en50221_app_dvb.h
new file mode 100644
index 0000000..be74e6b
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_dvb.h
@@ -0,0 +1,176 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_DVB_H__
+#define __EN50221_APPLICATION_DVB_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_DVB_RESOURCEID MKRID(32,1,1)
+
+
+/**
+ * Type definition for tune - called when we receive a tune request from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param network_id Network id requested by CAM.
+ * @param original_network_id Original Network id requested by CAM.
+ * @param transport_stream_id Transport stream id requested by CAM.
+ * @param service_id Service id requested by CAM.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_dvb_tune_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint16_t network_id,
+ uint32_t original_network_id,
+ uint16_t transport_stream_id,
+ uint16_t service_id);
+
+/**
+ * Type definition for replace - called when we receive a replace request from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param replacement_ref Replacement ref.
+ * @param replaced_pid PID to replace.
+ * @param replacement_pid PID to replace it with.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_dvb_replace_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t replacement_ref,
+ uint16_t replaced_pid,
+ uint16_t replacement_pid);
+
+
+/**
+ * Type definition for clear_replace - called when we receive a clear_replace request from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param replacement_ref Replacement ref.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_dvb_clear_replace_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t replacement_ref);
+
+
+/**
+ * Opaque type representing a dvb resource.
+ */
+struct en50221_app_dvb;
+
+/**
+ * Create an instance of the dvb resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_dvb *en50221_app_dvb_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the dvb resource.
+ *
+ * @param dvb Instance to destroy.
+ */
+extern void en50221_app_dvb_destroy(struct en50221_app_dvb *dvb);
+
+/**
+ * Register the callback for when we receive a tune request.
+ *
+ * @param dvb DVB resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_dvb_register_tune_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_tune_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a replace request.
+ *
+ * @param dvb DVB resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_dvb_register_replace_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_replace_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a clear replace request.
+ *
+ * @param dvb DVB resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_dvb_register_clear_replace_callback(struct en50221_app_dvb *dvb,
+ en50221_app_dvb_clear_replace_callback callback,
+ void *arg);
+
+/**
+ * Send an ask release request to the CAM.
+ *
+ * @param dvb DVB resource instance.
+ * @param session_number Session number to send it on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_dvb_ask_release(struct en50221_app_dvb *dvb,
+ uint16_t session_number);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param dvb dvb instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_dvb_message(struct en50221_app_dvb *dvb,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_epg.c b/lib/libdvben50221/en50221_app_epg.c
new file mode 100644
index 0000000..87b4743
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_epg.c
@@ -0,0 +1,167 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include <libucsi/dvb/types.h>
+#include "en50221_app_epg.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_epg {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_epg_reply_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_epg_parse_reply(struct en50221_app_epg *private,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_epg *en50221_app_epg_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_epg *epg = NULL;
+
+ // create structure and set it up
+ epg = malloc(sizeof(struct en50221_app_epg));
+ if (epg == NULL) {
+ return NULL;
+ }
+ epg->funcs = funcs;
+ epg->callback = NULL;
+
+ pthread_mutex_init(&epg->lock, NULL);
+
+ // done
+ return epg;
+}
+
+void en50221_app_epg_destroy(struct en50221_app_epg *epg)
+{
+ pthread_mutex_destroy(&epg->lock);
+ free(epg);
+}
+
+void en50221_app_epg_register_enquiry_callback(struct en50221_app_epg *epg,
+ en50221_app_epg_reply_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&epg->lock);
+ epg->callback = callback;
+ epg->callback_arg = arg;
+ pthread_mutex_unlock(&epg->lock);
+}
+
+int en50221_app_epg_enquire(struct en50221_app_epg *epg,
+ uint16_t session_number,
+ uint8_t command_id,
+ uint16_t network_id,
+ uint16_t original_network_id,
+ uint16_t transport_stream_id,
+ uint16_t service_id, uint16_t event_id)
+{
+ uint8_t data[15];
+
+ data[0] = (TAG_EPG_ENQUIRY >> 16) & 0xFF;
+ data[1] = (TAG_EPG_ENQUIRY >> 8) & 0xFF;
+ data[2] = TAG_EPG_ENQUIRY & 0xFF;
+ data[3] = 11;
+ data[4] = command_id;
+ data[5] = network_id >> 8;
+ data[6] = network_id;
+ data[7] = original_network_id >> 8;
+ data[8] = original_network_id;
+ data[9] = transport_stream_id >> 8;
+ data[10] = transport_stream_id;
+ data[11] = service_id >> 8;
+ data[12] = service_id;
+ data[13] = event_id >> 8;
+ data[14] = event_id;
+ return epg->funcs->send_data(epg->funcs->arg, session_number, data, 15);
+}
+
+int en50221_app_epg_message(struct en50221_app_epg *epg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ struct en50221_app_epg *private = (struct en50221_app_epg *) epg;
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_EPG_REPLY:
+ return en50221_app_epg_parse_reply(private, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+static int en50221_app_epg_parse_reply(struct en50221_app_epg *epg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t event_status = data[1];
+
+ // tell the app
+ pthread_mutex_lock(&epg->lock);
+ en50221_app_epg_reply_callback cb = epg->callback;
+ void *cb_arg = epg->callback_arg;
+ pthread_mutex_unlock(&epg->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, event_status);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_epg.h b/lib/libdvben50221/en50221_app_epg.h
new file mode 100644
index 0000000..dcfe9da
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_epg.h
@@ -0,0 +1,138 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_epg_H__
+#define __EN50221_APPLICATION_epg_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EPG_COMMAND_ID_MMI 0x02
+#define EPG_COMMAND_ID_QUERY 0x03
+
+#define EPG_EVENTSTATUS_ENTITLEMENT_UNKNOWN 0x00
+#define EPG_EVENTSTATUS_ENTITLEMENT_AVAILABLE 0x01
+#define EPG_EVENTSTATUS_ENTITLEMENT_NOT_AVAILABLE 0x02
+#define EPG_EVENTSTATUS_MMI_DIALOGUE_REQUIRED 0x03
+#define EPG_EVENTSTATUS_MMI_COMPLETE_UNKNOWN 0x04
+#define EPG_EVENTSTATUS_MMI_COMPLETE_AVAILABLE 0x05
+#define EPG_EVENTSTATUS_MMI_COMPLETE_NOT_AVAILABLE 0x06
+
+#define EN50221_APP_EPG_RESOURCEID(INSTANCE_NUM) MKRID(120,(INSTANCE_NUM),1)
+
+
+
+/**
+ * Type definition for reply - called when we receive an EPG reply from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param event_status One of the EPG_EVENTSTATUS_* values.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_epg_reply_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t event_status);
+
+/**
+ * Opaque type representing a epg resource.
+ */
+struct en50221_app_epg;
+
+/**
+ * Create an instance of the epg resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_epg *en50221_app_epg_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the epg resource.
+ *
+ * @param epg Instance to destroy.
+ */
+extern void en50221_app_epg_destroy(struct en50221_app_epg *epg);
+
+/**
+ * Register the callback for when we receive a enquiry response.
+ *
+ * @param epg epg resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_epg_register_reply_callback(struct en50221_app_epg *epg,
+ en50221_app_epg_reply_callback callback,
+ void *arg);
+
+/**
+ * Enquire about the entitlement status for an EPG entry.
+ *
+ * @param epg epg resource instance.
+ * @param session_number Session number to send it on.
+ * @param command_id One of the EPG_COMMAND_ID_* fields.
+ * @param network_id Network ID concerned.
+ * @param original_network_id Original network ID concerned.
+ * @param transport_stream_id Transport stream ID concerned.
+ * @param service_id Service ID concerned.
+ * @param event_id Event ID concerned.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_epg_enquire(struct en50221_app_epg *epg,
+ uint16_t session_number,
+ uint8_t command_id,
+ uint16_t network_id,
+ uint16_t original_network_id,
+ uint16_t transport_stream_id,
+ uint16_t service_id,
+ uint16_t event_id);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param epg epg instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_epg_message(struct en50221_app_epg *epg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_lowspeed.c b/lib/libdvben50221/en50221_app_lowspeed.c
new file mode 100644
index 0000000..a66598a
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_lowspeed.c
@@ -0,0 +1,533 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_lowspeed.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_lowspeed_session {
+ uint16_t session_number;
+ uint8_t *block_chain;
+ uint32_t block_length;
+
+ struct en50221_app_lowspeed_session *next;
+};
+
+struct en50221_app_lowspeed {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_lowspeed_command_callback command_callback;
+ void *command_callback_arg;
+
+ en50221_app_lowspeed_send_callback send_callback;
+ void *send_callback_arg;
+
+ struct en50221_app_lowspeed_session *sessions;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_lowspeed_parse_connect_on_channel(struct en50221_app_lowspeed_command *command,
+ uint8_t *data,
+ int data_length);
+static int en50221_app_lowspeed_parse_command(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length);
+static int en50221_app_lowspeed_parse_send(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ int more_last,
+ uint8_t *data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_lowspeed *en50221_app_lowspeed_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_lowspeed *lowspeed = NULL;
+
+ // create structure and set it up
+ lowspeed = malloc(sizeof(struct en50221_app_lowspeed));
+ if (lowspeed == NULL) {
+ return NULL;
+ }
+ lowspeed->funcs = funcs;
+ lowspeed->command_callback = NULL;
+ lowspeed->send_callback = NULL;
+ lowspeed->sessions = NULL;
+
+ pthread_mutex_init(&lowspeed->lock, NULL);
+
+ // done
+ return lowspeed;
+}
+
+void en50221_app_lowspeed_destroy(struct en50221_app_lowspeed *lowspeed)
+{
+ struct en50221_app_lowspeed_session *cur_s = lowspeed->sessions;
+ while (cur_s) {
+ struct en50221_app_lowspeed_session *next = cur_s->next;
+ if (cur_s->block_chain)
+ free(cur_s->block_chain);
+ free(cur_s);
+ cur_s = next;
+ }
+
+ pthread_mutex_destroy(&lowspeed->lock);
+ free(lowspeed);
+}
+
+void en50221_app_lowspeed_clear_session(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number)
+{
+ pthread_mutex_lock(&lowspeed->lock);
+ struct en50221_app_lowspeed_session *cur_s = lowspeed->sessions;
+ struct en50221_app_lowspeed_session *prev_s = NULL;
+ while (cur_s) {
+ if (cur_s->session_number == session_number) {
+ if (cur_s->block_chain)
+ free(cur_s->block_chain);
+ if (prev_s) {
+ prev_s->next = cur_s->next;
+ } else {
+ lowspeed->sessions = cur_s->next;
+ }
+ free(cur_s);
+ return;
+ }
+
+ prev_s = cur_s;
+ cur_s = cur_s->next;
+ }
+ pthread_mutex_unlock(&lowspeed->lock);
+}
+
+void en50221_app_lowspeed_register_command_callback(struct en50221_app_lowspeed *lowspeed,
+ en50221_app_lowspeed_command_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&lowspeed->lock);
+ lowspeed->command_callback = callback;
+ lowspeed->command_callback_arg = arg;
+ pthread_mutex_unlock(&lowspeed->lock);
+}
+
+void en50221_app_lowspeed_register_send_callback(struct en50221_app_lowspeed *lowspeed,
+ en50221_app_lowspeed_send_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&lowspeed->lock);
+ lowspeed->send_callback = callback;
+ lowspeed->send_callback_arg = arg;
+ pthread_mutex_unlock(&lowspeed->lock);
+}
+
+int en50221_app_lowspeed_send_comms_reply(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number,
+ uint8_t comms_reply_id,
+ uint8_t return_value)
+{
+ uint8_t data[6];
+
+ data[0] = (TAG_COMMS_REPLY >> 16) & 0xFF;
+ data[1] = (TAG_COMMS_REPLY >> 8) & 0xFF;
+ data[2] = TAG_COMMS_REPLY & 0xFF;
+ data[3] = 2;
+ data[4] = comms_reply_id;
+ data[5] = return_value;
+ return lowspeed->funcs->send_data(lowspeed->funcs->arg,
+ session_number, data, 6);
+}
+
+int en50221_app_lowspeed_send_comms_data(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number,
+ uint8_t phase_id,
+ uint32_t tx_data_length,
+ uint8_t * tx_data)
+{
+ uint8_t buf[10];
+
+ // the spec defines this limit
+ if (tx_data_length > 254) {
+ return -1;
+ }
+ // set up the tag
+ buf[0] = (TAG_COMMS_RECV_LAST >> 16) & 0xFF;
+ buf[1] = (TAG_COMMS_RECV_LAST >> 8) & 0xFF;
+ buf[2] = TAG_COMMS_RECV_LAST & 0xFF;
+
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(tx_data_length + 1, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ // the phase_id
+ buf[3 + length_field_len] = phase_id;
+
+ // build the iovecs
+ struct iovec iov[2];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len + 1;
+ iov[1].iov_base = tx_data;
+ iov[1].iov_len = tx_data_length;
+
+ // create the data and send it
+ return lowspeed->funcs->send_datav(lowspeed->funcs->arg,
+ session_number, iov, 2);
+}
+
+int en50221_app_lowspeed_message(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_COMMS_COMMAND:
+ return en50221_app_lowspeed_parse_command(lowspeed,
+ slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_COMMS_SEND_LAST:
+ return en50221_app_lowspeed_parse_send(lowspeed, slot_id,
+ session_number, 1,
+ data + 3,
+ data_length - 3);
+ case TAG_COMMS_SEND_MORE:
+ return en50221_app_lowspeed_parse_send(lowspeed, slot_id,
+ session_number, 0,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+static int en50221_app_lowspeed_parse_connect_on_channel(struct en50221_app_lowspeed_command *command,
+ uint8_t *data,
+ int data_length)
+{
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // check the tag
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+ if (tag != TAG_CONNECTION_DESCRIPTOR) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received bad CONNECT_ON_CHANNEL\n");
+ return -1;
+ }
+ data += 3;
+ data_length -= 3;
+
+ // parse the descriptor-length-field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ data += length_field_len;
+ data_length -= length_field_len;
+
+ // check length field
+ if (asn_data_length > data_length) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length < 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // get the descriptor type
+ command->u.connect_on_channel.descriptor_type = data[0];
+ data++;
+ data_length--;
+ asn_data_length--;
+
+ // deal with the descriptor itself
+ switch (command->u.connect_on_channel.descriptor_type) {
+ case CONNECTION_DESCRIPTOR_TYPE_TELEPHONE:
+ {
+ // get the raw descriptor and validate length
+ struct descriptor *d = (struct descriptor *) data;
+ if (asn_data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length != (2 + d->len)) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ if (d->tag != dtag_dvb_telephone) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received invalid telephone descriptor\n");
+ return -1;
+ }
+ // parse the telephone descriptor
+ command->u.connect_on_channel.descriptor.telephone = dvb_telephone_descriptor_codec(d);
+ if (command->u.connect_on_channel.descriptor.telephone == NULL) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received invalid telephone descriptor\n");
+ return -1;
+ }
+ data += 2 + d->len;
+ data_length -= 2 + d->len;
+ break;
+ }
+
+ case CONNECTION_DESCRIPTOR_TYPE_CABLE:
+ if (asn_data_length != 1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ command->u.connect_on_channel.descriptor.cable_channel_id = data[0];
+ data++;
+ data_length--;
+ break;
+ default:
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unknown connection descriptor %02x\n",
+ command->u.connect_on_channel.descriptor_type);
+ return -1;
+ }
+
+ // parse the last bit
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ command->u.connect_on_channel.retry_count = data[0];
+ command->u.connect_on_channel.timeout = data[1];
+
+ // ok
+ return 0;
+}
+
+static int en50221_app_lowspeed_parse_command(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length < 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ data += length_field_len;
+
+ // get command id
+ uint8_t command_id = data[0];
+ data++;
+ asn_data_length--;
+
+ // parse the command
+ struct en50221_app_lowspeed_command command;
+ switch (command_id) {
+ case COMMS_COMMAND_ID_CONNECT_ON_CHANNEL:
+ if (en50221_app_lowspeed_parse_connect_on_channel
+ (&command, data, asn_data_length)) {
+ return -1;
+ }
+ break;
+ case COMMS_COMMAND_ID_SET_PARAMS:
+ if (asn_data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ command.u.set_params.buffer_size = data[0];
+ command.u.set_params.timeout = data[1];
+ break;
+ case COMMS_COMMAND_ID_GET_NEXT_BUFFER:
+ if (asn_data_length != 1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ command.u.get_next_buffer.phase_id = data[0];
+ break;
+
+ case COMMS_COMMAND_ID_DISCONNECT_ON_CHANNEL:
+ case COMMS_COMMAND_ID_ENQUIRE_STATUS:
+ break;
+
+ default:
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected command_id %02x\n", command_id);
+ return -1;
+ }
+
+ // tell the app
+ pthread_mutex_lock(&lowspeed->lock);
+ en50221_app_lowspeed_command_callback cb = lowspeed->command_callback;
+ void *cb_arg = lowspeed->command_callback_arg;
+ pthread_mutex_unlock(&lowspeed->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, command_id,
+ &command);
+ }
+ return 0;
+}
+
+static int en50221_app_lowspeed_parse_send(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ int more_last,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // skip over the length field
+ data += length_field_len;
+
+ // find previous session
+ pthread_mutex_lock(&lowspeed->lock);
+ struct en50221_app_lowspeed_session *cur_s = lowspeed->sessions;
+ while (cur_s) {
+ if (cur_s->session_number == session_number)
+ break;
+ cur_s = cur_s->next;
+ }
+
+ // more data is still to come
+ if (!more_last) {
+ // if there was no previous session, create one
+ if (cur_s == NULL) {
+ cur_s = malloc(sizeof(struct en50221_app_lowspeed_session));
+ if (cur_s == NULL) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Ran out of memory\n");
+ pthread_mutex_unlock(&lowspeed->lock);
+ return -1;
+ }
+ cur_s->session_number = session_number;
+ cur_s->block_chain = NULL;
+ cur_s->block_length = 0;
+ cur_s->next = lowspeed->sessions;
+ lowspeed->sessions = cur_s;
+ }
+ // append the data
+ uint8_t *new_data = realloc(cur_s->block_chain,
+ cur_s->block_length + asn_data_length);
+ if (new_data == NULL) {
+ print(LOG_LEVEL, ERROR, 1, "Ran out of memory\n");
+ pthread_mutex_unlock(&lowspeed->lock);
+ return -1;
+ }
+ memcpy(new_data + cur_s->block_length, data, asn_data_length);
+ cur_s->block_chain = new_data;
+ cur_s->block_length += asn_data_length;
+
+ // done
+ pthread_mutex_unlock(&lowspeed->lock);
+ return 0;
+ }
+ // we hit the last of a possible chain of fragments
+ int do_free = 0;
+ if (cur_s != NULL) {
+ // we have a preceding fragment - need to append
+ uint8_t *new_data = realloc(cur_s->block_chain,
+ cur_s->block_length + asn_data_length);
+ if (new_data == NULL) {
+ print(LOG_LEVEL, ERROR, 1, "Ran out of memory\n");
+ pthread_mutex_unlock(&lowspeed->lock);
+ return -1;
+ }
+ memcpy(new_data + cur_s->block_length, data, asn_data_length);
+ asn_data_length = cur_s->block_length + asn_data_length;
+ data = new_data;
+ cur_s->block_chain = NULL;
+ cur_s->block_length = 0;
+ do_free = 1;
+ }
+ // check the reassembled data length
+ if (asn_data_length < 1) {
+ pthread_mutex_unlock(&lowspeed->lock);
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ if (do_free)
+ free(data);
+ return -1;
+ }
+ // now, parse the data
+ uint8_t phase_id = data[0];
+
+ // tell the app
+ en50221_app_lowspeed_send_callback cb = lowspeed->send_callback;
+ void *cb_arg = lowspeed->send_callback_arg;
+ pthread_mutex_unlock(&lowspeed->lock);
+ int cbstatus = 0;
+ if (cb) {
+ cbstatus =
+ cb(cb_arg, slot_id, session_number, phase_id, data + 1, asn_data_length - 1);
+ }
+ // done
+ if (do_free)
+ free(data);
+ return cbstatus;
+}
diff --git a/lib/libdvben50221/en50221_app_lowspeed.h b/lib/libdvben50221/en50221_app_lowspeed.h
new file mode 100644
index 0000000..0ef983c
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_lowspeed.h
@@ -0,0 +1,219 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_LOWSPEED_H__
+#define __EN50221_APPLICATION_LOWSPEED_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+#include <libucsi/dvb/descriptor.h>
+
+#define COMMS_COMMAND_ID_CONNECT_ON_CHANNEL 0x01
+#define COMMS_COMMAND_ID_DISCONNECT_ON_CHANNEL 0x02
+#define COMMS_COMMAND_ID_SET_PARAMS 0x03
+#define COMMS_COMMAND_ID_ENQUIRE_STATUS 0x04
+#define COMMS_COMMAND_ID_GET_NEXT_BUFFER 0x05
+
+#define CONNECTION_DESCRIPTOR_TYPE_TELEPHONE 0x01
+#define CONNECTION_DESCRIPTOR_TYPE_CABLE 0x02
+
+#define COMMS_REPLY_ID_CONNECT_ACK 0x01
+#define COMMS_REPLY_ID_DISCONNECT_ACK 0x02
+#define COMMS_REPLY_ID_SET_PARAMS_ACK 0x03
+#define COMMS_REPLY_ID_STATUS_REPLY 0x04
+#define COMMS_REPLY_ID_GET_NEXT_BUFFER_ACK 0x05
+#define COMMS_REPLY_ID_SEND_ACK 0x06
+
+#define EN50221_APP_LOWSPEED_RESOURCEID(DEVICE_TYPE, DEVICE_NUMBER) MKRID(96,((DEVICE_TYPE)<<2)|((DEVICE_NUMBER) & 0x03),1)
+
+
+/**
+ * Structure holding information on a received comms command.
+ */
+struct en50221_app_lowspeed_command {
+ union {
+ struct {
+ uint8_t descriptor_type; // CONNECTION_DESCRIPTOR_TYPE_*
+ uint8_t retry_count;
+ uint8_t timeout;
+ union {
+ struct dvb_telephone_descriptor *telephone;
+ uint8_t cable_channel_id;
+ } descriptor;
+ } connect_on_channel;
+
+ struct {
+ uint8_t buffer_size;
+ uint8_t timeout;
+ } set_params;
+
+ struct {
+ uint8_t phase_id;
+ } get_next_buffer;
+ } u;
+};
+
+/**
+ * Type definition for command - called when we receive a comms command.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param command_id One of the COMMS_COMMAND_ID_* values
+ * @param command Pointer to a lowspeed command structure containing the command data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_lowspeed_command_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t command_id,
+ struct en50221_app_lowspeed_command *command);
+
+/**
+ * Type definition for send - called when we receive data to send. The block can be segmented into
+ * multiple pieces - last_more indicates the details of this.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param phase_id Comms phase id.
+ * @param data The data.
+ * @param length Number of bytes.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_lowspeed_send_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t phase_id,
+ uint8_t *data,
+ uint32_t length);
+
+/**
+ * Opaque type representing a lowspeed resource.
+ */
+struct en50221_app_lowspeed;
+
+/**
+ * Create an instance of the lowspeed resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_lowspeed *
+ en50221_app_lowspeed_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the lowspeed resource.
+ *
+ * @param lowspeed Instance to destroy.
+ */
+extern void en50221_app_lowspeed_destroy(struct en50221_app_lowspeed *lowspeed);
+
+/**
+ * Informs the lowspeed object that a session to it has been closed - cleans up internal state.
+ *
+ * @param lowspeed lowspeed resource instance.
+ * @param session_number The session concerned.
+ */
+extern void en50221_app_lowspeed_clear_session(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number);
+
+/**
+ * Register the callback for when we receive a comms command.
+ *
+ * @param lowspeed lowspeed resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_lowspeed_register_command_callback(struct en50221_app_lowspeed *lowspeed,
+ en50221_app_lowspeed_command_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive data to send.
+ *
+ * @param lowspeed lowspeed resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_lowspeed_register_send_callback(struct en50221_app_lowspeed *lowspeed,
+ en50221_app_lowspeed_send_callback callback,
+ void *arg);
+
+/**
+ * Send a comms reply to the CAM.
+ *
+ * @param lowspeed lowspeed resource instance.
+ * @param session_number Session number to send it on.
+ * @param comms_reply_id One of the COMMS_REPLY_ID_* values.
+ * @param return_value Comms reply specific value.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_lowspeed_send_comms_reply(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number,
+ uint8_t comms_reply_id,
+ uint8_t return_value);
+
+/**
+ * Send received data to the CAM.
+ *
+ * @param lowspeed lowspeed resource instance.
+ * @param session_number Session number to send it on.
+ * @param phase_id Comms phase id.
+ * @param tx_data_length Length of data in bytes (max 254 bytes as per spec).
+ * @param tx_data Data.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_lowspeed_send_comms_data(struct en50221_app_lowspeed *lowspeed,
+ uint16_t session_number,
+ uint8_t phase_id,
+ uint32_t tx_data_length,
+ uint8_t * tx_data);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param lowspeed lowspeed instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_lowspeed_message(struct en50221_app_lowspeed *lowspeed,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_mmi.c b/lib/libdvben50221/en50221_app_mmi.c
new file mode 100644
index 0000000..830eaa3
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_mmi.c
@@ -0,0 +1,1397 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include <libucsi/dvb/types.h>
+#include "en50221_app_mmi.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_mmi_session {
+ uint16_t session_number;
+
+ uint8_t *menu_block_chain;
+ uint32_t menu_block_length;
+
+ uint8_t *list_block_chain;
+ uint32_t list_block_length;
+
+ uint8_t *subtitlesegment_block_chain;
+ uint32_t subtitlesegment_block_length;
+
+ uint8_t *subtitledownload_block_chain;
+ uint32_t subtitledownload_block_length;
+
+ struct en50221_app_mmi_session *next;
+};
+
+struct en50221_app_mmi {
+ struct en50221_app_send_functions *funcs;
+ struct en50221_app_mmi_session *sessions;
+
+ en50221_app_mmi_close_callback closecallback;
+ void *closecallback_arg;
+
+ en50221_app_mmi_display_control_callback displaycontrolcallback;
+ void *displaycontrolcallback_arg;
+
+ en50221_app_mmi_keypad_control_callback keypadcontrolcallback;
+ void *keypadcontrolcallback_arg;
+
+ en50221_app_mmi_subtitle_segment_callback subtitlesegmentcallback;
+ void *subtitlesegmentcallback_arg;
+
+ en50221_app_mmi_scene_end_mark_callback sceneendmarkcallback;
+ void *sceneendmarkcallback_arg;
+
+ en50221_app_mmi_scene_control_callback scenecontrolcallback;
+ void *scenecontrolcallback_arg;
+
+ en50221_app_mmi_subtitle_download_callback subtitledownloadcallback;
+ void *subtitledownloadcallback_arg;
+
+ en50221_app_mmi_flush_download_callback flushdownloadcallback;
+ void *flushdownloadcallback_arg;
+
+ en50221_app_mmi_enq_callback enqcallback;
+ void *enqcallback_arg;
+
+ en50221_app_mmi_menu_callback menucallback;
+ void *menucallback_arg;
+
+ en50221_app_mmi_list_callback listcallback;
+ void *listcallback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_mmi_parse_close(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_display_control(struct en50221_app_mmi
+ *mmi, uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_keypad_control(struct en50221_app_mmi
+ *mmi, uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_enq(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length);
+static int en50221_app_mmi_parse_list_menu(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_subtitle(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_scene_end_mark(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_scene_control(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_subtitle(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_parse_flush_download(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_mmi_defragment(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * indata,
+ uint32_t indata_length,
+ uint8_t ** outdata,
+ uint32_t * outdata_length);
+static int en50221_app_mmi_defragment_text(uint8_t * data,
+ uint32_t data_length,
+ uint8_t ** outdata,
+ uint32_t * outdata_length,
+ uint32_t * outconsumed);
+
+
+
+struct en50221_app_mmi *en50221_app_mmi_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_mmi *mmi = NULL;
+
+ // create structure and set it up
+ mmi = malloc(sizeof(struct en50221_app_mmi));
+ if (mmi == NULL) {
+ return NULL;
+ }
+ mmi->funcs = funcs;
+ mmi->closecallback = NULL;
+ mmi->displaycontrolcallback = NULL;
+ mmi->keypadcontrolcallback = NULL;
+ mmi->subtitlesegmentcallback = NULL;
+ mmi->sceneendmarkcallback = NULL;
+ mmi->scenecontrolcallback = NULL;
+ mmi->subtitledownloadcallback = NULL;
+ mmi->flushdownloadcallback = NULL;
+ mmi->enqcallback = NULL;
+ mmi->menucallback = NULL;
+ mmi->listcallback = NULL;
+ mmi->sessions = NULL;
+
+ pthread_mutex_init(&mmi->lock, NULL);
+
+ // done
+ return mmi;
+}
+
+void en50221_app_mmi_destroy(struct en50221_app_mmi *mmi)
+{
+ struct en50221_app_mmi_session *cur_s = mmi->sessions;
+ while (cur_s) {
+ struct en50221_app_mmi_session *next = cur_s->next;
+ if (cur_s->menu_block_chain)
+ free(cur_s->menu_block_chain);
+ if (cur_s->list_block_chain)
+ free(cur_s->list_block_chain);
+ if (cur_s->subtitlesegment_block_chain)
+ free(cur_s->subtitlesegment_block_chain);
+ if (cur_s->subtitledownload_block_chain)
+ free(cur_s->subtitledownload_block_chain);
+ free(cur_s);
+ cur_s = next;
+ }
+
+ pthread_mutex_destroy(&mmi->lock);
+ free(mmi);
+}
+
+void en50221_app_mmi_clear_session(struct en50221_app_mmi *mmi,
+ uint16_t session_number)
+{
+ pthread_mutex_lock(&mmi->lock);
+ struct en50221_app_mmi_session *cur_s = mmi->sessions;
+ struct en50221_app_mmi_session *prev_s = NULL;
+ while (cur_s) {
+ if (cur_s->session_number == session_number) {
+ if (cur_s->menu_block_chain)
+ free(cur_s->menu_block_chain);
+ if (cur_s->list_block_chain)
+ free(cur_s->list_block_chain);
+ if (cur_s->subtitlesegment_block_chain)
+ free(cur_s->subtitlesegment_block_chain);
+ if (cur_s->subtitledownload_block_chain)
+ free(cur_s->subtitledownload_block_chain);
+ if (prev_s) {
+ prev_s->next = cur_s->next;
+ } else {
+ mmi->sessions = cur_s->next;
+ }
+ free(cur_s);
+ return;
+ }
+
+ prev_s = cur_s;
+ cur_s = cur_s->next;
+ }
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_close_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_close_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->closecallback = callback;
+ mmi->closecallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_display_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_display_control_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->displaycontrolcallback = callback;
+ mmi->displaycontrolcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_keypad_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_keypad_control_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->keypadcontrolcallback = callback;
+ mmi->keypadcontrolcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_subtitle_segment_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_subtitle_segment_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->subtitlesegmentcallback = callback;
+ mmi->subtitlesegmentcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_scene_end_mark_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_scene_end_mark_callback callback, void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->sceneendmarkcallback = callback;
+ mmi->sceneendmarkcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_scene_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_scene_control_callback callback, void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->scenecontrolcallback = callback;
+ mmi->scenecontrolcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_subtitle_download_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_subtitle_download_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->subtitledownloadcallback = callback;
+ mmi->subtitledownloadcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_flush_download_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_flush_download_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->flushdownloadcallback = callback;
+ mmi->flushdownloadcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_enq_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_enq_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->enqcallback = callback;
+ mmi->enqcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_menu_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_menu_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->menucallback = callback;
+ mmi->menucallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+void en50221_app_mmi_register_list_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_list_callback callback,
+ void *arg)
+{
+ pthread_mutex_lock(&mmi->lock);
+ mmi->listcallback = callback;
+ mmi->listcallback_arg = arg;
+ pthread_mutex_unlock(&mmi->lock);
+}
+
+int en50221_app_mmi_close(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay)
+{
+ uint8_t data[6];
+ int data_length = 5;
+
+ data[0] = (TAG_CLOSE_MMI >> 16) & 0xFF;
+ data[1] = (TAG_CLOSE_MMI >> 8) & 0xFF;
+ data[2] = TAG_CLOSE_MMI & 0xFF;
+ data[3] = 1;
+ data[4] = cmd_id;
+ if (cmd_id == MMI_CLOSE_MMI_CMD_ID_DELAY) {
+ data[3] = 2;
+ data[5] = delay;
+ data_length = 6;
+ }
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data,
+ data_length);
+}
+
+int en50221_app_mmi_display_reply(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t reply_id,
+ struct en50221_app_mmi_display_reply_details *details)
+{
+ uint8_t data[32];
+ struct iovec iov[2];
+ uint32_t iov_count;
+ int length_field_len;
+
+ // fill out the start of the header
+ data[0] = (TAG_DISPLAY_REPLY >> 16) & 0xFF;
+ data[1] = (TAG_DISPLAY_REPLY >> 8) & 0xFF;
+ data[2] = TAG_DISPLAY_REPLY & 0xFF;
+
+ switch (reply_id) {
+ case MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK:
+ data[3] = 2;
+ data[4] = reply_id;
+ data[5] = details->u.mode_ack.mmi_mode;
+ iov[0].iov_base = data;
+ iov[0].iov_len = 6;
+ iov_count = 1;
+ break;
+
+ case MMI_DISPLAY_REPLY_ID_LIST_DISPLAY_CHAR_TABLES:
+ case MMI_DISPLAY_REPLY_ID_LIST_INPUT_CHAR_TABLES:
+ if ((length_field_len =
+ asn_1_encode(details->u.char_table.table_length + 1, data + 3, 3)) < 0) {
+ return -1;
+ }
+ data[3 + length_field_len] = reply_id;
+ iov[0].iov_base = data;
+ iov[0].iov_len = 3 + length_field_len + 1;
+ iov[1].iov_base = details->u.char_table.table;
+ iov[1].iov_len = details->u.char_table.table_length;
+ iov_count = 2;
+ break;
+
+ case MMI_DISPLAY_REPLY_ID_LIST_OVERLAY_GFX_CHARACTERISTICS:
+ case MMI_DISPLAY_REPLY_ID_LIST_FULLSCREEN_GFX_CHARACTERISTICS:
+ {
+ if ((length_field_len =
+ asn_1_encode(1 + 9 + (details->u.gfx.num_pixel_depths * 2), data + 3, 3)) < 0) {
+ return -1;
+ }
+ data[3 + length_field_len] = reply_id;
+ data[3 + length_field_len + 1] = details->u.gfx.width >> 8;
+ data[3 + length_field_len + 2] = details->u.gfx.width;
+ data[3 + length_field_len + 3] = details->u.gfx.height >> 8;
+ data[3 + length_field_len + 4] = details->u.gfx.height;
+ data[3 + length_field_len + 5] =
+ ((details->u.gfx.aspect_ratio & 0x0f) << 4) |
+ ((details->u.gfx.gfx_relation_to_video & 0x07) << 1) |
+ (details->u.gfx.multiple_depths & 1);
+ data[3 + length_field_len + 6] = details->u.gfx.display_bytes >> 4;
+ data[3 + length_field_len + 7] =
+ ((details->u.gfx.display_bytes & 0x0f) << 4) |
+ ((details->u.gfx.composition_buffer_bytes & 0xf0) >> 4);
+ data[3 + length_field_len + 8] =
+ ((details->u.gfx.composition_buffer_bytes & 0x0f) << 4) |
+ ((details->u.gfx.object_cache_bytes & 0xf0) >> 4);
+ data[3 + length_field_len + 9] =
+ ((details->u.gfx.object_cache_bytes & 0x0f) << 4) |
+ (details->u.gfx.num_pixel_depths & 0x0f);
+
+ // render the pixel depths themselves
+ uint8_t *pixdepths =
+ alloca(details->u.gfx.num_pixel_depths * 2);
+ if (pixdepths == NULL) {
+ return -1;
+ }
+ uint32_t i;
+ for (i = 0; i < details->u.gfx.num_pixel_depths; i++) {
+ pixdepths[0] =
+ ((details->u.gfx.pixel_depths[i].display_depth & 0x07) << 5) |
+ ((details->u.gfx.pixel_depths[i].pixels_per_byte & 0x07) << 2);
+ pixdepths[1] =
+ details->u.gfx.pixel_depths[i].region_overhead;
+ pixdepths += 2;
+ }
+
+ // make up the iovs
+ iov[0].iov_base = data;
+ iov[0].iov_len = 3 + length_field_len + 10;
+ iov[1].iov_base = pixdepths;
+ iov[1].iov_len =
+ details->u.gfx.num_pixel_depths * 2;
+ iov_count = 2;
+ break;
+ }
+
+ default:
+ data[3] = 1;
+ data[4] = reply_id;
+ iov[0].iov_base = data;
+ iov[0].iov_len = 5;
+ iov_count = 1;
+ break;
+ }
+
+ // sendit
+ return mmi->funcs->send_datav(mmi->funcs->arg, session_number, iov, iov_count);
+}
+
+int en50221_app_mmi_keypress(struct en50221_app_mmi *mmi,
+ uint16_t session_number, uint8_t keycode)
+{
+ uint8_t data[5];
+
+ data[0] = (TAG_KEYPRESS >> 16) & 0xFF;
+ data[1] = (TAG_KEYPRESS >> 8) & 0xFF;
+ data[2] = TAG_KEYPRESS & 0xFF;
+ data[3] = 1;
+ data[4] = keycode;
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data, 5);
+}
+
+int en50221_app_mmi_display_message(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t display_message_id)
+{
+ uint8_t data[5];
+
+ data[0] = (TAG_DISPLAY_MESSAGE >> 16) & 0xFF;
+ data[1] = (TAG_DISPLAY_MESSAGE >> 8) & 0xFF;
+ data[2] = TAG_DISPLAY_MESSAGE & 0xFF;
+ data[3] = 1;
+ data[4] = display_message_id;
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data, 5);
+}
+
+int en50221_app_mmi_scene_done(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t decoder_continue,
+ uint8_t scene_reveal, uint8_t scene_tag)
+{
+ uint8_t data[5];
+
+ data[0] = (TAG_SCENE_DONE >> 16) & 0xFF;
+ data[1] = (TAG_SCENE_DONE >> 8) & 0xFF;
+ data[2] = TAG_SCENE_DONE & 0xFF;
+ data[3] = 1;
+ data[4] =
+ (decoder_continue ? 0x80 : 0x00) |
+ (scene_reveal ? 0x40 : 0x00) |
+ (scene_tag & 0x0f);
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data, 5);
+}
+
+int en50221_app_mmi_download_reply(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint16_t object_id,
+ uint8_t download_reply_id)
+{
+ uint8_t data[7];
+
+ data[0] = (TAG_DOWNLOAD_REPLY >> 16) & 0xFF;
+ data[1] = (TAG_DOWNLOAD_REPLY >> 8) & 0xFF;
+ data[2] = TAG_DOWNLOAD_REPLY & 0xFF;
+ data[3] = 3;
+ data[4] = object_id >> 8;
+ data[5] = object_id;
+ data[6] = download_reply_id;
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data, 7);
+}
+
+int en50221_app_mmi_answ(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t answ_id,
+ uint8_t * text, uint32_t text_count)
+{
+ uint8_t buf[10];
+
+ // set up the tag
+ buf[0] = (TAG_ANSWER >> 16) & 0xFF;
+ buf[1] = (TAG_ANSWER >> 8) & 0xFF;
+ buf[2] = TAG_ANSWER & 0xFF;
+
+ // encode the length field
+ struct iovec iov[2];
+ int length_field_len = 0;
+ int iov_count = 1;
+ if (answ_id == MMI_ANSW_ID_ANSWER) {
+ if ((length_field_len = asn_1_encode(text_count + 1, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ buf[3 + length_field_len] = answ_id;
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len + 1;
+ iov[1].iov_base = text;
+ iov[1].iov_len = text_count;
+ iov_count = 2;
+ } else {
+ buf[3] = 1;
+ buf[4] = answ_id;
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 5;
+ iov_count = 1;
+ }
+
+ // create the data and send it
+ return mmi->funcs->send_datav(mmi->funcs->arg, session_number, iov,
+ iov_count);
+}
+
+int en50221_app_mmi_menu_answ(struct en50221_app_mmi *mmi,
+ uint16_t session_number, uint8_t choice_ref)
+{
+ uint8_t data[5];
+
+ data[0] = (TAG_MENU_ANSWER >> 16) & 0xFF;
+ data[1] = (TAG_MENU_ANSWER >> 8) & 0xFF;
+ data[2] = TAG_MENU_ANSWER & 0xFF;
+ data[3] = 1;
+ data[4] = choice_ref;
+ return mmi->funcs->send_data(mmi->funcs->arg, session_number, data, 5);
+}
+
+int en50221_app_mmi_message(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_CLOSE_MMI:
+ return en50221_app_mmi_parse_close(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_DISPLAY_CONTROL:
+ return en50221_app_mmi_parse_display_control(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_KEYPAD_CONTROL:
+ return en50221_app_mmi_parse_keypad_control(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_ENQUIRY:
+ return en50221_app_mmi_parse_enq(mmi, slot_id,
+ session_number, data + 3,
+ data_length - 3);
+ case TAG_MENU_LAST:
+ return en50221_app_mmi_parse_list_menu(mmi, slot_id,
+ session_number, tag,
+ 1, data + 3,
+ data_length - 3);
+ case TAG_MENU_MORE:
+ return en50221_app_mmi_parse_list_menu(mmi, slot_id,
+ session_number, tag,
+ 0, data + 3,
+ data_length - 3);
+ case TAG_LIST_LAST:
+ return en50221_app_mmi_parse_list_menu(mmi, slot_id,
+ session_number, tag,
+ 1, data + 3,
+ data_length - 3);
+ case TAG_LIST_MORE:
+ return en50221_app_mmi_parse_list_menu(mmi, slot_id,
+ session_number, tag,
+ 0, data + 3,
+ data_length - 3);
+ case TAG_SUBTITLE_SEGMENT_LAST:
+ return en50221_app_mmi_parse_subtitle(mmi, slot_id,
+ session_number, tag,
+ 1, data + 3,
+ data_length - 3);
+ case TAG_SUBTITLE_SEGMENT_MORE:
+ return en50221_app_mmi_parse_subtitle(mmi, slot_id,
+ session_number, tag,
+ 0, data + 3,
+ data_length - 3);
+ case TAG_SCENE_END_MARK:
+ return en50221_app_mmi_parse_scene_end_mark(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_SCENE_CONTROL:
+ return en50221_app_mmi_parse_scene_control(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_SUBTITLE_DOWNLOAD_LAST:
+ return en50221_app_mmi_parse_subtitle(mmi, slot_id,
+ session_number, tag,
+ 1, data + 3,
+ data_length - 3);
+ case TAG_SUBTITLE_DOWNLOAD_MORE:
+ return en50221_app_mmi_parse_subtitle(mmi, slot_id,
+ session_number, tag,
+ 0, data + 3,
+ data_length - 3);
+ case TAG_FLUSH_DOWNLOAD:
+ return en50221_app_mmi_parse_flush_download(mmi, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+
+
+static int en50221_app_mmi_parse_close(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] > (data_length - 1)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t cmd_id = data[1];
+ uint8_t delay = 0;
+ if (cmd_id == MMI_CLOSE_MMI_CMD_ID_DELAY) {
+ if (data[0] != 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ delay = data[2];
+ }
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_close_callback cb = mmi->closecallback;
+ void *cb_arg = mmi->closecallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, cmd_id, delay);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_display_control(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] > (data_length - 1)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t cmd_id = data[1];
+ uint8_t mmi_mode = 0;
+ if (cmd_id == MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE) {
+ if (data[0] != 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received short data\n");
+ return -1;
+ }
+ mmi_mode = data[2];
+ }
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_display_control_callback cb = mmi->displaycontrolcallback;
+ void *cb_arg = mmi->displaycontrolcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, cmd_id, mmi_mode);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_keypad_control(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length < 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // skip over the length field
+ data += length_field_len;
+
+ // extract the information
+ uint8_t cmd_id = data[0];
+ uint8_t *keycodes = data + 1;
+
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_keypad_control_callback cb = mmi->keypadcontrolcallback;
+ void *cb_arg = mmi->keypadcontrolcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, cmd_id,
+ keycodes, asn_data_length - 1);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_enq(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data, uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length < 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // skip over the length field
+ data += length_field_len;
+
+ // extract the information
+ uint8_t blind_answer = (data[0] & 0x01) ? 1 : 0;
+ uint8_t answer_length = data[1];
+ uint8_t *text = data + 2;
+
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_enq_callback cb = mmi->enqcallback;
+ void *cb_arg = mmi->enqcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, blind_answer,
+ answer_length, text, asn_data_length - 2);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_list_menu(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ int result = 0;
+ uint8_t *text_flags = NULL;
+ struct en50221_app_mmi_text *text_data = NULL;
+ uint32_t i;
+ uint8_t text_count = 0;
+
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // skip over the length field
+ data += length_field_len;
+
+ // defragment
+ pthread_mutex_lock(&mmi->lock);
+ uint8_t *outdata;
+ uint32_t outdata_length;
+ int dfstatus =
+ en50221_app_mmi_defragment(mmi, session_number, tag_id,
+ more_last,
+ data, asn_data_length,
+ &outdata, &outdata_length);
+ if (dfstatus <= 0) {
+ pthread_mutex_unlock(&mmi->lock);
+ return dfstatus;
+ }
+ data = outdata;
+ data_length = outdata_length;
+
+ // check the reassembled data length
+ if (data_length < 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ pthread_mutex_unlock(&mmi->lock);
+ result = -1;
+ goto exit_cleanup;
+ }
+ // now, parse the data
+ uint8_t choice_nb = data[0];
+ text_count = choice_nb + 3;
+ if (choice_nb == 0xff)
+ text_count = 3;
+ data++;
+ data_length--;
+
+ // variables for extracted text state
+ text_flags = alloca(text_count);
+ if (text_flags == NULL) {
+ pthread_mutex_unlock(&mmi->lock);
+ result = -1;
+ goto exit_cleanup;
+ }
+ memset(text_flags, 0, text_count);
+ text_data = (struct en50221_app_mmi_text *)
+ alloca(sizeof(struct en50221_app_mmi_text) * text_count);
+ if (text_data == NULL) {
+ pthread_mutex_unlock(&mmi->lock);
+ result = -1;
+ goto exit_cleanup;
+ }
+ memset(text_data, 0,
+ sizeof(struct en50221_app_mmi_text) * text_count);
+
+ // extract the text!
+ for (i = 0; i < text_count; i++) {
+ uint32_t consumed = 0;
+ int cur_status =
+ en50221_app_mmi_defragment_text(data, data_length,
+ &text_data[i].text,
+ &text_data[i].text_length,
+ &consumed);
+ if (cur_status < 0) {
+ pthread_mutex_unlock(&mmi->lock);
+ result = -1;
+ goto exit_cleanup;
+ }
+
+ text_flags[i] = cur_status;
+ data += consumed;
+ data_length -= consumed;
+ }
+
+ // work out what to pass to the user
+ struct en50221_app_mmi_text *text_data_for_user = (struct en50221_app_mmi_text *)
+ alloca(sizeof(struct en50221_app_mmi_text) * text_count);
+ if (text_data_for_user == NULL) {
+ result = -1;
+ goto exit_cleanup;
+ }
+ memcpy(text_data_for_user, text_data,
+ sizeof(struct en50221_app_mmi_text) * text_count);
+ struct en50221_app_mmi_text *text_ptr = NULL;
+ if (text_count > 3) {
+ text_ptr = &text_data_for_user[3];
+ }
+ uint8_t *items_raw = NULL;
+ uint32_t items_raw_length = 0;
+ if (choice_nb == 0xff) {
+ items_raw = data;
+ items_raw_length = data_length;
+ }
+ // do callback
+ result = 0;
+ switch (tag_id) {
+ case TAG_MENU_LAST:
+ {
+ en50221_app_mmi_menu_callback cb = mmi->menucallback;
+ void *cb_arg = mmi->menucallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ result =
+ cb(cb_arg, slot_id, session_number,
+ &text_data_for_user[0],
+ &text_data_for_user[1],
+ &text_data_for_user[2],
+ text_count - 3, text_ptr,
+ items_raw_length, items_raw);
+ }
+ break;
+ }
+
+ case TAG_LIST_LAST:
+ {
+ en50221_app_mmi_list_callback cb = mmi->listcallback;
+ void *cb_arg = mmi->listcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ result =
+ cb(cb_arg, slot_id, session_number,
+ &text_data_for_user[0],
+ &text_data_for_user[1],
+ &text_data_for_user[2],
+ text_count - 3, text_ptr,
+ items_raw_length, items_raw);
+ }
+ break;
+ }
+
+ default:
+ pthread_mutex_unlock(&mmi->lock);
+ break;
+ }
+
+exit_cleanup:
+ if ((dfstatus == 2) && outdata)
+ free(outdata);
+ if (text_flags && text_data) {
+ for (i = 0; i < text_count; i++) {
+ if ((text_flags[i] == 2) && text_data[i].text) {
+ free(text_data[i].text);
+ }
+ }
+ }
+ return result;
+}
+
+static int en50221_app_mmi_parse_subtitle(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t tag_id, int more_last,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // skip over the length field
+ data += length_field_len;
+
+ // defragment
+ pthread_mutex_lock(&mmi->lock);
+ uint8_t *outdata;
+ uint32_t outdata_length;
+ int dfstatus =
+ en50221_app_mmi_defragment(mmi, session_number, tag_id,
+ more_last,
+ data, asn_data_length,
+ &outdata, &outdata_length);
+ if (dfstatus <= 0) {
+ pthread_mutex_unlock(&mmi->lock);
+ return dfstatus;
+ }
+ // do callback
+ int cbstatus = 0;
+ switch (tag_id) {
+ case TAG_SUBTITLE_SEGMENT_LAST:
+ {
+ en50221_app_mmi_subtitle_segment_callback cb =
+ mmi->subtitlesegmentcallback;
+ void *cb_arg = mmi->subtitlesegmentcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ cbstatus =
+ cb(cb_arg, slot_id, session_number, outdata, outdata_length);
+ }
+ break;
+ }
+
+ case TAG_SUBTITLE_DOWNLOAD_LAST:
+ {
+ en50221_app_mmi_subtitle_download_callback cb =
+ mmi->subtitledownloadcallback;
+ void *cb_arg = mmi->subtitledownloadcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ cbstatus =
+ cb(cb_arg, slot_id, session_number, outdata, outdata_length);
+ }
+ break;
+ }
+ }
+
+ // free the data returned by the defragment call if asked to
+ if (dfstatus == 2) {
+ free(outdata);
+ }
+ // done
+ return cbstatus;
+}
+
+static int en50221_app_mmi_parse_scene_end_mark(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t flags = data[1];
+
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_scene_end_mark_callback cb =
+ mmi->sceneendmarkcallback;
+ void *cb_arg = mmi->sceneendmarkcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ (flags & 0x80) ? 1 : 0,
+ (flags & 0x40) ? 1 : 0,
+ (flags & 0x20) ? 1 : 0, flags & 0x0f);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_scene_control(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t flags = data[1];
+
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_scene_control_callback cb = mmi->scenecontrolcallback;
+ void *cb_arg = mmi->scenecontrolcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number,
+ (flags & 0x80) ? 1 : 0,
+ (flags & 0x40) ? 1 : 0, flags & 0x0f);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_parse_flush_download(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ // validate data
+ if (data_length != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 0) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ // tell the app
+ pthread_mutex_lock(&mmi->lock);
+ en50221_app_mmi_flush_download_callback cb = mmi->flushdownloadcallback;
+ void *cb_arg = mmi->flushdownloadcallback_arg;
+ pthread_mutex_unlock(&mmi->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number);
+ }
+ return 0;
+}
+
+static int en50221_app_mmi_defragment(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint32_t tag_id,
+ int more_last,
+ uint8_t * indata,
+ uint32_t indata_length,
+ uint8_t ** outdata,
+ uint32_t * outdata_length)
+{
+ struct en50221_app_mmi_session *cur_s = mmi->sessions;
+ while (cur_s) {
+ if (cur_s->session_number == session_number)
+ break;
+ cur_s = cur_s->next;
+ }
+
+ // more data is still to come
+ if (!more_last) {
+ // if there was no previous session, create one
+ if (cur_s == NULL) {
+ cur_s = malloc(sizeof(struct en50221_app_mmi_session));
+ if (cur_s == NULL) {
+ print(LOG_LEVEL, ERROR, 1, "Ran out of memory\n");
+ return -1;
+ }
+ cur_s->session_number = session_number;
+ cur_s->menu_block_chain = NULL;
+ cur_s->menu_block_length = 0;
+ cur_s->list_block_chain = NULL;
+ cur_s->list_block_length = 0;
+ cur_s->subtitlesegment_block_chain = NULL;
+ cur_s->subtitlesegment_block_length = 0;
+ cur_s->subtitledownload_block_chain = NULL;
+ cur_s->subtitledownload_block_length = 0;
+ cur_s->next = mmi->sessions;
+ mmi->sessions = cur_s;
+ }
+ // find the block/block_length to use
+ uint8_t **block_chain;
+ uint32_t *block_length;
+ switch (tag_id) {
+ case TAG_MENU_LAST:
+ case TAG_MENU_MORE:
+ block_chain = &cur_s->menu_block_chain;
+ block_length = &cur_s->menu_block_length;
+ break;
+ case TAG_LIST_LAST:
+ case TAG_LIST_MORE:
+ block_chain = &cur_s->list_block_chain;
+ block_length = &cur_s->list_block_length;
+ break;
+ case TAG_SUBTITLE_SEGMENT_LAST:
+ case TAG_SUBTITLE_SEGMENT_MORE:
+ block_chain = &cur_s->subtitlesegment_block_chain;
+ block_length = &cur_s->subtitlesegment_block_length;
+ break;
+ case TAG_SUBTITLE_DOWNLOAD_LAST:
+ case TAG_SUBTITLE_DOWNLOAD_MORE:
+ block_chain = &cur_s->subtitledownload_block_chain;
+ block_length = &cur_s->subtitledownload_block_length;
+ break;
+ default:
+ return -1;
+ }
+
+ // append the data
+ uint8_t *new_data =
+ realloc(*block_chain, *block_length + indata_length);
+ if (new_data == NULL) {
+ print(LOG_LEVEL, ERROR, 1, "Ran out of memory\n");
+ return -1;
+ }
+ memcpy(new_data + *block_length, indata, indata_length);
+ *block_chain = new_data;
+ *block_length += indata_length;
+
+ // success, but block not complete yet
+ return 0;
+ }
+ // we hit the last of a possible chain of fragments
+ if (cur_s != NULL) {
+ // find the block/block_length to use
+ uint8_t **block_chain;
+ uint32_t *block_length;
+ switch (tag_id) {
+ case TAG_MENU_LAST:
+ case TAG_MENU_MORE:
+ block_chain = &cur_s->menu_block_chain;
+ block_length = &cur_s->menu_block_length;
+ break;
+ case TAG_LIST_LAST:
+ case TAG_LIST_MORE:
+ block_chain = &cur_s->list_block_chain;
+ block_length = &cur_s->list_block_length;
+ break;
+ case TAG_SUBTITLE_SEGMENT_LAST:
+ case TAG_SUBTITLE_SEGMENT_MORE:
+ block_chain = &cur_s->subtitlesegment_block_chain;
+ block_length = &cur_s->subtitlesegment_block_length;
+ break;
+ case TAG_SUBTITLE_DOWNLOAD_LAST:
+ case TAG_SUBTITLE_DOWNLOAD_MORE:
+ block_chain = &cur_s->subtitledownload_block_chain;
+ block_length = &cur_s->subtitledownload_block_length;
+ break;
+ default:
+ return -1;
+ }
+
+ // we have a preceding fragment - need to append
+ uint8_t *new_data =
+ realloc(*block_chain, *block_length + indata_length);
+ if (new_data == NULL) {
+ print(LOG_LEVEL, ERROR, 1, "Ran out of memory\n");
+ return -1;
+ }
+ memcpy(new_data + *block_length, indata, indata_length);
+ *outdata_length = *block_length + indata_length;
+ *outdata = new_data;
+ *block_chain = NULL;
+ *block_length = 0;
+
+ // success, and indicate to free the block when done
+ return 2;
+ }
+ // success, but indicate it is not to be freed
+ *outdata_length = indata_length;
+ *outdata = indata;
+ return 1;
+}
+
+static int en50221_app_mmi_defragment_text(uint8_t * data,
+ uint32_t data_length,
+ uint8_t ** outdata,
+ uint32_t * outdata_length,
+ uint32_t * outconsumed)
+{
+ uint8_t *text = NULL;
+ uint32_t text_length = 0;
+ uint32_t consumed = 0;
+
+ while (1) {
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Short data\n");
+ if (text)
+ free(text);
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+ data += 3;
+ data_length -= 3;
+ consumed += 3;
+
+ // get the length of the data and adjust
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len =
+ asn_1_decode(&asn_data_length, data,
+ data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ if (text)
+ free(text);
+ return -1;
+ }
+ data += length_field_len;
+ data_length -= length_field_len;
+ consumed += length_field_len;
+
+ // deal with the tags
+ if (tag == TAG_TEXT_LAST) {
+ if (text == NULL) {
+ *outdata = data;
+ *outdata_length = asn_data_length;
+ *outconsumed = consumed + asn_data_length;
+ return 1;
+ } else {
+ // append the data
+ uint8_t *new_text = realloc(text,
+ text_length + asn_data_length);
+ if (new_text == NULL) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Ran out of memory\n");
+ if (text)
+ free(text);
+ return -1;
+ }
+ memcpy(new_text + text_length, data,
+ asn_data_length);
+ *outdata = new_text;
+ *outdata_length =
+ text_length + asn_data_length;
+ *outconsumed = consumed + asn_data_length;
+ return 2;
+ }
+
+ } else if (tag == TAG_TEXT_MORE) {
+ // append the data
+ uint8_t *new_text =
+ realloc(text, text_length + asn_data_length);
+ if (new_text == NULL) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Ran out of memory\n");
+ if (text)
+ free(text);
+ return -1;
+ }
+ memcpy(new_text + text_length, data,
+ asn_data_length);
+ text = new_text;
+ text_length += asn_data_length;
+
+ // consume the data
+ data += asn_data_length;
+ data_length -= asn_data_length;
+ consumed += asn_data_length;
+ } else {
+ // unknown tag
+ print(LOG_LEVEL, ERROR, 1,
+ "Unknown MMI text tag\n");
+ if (text)
+ free(text);
+ return -1;
+ }
+ }
+}
diff --git a/lib/libdvben50221/en50221_app_mmi.h b/lib/libdvben50221/en50221_app_mmi.h
new file mode 100644
index 0000000..5c5b727
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_mmi.h
@@ -0,0 +1,618 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_mmi_H__
+#define __EN50221_APPLICATION_mmi_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_MMI_RESOURCEID MKRID(64,1,1)
+
+#define MMI_CLOSE_MMI_CMD_ID_IMMEDIATE 0x00
+#define MMI_CLOSE_MMI_CMD_ID_DELAY 0x01
+
+#define MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE 0x01
+#define MMI_DISPLAY_CONTROL_CMD_ID_GET_DISPLAY_CHAR_TABLES 0x02
+#define MMI_DISPLAY_CONTROL_CMD_ID_GET_INPUT_CHAR_TABLES 0x03
+#define MMI_DISPLAY_CONTROL_CMD_ID_GET_OVERLAY_GFX_CHARACTERISTICS 0x04
+#define MMI_DISPLAY_CONTROL_CMD_ID_GET_FULLSCREEN_GFX_CHARACTERISTICS 0x05
+
+#define MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK 0x01
+#define MMI_DISPLAY_REPLY_ID_LIST_DISPLAY_CHAR_TABLES 0x02
+#define MMI_DISPLAY_REPLY_ID_LIST_INPUT_CHAR_TABLES 0x03
+#define MMI_DISPLAY_REPLY_ID_LIST_OVERLAY_GFX_CHARACTERISTICS 0x04
+#define MMI_DISPLAY_REPLY_ID_LIST_FULLSCREEN_GFX_CHARACTERISTICS 0x05
+#define MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID 0xF0
+#define MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE 0xF1
+#define MMI_DISPLAY_REPLY_ID_UNKNOWN_CHAR_TABLE 0xF2
+
+#define MMI_MODE_HIGH_LEVEL 0x01
+#define MMI_MODE_LOW_LEVEL_OVERLAY_GFX 0x02
+#define MMI_MODE_LOW_LEVEL_FULLSCREEN_GFX 0x03
+
+#define MMI_KEYPAD_CONTROL_CMD_ID_INTERCEPT_ALL 0x01
+#define MMI_KEYPAD_CONTROL_CMD_ID_IGNORE_ALL 0x02
+#define MMI_KEYPAD_CONTROL_CMD_ID_INTERCEPT_SELECTED 0x03
+#define MMI_KEYPAD_CONTROL_CMD_ID_IGNORE_SELECTED 0x04
+#define MMI_KEYPAD_CONTROL_CMD_ID_REJECT_KEYPRESS 0x05
+
+#define MMI_GFX_VIDEO_RELATION_NONE 0x00
+#define MMI_GFX_VIDEO_RELATION_MATCHES_EXACTLY 0x07
+
+#define MMI_DISPLAY_MESSAGE_ID_OK 0x00
+#define MMI_DISPLAY_MESSAGE_ID_ERROR 0x01
+#define MMI_DISPLAY_MESSAGE_ID_OUT_OF_MEMORY 0x02
+#define MMI_DISPLAY_MESSAGE_ID_SUBTITLE_SYNTAX_ERROR 0x03
+#define MMI_DISPLAY_MESSAGE_ID_UNDEFINED_REGION 0x04
+#define MMI_DISPLAY_MESSAGE_ID_UNDEFINED_CLUT 0x05
+#define MMI_DISPLAY_MESSAGE_ID_UNDEFINED_OBJECT 0x06
+#define MMI_DISPLAY_MESSAGE_ID_INCOMPATABLE_OBJECT 0x07
+#define MMI_DISPLAY_MESSAGE_ID_UNKNOWN_CHARACTER 0x08
+#define MMI_DISPLAY_MESSAGE_ID_DISPLAY_CHANGED 0x09
+
+#define MMI_DOWNLOAD_REPLY_ID_OK 0x00
+#define MMI_DOWNLOAD_REPLY_ID_NOT_OBJECT_SEGMENT 0x01
+#define MMI_DOWNLOAD_REPLY_ID_OUT_OF_MEMORY 0x02
+
+#define MMI_ANSW_ID_CANCEL 0x00
+#define MMI_ANSW_ID_ANSWER 0x01
+
+/**
+ * A pixel depth as supplied with display_reply details
+ */
+struct en50221_app_mmi_pixel_depth {
+ uint8_t display_depth;
+ uint8_t pixels_per_byte;
+ uint8_t region_overhead;
+};
+
+/**
+ * Details returned with a display_reply
+ */
+struct en50221_app_mmi_display_reply_details {
+ union {
+ struct {
+ uint16_t width;
+ uint16_t height;
+ uint8_t aspect_ratio;
+ uint8_t gfx_relation_to_video; /* one of MMI_GFX_VIDEO_RELATION_* */
+ uint8_t multiple_depths;
+ uint16_t display_bytes;
+ uint8_t composition_buffer_bytes;
+ uint8_t object_cache_bytes;
+ uint8_t num_pixel_depths;
+ struct en50221_app_mmi_pixel_depth *pixel_depths;
+ } gfx; /* MMI_DISPLAY_REPLY_ID_LIST_OVERLAY_GFX_CHARACTERISTICS or
+ MMI_DISPLAY_REPLY_ID_LIST_FULLSCREEN_GFX_CHARACTERISTICS */
+
+ struct {
+ uint32_t table_length;
+ uint8_t *table;
+ } char_table; /* MMI_DISPLAY_REPLY_ID_LIST_DISPLAY_CHAR_TABLES or
+ MMI_DISPLAY_REPLY_ID_LIST_INPUT_CHAR_TABLES */
+
+ struct {
+ uint8_t mmi_mode; /* one of the MMI_MODE_* values */
+ } mode_ack; /* for MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK */
+ } u;
+};
+
+/**
+ * Pointer to a text string.
+ */
+struct en50221_app_mmi_text {
+ uint8_t *text;
+ uint32_t text_length;
+};
+
+/**
+ * Type definition for close - called when we receive an mmi_close from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param cmd_id One of the MMI_CLOSE_MMI_CMD_ID_* values.
+ * @param delay Delay supplied with MMI_CLOSE_MMI_CMD_ID_DELAY.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_close_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t cmd_id,
+ uint8_t delay);
+
+/**
+ * Type definition for display_control callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param cmd_id One of the MMI_DISPLAY_CONTROL_CMD_ID_* values.
+ * @param delay One of the MMI_MODE_* values.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_display_control_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t cmd_id,
+ uint8_t mmi_mode);
+
+/**
+ * Type definition for keypad_control callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param cmd_id One of the MMI_KEYPAD_CONTROL_CMD_ID_* values.
+ * @param key_codes Pointer to the key codes.
+ * @param key_codes_count Number of key codes.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_keypad_control_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t cmd_id,
+ uint8_t *key_codes,
+ uint32_t key_codes_count);
+
+/**
+ * Type definition for subtitle_segment callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param segment Pointer to the segment data.
+ * @param segment_size Size of segment data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_subtitle_segment_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *segment,
+ uint32_t segment_size);
+
+/**
+ * Type definition for scene_end_mark callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param decoder_continue_flag
+ * @param scene_reveal_flag
+ * @param send_scene_done
+ * @param scene_tag
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_scene_end_mark_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t decoder_continue_flag,
+ uint8_t scene_reveal_flag,
+ uint8_t send_scene_done,
+ uint8_t scene_tag);
+
+/**
+ * Type definition for scene_control callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param decoder_continue_flag
+ * @param scene_reveal_flag
+ * @param scene_tag
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_scene_control_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t decoder_continue_flag,
+ uint8_t scene_reveal_flag,
+ uint8_t scene_tag);
+
+/**
+ * Type definition for subtitle_download callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param segment Pointer to the segment data.
+ * @param segment_size Size of segment data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_subtitle_download_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *segment,
+ uint32_t segment_size);
+
+/**
+ * Type definition for flush_download callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_flush_download_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number);
+
+/**
+ * Type definition for enq callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param blind_answer 1=>Obscure text input in some manner,
+ * @param expected_answer_length Expected max number of characters to be returned.
+ * @param text Pointer to the text data.
+ * @param text_size Size of text data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_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);
+
+/**
+ * Type definition for menu callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param title Title text.
+ * @param sub_title Sub-Title text.
+ * @param bottom Bottom text.
+ * @param item_count Number of text elements in items.
+ * @param items Pointer to array of en50221_app_mmi_text structures which are standard menu choices,
+ * @param item_raw_length Length of item raw data.
+ * @param items_raw If nonstandard items were supplied, pointer to their data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_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);
+
+/**
+ * Type definition for list callback.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param title Title text.
+ * @param sub_title Sub-Title text.
+ * @param bottom Bottom text.
+ * @param item_count Number of text elements in items.
+ * @param items Pointer to array of en50221_app_mmi_text structures which are standard menu choices,
+ * @param item_raw_length Length of item raw data.
+ * @param items_raw If nonstandard items were supplied, pointer to their data.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_mmi_list_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);
+
+/**
+ * Opaque type representing a mmi resource.
+ */
+struct en50221_app_mmi;
+
+/**
+ * Create an instance of the mmi resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_mmi *en50221_app_mmi_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the mmi resource.
+ *
+ * @param mmi Instance to destroy.
+ */
+extern void en50221_app_mmi_destroy(struct en50221_app_mmi *mmi);
+
+/**
+ * Informs the mmi object that a session to it has been closed - cleans up internal state.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number The session concerned.
+ */
+extern void en50221_app_mmi_clear_session(struct en50221_app_mmi *mmi,
+ uint16_t session_number);
+
+/**
+ * Register the callback for when we receive an mmi_close request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_close_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_close_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a display control request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_display_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_display_control_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a keypad control request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_keypad_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_keypad_control_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a subtitle segment request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_subtitle_segment_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_subtitle_segment_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a scene end mark request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_scene_end_mark_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_scene_end_mark_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a scene control request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_scene_control_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_scene_control_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a subtitle download request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_subtitle_download_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_subtitle_download_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a flush download request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_flush_download_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_flush_download_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive an enq request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_enq_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_enq_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a menu request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_menu_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_menu_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a list request.
+ *
+ * @param mmi mmi resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_mmi_register_list_callback(struct en50221_app_mmi *mmi,
+ en50221_app_mmi_list_callback callback,
+ void *arg);
+
+/**
+ * Send an mmi_close to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param cmd_id One of the MMI_CLOSE_MMI_CMD_ID_* values.
+ * @param delay Delay to use if MMI_CLOSE_MMI_CMD_ID_DELAY specified.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_close(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t cmd_id, uint8_t delay);
+
+/**
+ * Send a display_reply to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param reply_id One of the MMI_DISPLAY_REPLY_ID_* values.
+ * @param details The details of the reply - can be NULL if the chosen reply_id does not need it.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_display_reply(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t reply_id,
+ struct en50221_app_mmi_display_reply_details *details);
+
+/**
+ * Send a keypress to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param keycode The keycode.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_keypress(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t keycode);
+
+/**
+ * Send a display message to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param display_message_id One of the MMI_DISPLAY_MESSAGE_ID_* values.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_display_message(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t display_message_id);
+
+/**
+ * Send a scene done message to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param decoder_continue Copy of flag in scene_end_mark.
+ * @param scene_reveal Copy of flag in scene_end_mark.
+ * @param scene_tag Scene tag this responds to.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_scene_done(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t decoder_continue,
+ uint8_t scene_reveal,
+ uint8_t scene_tag);
+
+/**
+ * Send a download reply to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param object_id Object id.
+ * @param download_reply_id One of the MMI_DOWNLOAD_REPLY_ID_* values.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_download_reply(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint16_t object_id,
+ uint8_t download_reply_id);
+
+/**
+ * Send an answ to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param answ_id One of the MMI_ANSW_ID_* values.
+ * @param text The text if MMI_ANSW_ID_ANSWER.
+ * @param text_count Length of text.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_answ(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t answ_id,
+ uint8_t * text,
+ uint32_t text_count);
+
+/**
+ * Send a menu answ to the cam.
+ *
+ * @param mmi mmi resource instance.
+ * @param session_number Session number to send it on.
+ * @param choice_ref Option chosen by user (0=>canceled).
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_menu_answ(struct en50221_app_mmi *mmi,
+ uint16_t session_number,
+ uint8_t choice_ref);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param mmi mmi instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_mmi_message(struct en50221_app_mmi *mmi,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_rm.c b/lib/libdvben50221/en50221_app_rm.c
new file mode 100644
index 0000000..7a5bc2f
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_rm.c
@@ -0,0 +1,307 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include <libucsi/endianops.h>
+#include "en50221_app_rm.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_rm {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_rm_enq_callback enqcallback;
+ void *enqcallback_arg;
+
+ en50221_app_rm_reply_callback replycallback;
+ void *replycallback_arg;
+
+ en50221_app_rm_changed_callback changedcallback;
+ void *changedcallback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_rm_parse_profile_enq(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_rm_parse_profile_reply(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_app_rm_parse_profile_change(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+struct en50221_app_rm *en50221_app_rm_create(struct
+ en50221_app_send_functions
+ *funcs)
+{
+ struct en50221_app_rm *rm = NULL;
+
+ // create structure and set it up
+ rm = malloc(sizeof(struct en50221_app_rm));
+ if (rm == NULL) {
+ return NULL;
+ }
+ rm->funcs = funcs;
+ rm->enqcallback = NULL;
+ rm->replycallback = NULL;
+ rm->changedcallback = NULL;
+
+ pthread_mutex_init(&rm->lock, NULL);
+
+ // done
+ return rm;
+}
+
+void en50221_app_rm_destroy(struct en50221_app_rm *rm)
+{
+ pthread_mutex_destroy(&rm->lock);
+ free(rm);
+}
+
+void en50221_app_rm_register_enq_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_enq_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&rm->lock);
+ rm->enqcallback = callback;
+ rm->enqcallback_arg = arg;
+ pthread_mutex_unlock(&rm->lock);
+}
+
+void en50221_app_rm_register_reply_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_reply_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&rm->lock);
+ rm->replycallback = callback;
+ rm->replycallback_arg = arg;
+ pthread_mutex_unlock(&rm->lock);
+}
+
+void en50221_app_rm_register_changed_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_changed_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&rm->lock);
+ rm->changedcallback = callback;
+ rm->changedcallback_arg = arg;
+ pthread_mutex_unlock(&rm->lock);
+}
+
+int en50221_app_rm_enq(struct en50221_app_rm *rm, uint16_t session_number)
+{
+ uint8_t buf[4];
+
+ // set up the tag
+ buf[0] = (TAG_PROFILE_ENQUIRY >> 16) & 0xFF;
+ buf[1] = (TAG_PROFILE_ENQUIRY >> 8) & 0xFF;
+ buf[2] = TAG_PROFILE_ENQUIRY & 0xFF;
+ buf[3] = 0;
+
+ // create the data and send it
+ return rm->funcs->send_data(rm->funcs->arg, session_number, buf, 4);
+}
+
+int en50221_app_rm_reply(struct en50221_app_rm *rm,
+ uint16_t session_number,
+ uint32_t resource_id_count,
+ uint32_t * resource_ids)
+{
+ uint8_t buf[10];
+
+ // set up the tag
+ buf[0] = (TAG_PROFILE >> 16) & 0xFF;
+ buf[1] = (TAG_PROFILE >> 8) & 0xFF;
+ buf[2] = TAG_PROFILE & 0xFF;
+
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(resource_id_count * 4, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ // copy the data and byteswap it
+ uint32_t *copy_resource_ids = alloca(4 * resource_id_count);
+ if (copy_resource_ids == NULL) {
+ return -1;
+ }
+ uint8_t *data = (uint8_t *) copy_resource_ids;
+ memcpy(data, resource_ids, resource_id_count * 4);
+ uint32_t i;
+ for (i = 0; i < resource_id_count; i++) {
+ bswap32(data);
+ data += 4;
+ }
+
+ // build the iovecs
+ struct iovec iov[2];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len;
+ iov[1].iov_base = (uint8_t *) copy_resource_ids;
+ iov[1].iov_len = resource_id_count * 4;
+
+ // create the data and send it
+ return rm->funcs->send_datav(rm->funcs->arg, session_number, iov, 2);
+}
+
+int en50221_app_rm_changed(struct en50221_app_rm *rm,
+ uint16_t session_number)
+{
+ uint8_t buf[4];
+
+ // set up the tag
+ buf[0] = (TAG_PROFILE_CHANGE >> 16) & 0xFF;
+ buf[1] = (TAG_PROFILE_CHANGE >> 8) & 0xFF;
+ buf[2] = TAG_PROFILE_CHANGE & 0xFF;
+ buf[3] = 0;
+
+ // create the data and send it
+ return rm->funcs->send_data(rm->funcs->arg, session_number, buf, 4);
+}
+
+int en50221_app_rm_message(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ // dispatch it
+ switch (tag) {
+ case TAG_PROFILE_ENQUIRY:
+ return en50221_app_rm_parse_profile_enq(rm, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_PROFILE:
+ return en50221_app_rm_parse_profile_reply(rm, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_PROFILE_CHANGE:
+ return en50221_app_rm_parse_profile_change(rm, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+static int en50221_app_rm_parse_profile_enq(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ (void) data;
+ (void) data_length;
+
+ pthread_mutex_lock(&rm->lock);
+ en50221_app_rm_enq_callback cb = rm->enqcallback;
+ void *cb_arg = rm->enqcallback_arg;
+ pthread_mutex_unlock(&rm->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number);
+ }
+ return 0;
+}
+
+static int en50221_app_rm_parse_profile_reply(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t resources_count = asn_data_length / 4;
+ uint32_t *resource_ids = (uint32_t *) (data + length_field_len);
+ data += length_field_len;
+
+ // byteswap it
+ uint32_t i;
+ for (i = 0; i < resources_count; i++) {
+ bswap32(data);
+ data += 4;
+ }
+
+ // inform observer
+ pthread_mutex_lock(&rm->lock);
+ en50221_app_rm_reply_callback cb = rm->replycallback;
+ void *cb_arg = rm->replycallback_arg;
+ pthread_mutex_unlock(&rm->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, resources_count, resource_ids);
+ }
+ return 0;
+}
+
+static int en50221_app_rm_parse_profile_change(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ (void) data;
+ (void) data_length;
+
+ pthread_mutex_lock(&rm->lock);
+ en50221_app_rm_changed_callback cb = rm->changedcallback;
+ void *cb_arg = rm->changedcallback_arg;
+ pthread_mutex_unlock(&rm->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_rm.h b/lib/libdvben50221/en50221_app_rm.h
new file mode 100644
index 0000000..ec97372
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_rm.h
@@ -0,0 +1,187 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_RM_H__
+#define __EN50221_APPLICATION_RM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_RM_RESOURCEID MKRID(1,1,1)
+
+/**
+ * Type definition for profile_enq callback function - called when we receive
+ * a profile_enq from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_rm_enq_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number);
+
+/**
+ * Type definition for profile_reply callback function - called when we receive
+ * a profile_reply from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id_count Number of resource_ids.
+ * @param resource_ids The resource ids themselves.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_rm_reply_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id_count,
+ uint32_t *resource_ids);
+/**
+ * Type definition for profile_changed callback function - called when we receive
+ * a profile_changed from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_rm_changed_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number);
+
+
+
+/**
+ * Opaque type representing a resource manager.
+ */
+struct en50221_app_rm;
+
+/**
+ * Create an instance of the resource manager.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_rm *en50221_app_rm_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the resource manager.
+ *
+ * @param rm Instance to destroy.
+ */
+extern void en50221_app_rm_destroy(struct en50221_app_rm *rm);
+
+/**
+ * Register the callback for when we receive a profile_enq from a CAM.
+ *
+ * @param rm Resource manager instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_rm_register_enq_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_enq_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a profile_reply from a CAM.
+ *
+ * @param rm Resource manager instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_rm_register_reply_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_reply_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive a profile_changed from a CAM.
+ *
+ * @param rm Resource manager instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_rm_register_changed_callback(struct en50221_app_rm *rm,
+ en50221_app_rm_changed_callback callback,
+ void *arg);
+
+/**
+ * Send a profile_enq to a CAM.
+ *
+ * @param rm Resource manager resource instance.
+ * @param session_number Session number to send it on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_rm_enq(struct en50221_app_rm *rm, uint16_t session_number);
+
+/**
+ * Send a profile_reply to a CAM.
+ *
+ * @param rm Resource manager resource instance.
+ * @param session_number Session number to send it on.
+ * @param resource_id_count Number of resource ids.
+ * @param resource_ids The resource IDs themselves
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_rm_reply(struct en50221_app_rm *rm,
+ uint16_t session_number,
+ uint32_t resource_id_count,
+ uint32_t * resource_ids);
+
+/**
+ * Send a profile_changed to a CAM.
+ *
+ * @param rm Resource manager resource instance.
+ * @param session_number Session number to send it on.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_rm_changed(struct en50221_app_rm *rm, uint16_t session_number);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param rm rm instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_rm_message(struct en50221_app_rm *rm,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_smartcard.c b/lib/libdvben50221/en50221_app_smartcard.c
new file mode 100644
index 0000000..763c6c4
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_smartcard.c
@@ -0,0 +1,296 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_smartcard.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_smartcard {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_smartcard_command_callback command_callback;
+ void *command_callback_arg;
+
+ en50221_app_smartcard_send_callback send_callback;
+ void *send_callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_smartcard_parse_command(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+static int en50221_app_smartcard_parse_send(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+struct en50221_app_smartcard *en50221_app_smartcard_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_smartcard *smartcard = NULL;
+
+ // create structure and set it up
+ smartcard = malloc(sizeof(struct en50221_app_smartcard));
+ if (smartcard == NULL) {
+ return NULL;
+ }
+ smartcard->funcs = funcs;
+ smartcard->command_callback = NULL;
+ smartcard->send_callback = NULL;
+
+ pthread_mutex_init(&smartcard->lock, NULL);
+
+ // done
+ return smartcard;
+}
+
+void en50221_app_smartcard_destroy(struct en50221_app_smartcard *smartcard)
+{
+ pthread_mutex_destroy(&smartcard->lock);
+ free(smartcard);
+}
+
+void en50221_app_smartcard_register_command_callback(struct en50221_app_smartcard *smartcard,
+ en50221_app_smartcard_command_callback callback, void *arg)
+{
+ pthread_mutex_lock(&smartcard->lock);
+ smartcard->command_callback = callback;
+ smartcard->command_callback_arg = arg;
+ pthread_mutex_unlock(&smartcard->lock);
+}
+
+void en50221_app_smartcard_register_send_callback(struct en50221_app_smartcard *smartcard,
+ en50221_app_smartcard_send_callback callback, void *arg)
+{
+ pthread_mutex_lock(&smartcard->lock);
+ smartcard->send_callback = callback;
+ smartcard->send_callback_arg = arg;
+ pthread_mutex_unlock(&smartcard->lock);
+}
+
+int en50221_app_smartcard_command_reply(struct en50221_app_smartcard *smartcard,
+ uint16_t session_number,
+ uint8_t reply_id, uint8_t status,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ uint8_t hdr[10];
+ struct iovec iovec[2];
+ int iov_count = 0;
+
+ // the tag
+ hdr[0] = (TAG_SMARTCARD_REPLY >> 16) & 0xFF;
+ hdr[1] = (TAG_SMARTCARD_REPLY >> 8) & 0xFF;
+ hdr[2] = TAG_SMARTCARD_REPLY & 0xFF;
+
+ // the rest of the data
+ if (reply_id == SMARTCARD_REPLY_ID_ANSW_TO_RESET) {
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(data_length + 2, data + 3, 3)) < 0) {
+ return -1;
+ }
+ // the rest of the header
+ hdr[3 + length_field_len] = reply_id;
+ hdr[3 + length_field_len + 1] = status;
+ iovec[0].iov_base = hdr;
+ iovec[0].iov_len = 3 + length_field_len + 2;
+
+ // the data
+ iovec[1].iov_base = data;
+ iovec[1].iov_len = data_length;
+ iov_count = 2;
+ } else {
+ hdr[3] = 2;
+ hdr[4] = reply_id;
+ hdr[5] = status;
+ iovec[0].iov_base = data;
+ iovec[0].iov_len = 6;
+ iov_count = 1;
+ }
+
+ return smartcard->funcs->send_datav(smartcard->funcs->arg, session_number, iovec, iov_count);
+}
+
+int en50221_app_smartcard_receive(struct en50221_app_smartcard *smartcard,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length,
+ uint8_t SW1, uint8_t SW2)
+{
+ uint8_t buf[10];
+ uint8_t trailer[10];
+
+ // set up the tag
+ buf[0] = (TAG_SMARTCARD_RCV >> 16) & 0xFF;
+ buf[1] = (TAG_SMARTCARD_RCV >> 8) & 0xFF;
+ buf[2] = TAG_SMARTCARD_RCV & 0xFF;
+
+ // encode the length field
+ int length_field_len;
+ if ((length_field_len = asn_1_encode(data_length + 2, buf + 3, 3)) < 0) {
+ return -1;
+ }
+ // set up the trailer
+ trailer[0] = SW1;
+ trailer[1] = SW2;
+
+ // build the iovecs
+ struct iovec iov[3];
+ iov[0].iov_base = buf;
+ iov[0].iov_len = 3 + length_field_len;
+ iov[1].iov_base = data;
+ iov[1].iov_len = data_length;
+ iov[2].iov_base = trailer;
+ iov[2].iov_len = 2;
+
+ // create the data and send it
+ return smartcard->funcs->send_datav(smartcard->funcs->arg,
+ session_number, iov, 3);
+}
+
+int en50221_app_smartcard_message(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t *data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_SMARTCARD_COMMAND:
+ return en50221_app_smartcard_parse_command(smartcard,
+ slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ case TAG_SMARTCARD_SEND:
+ return en50221_app_smartcard_parse_send(smartcard, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+
+
+
+
+
+static int en50221_app_smartcard_parse_command(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ if (data_length != 2) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (data[0] != 1) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t command_id = data[1];
+
+ // tell the app
+ pthread_mutex_lock(&smartcard->lock);
+ en50221_app_smartcard_command_callback cb = smartcard->command_callback;
+ void *cb_arg = smartcard->command_callback_arg;
+ pthread_mutex_unlock(&smartcard->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, command_id);
+ }
+ return 0;
+}
+
+static int en50221_app_smartcard_parse_send(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+ // check it
+ if (asn_data_length < 8) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ data += length_field_len;
+
+ // parse
+ uint8_t CLA = data[0];
+ uint8_t INS = data[1];
+ uint8_t P1 = data[2];
+ uint8_t P2 = data[3];
+ uint16_t length_in = (data[4] << 8) | data[5];
+ uint8_t *data_in = data + 6;
+
+ // validate the length
+ if ((length_in + 8) != asn_data_length) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint16_t length_out =
+ (data[6 + length_in] << 8) | data[6 + length_in + 1];
+
+ // tell the app
+ pthread_mutex_lock(&smartcard->lock);
+ en50221_app_smartcard_send_callback cb = smartcard->send_callback;
+ void *cb_arg = smartcard->send_callback_arg;
+ pthread_mutex_unlock(&smartcard->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, CLA, INS, P1,
+ P2, data_in, length_in, length_out);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_smartcard.h b/lib/libdvben50221/en50221_app_smartcard.h
new file mode 100644
index 0000000..bbad4a9
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_smartcard.h
@@ -0,0 +1,200 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_smartcard_H__
+#define __EN50221_APPLICATION_smartcard_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define SMARTCARD_COMMAND_ID_CONNECT 0x01
+#define SMARTCARD_COMMAND_ID_DISCONNECT 0x02
+#define SMARTCARD_COMMAND_ID_POWERON_CARD 0x03
+#define SMARTCARD_COMMAND_ID_POWEROFF_CARD 0x04
+#define SMARTCARD_COMMAND_ID_RESET_CARD 0x05
+#define SMARTCARD_COMMAND_ID_RESET_STATUS 0x06
+#define SMARTCARD_COMMAND_ID_READ_ANSW_TO_RESET 0x07
+
+#define SMARTCARD_REPLY_ID_CONNECTED 0x01
+#define SMARTCARD_REPLY_ID_FREE 0x02
+#define SMARTCARD_REPLY_ID_BUSY 0x03
+#define SMARTCARD_REPLY_ID_ANSW_TO_RESET 0x04
+#define SMARTCARD_REPLY_ID_NO_ANSW_TO_RESET 0x05
+
+#define SMARTCARD_STATUS_CARD_INSERTED 0x01
+#define SMARTCARD_STATUS_CARD_REMOVED 0x02
+#define SMARTCARD_STATUS_CARD_IN_PLACE_POWEROFF 0x03
+#define SMARTCARD_STATUS_CARD_IN_PLACE_POWERON 0x04
+#define SMARTCARD_STATUS_CARD_NO_CARD 0x05
+#define SMARTCARD_STATUS_CARD_UNRESPONSIVE_CARD 0x06
+#define SMARTCARD_STATUS_CARD_REFUSED_CARD 0x07
+
+#define EN50221_APP_SMARTCARD_RESOURCEID(DEVICE_NUMBER) MKRID(112, ((DEVICE_NUMBER)& 0x0f), 1)
+
+
+
+/**
+ * Type definition for command - called when we receive a command.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param command_id One of the SMARTCARD_COMMAND_ID_* values
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_smartcard_command_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t command_id);
+
+/**
+ * Type definition for command - called when we receive a send command.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param CLA CLA value.
+ * @param INS INS value.
+ * @param P1 P1 value.
+ * @param P2 P2 value.
+ * @param in Data to send to the card
+ * @param in_length Number of bytes to send.
+ * @param out_length Number of bytes expected.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_smartcard_send_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t CLA,
+ uint8_t INS,
+ uint8_t P1,
+ uint8_t P2,
+ uint8_t *in,
+ uint32_t in_length,
+ uint32_t out_length);
+
+/**
+ * Opaque type representing a smartcard resource.
+ */
+struct en50221_app_smartcard;
+
+/**
+ * Create an instance of the smartcard resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_smartcard *
+ en50221_app_smartcard_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the smartcard resource.
+ *
+ * @param smartcard Instance to destroy.
+ */
+extern void en50221_app_smartcard_destroy(struct en50221_app_smartcard *smartcard);
+
+/**
+ * Register the callback for when we receive a comms command.
+ *
+ * @param smartcard smartcard resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_smartcard_register_command_callback(struct en50221_app_smartcard *smartcard,
+ en50221_app_smartcard_command_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for when we receive data to send.
+ *
+ * @param smartcard smartcard resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_smartcard_register_send_callback(struct en50221_app_smartcard *smartcard,
+ en50221_app_smartcard_send_callback callback,
+ void *arg);
+
+/**
+ * Send a command response to the CAM.
+ *
+ * @param smartcard smartcard resource instance.
+ * @param session_number Session number to send it on.
+ * @param reply_id One of the SMARTCARD_REPLY_ID_* values.
+ * @param status One of the SMARTCARD_STATUS_* values.
+ * @param data Data to send when it is a SMARTCARD_REPLY_ID_ANSW_TO_RESET.
+ * @param data_length Length of data to send.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_smartcard_command_reply(struct en50221_app_smartcard *smartcard,
+ uint16_t session_number,
+ uint8_t reply_id,
+ uint8_t status,
+ uint8_t * data,
+ uint32_t data_length);
+
+/**
+ * Send data received from a smartcart to the CAM.
+ *
+ * @param smartcard smartcard resource instance.
+ * @param session_number Session number to send it on.
+ * @param data Data to send when it is a SMARTCARD_REPLY_ID_ANSW_TO_RESET.
+ * @param data_length Length of data to send.
+ * @param SW1 SW1 value.
+ * @param SW2 SW2 value.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_smartcard_receive(struct en50221_app_smartcard *smartcard,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length,
+ uint8_t SW1, uint8_t SW2);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param smartcard smartcard instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_smartcard_message(struct en50221_app_smartcard *smartcard,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_tags.h b/lib/libdvben50221/en50221_app_tags.h
new file mode 100644
index 0000000..0f5c2fc
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_tags.h
@@ -0,0 +1,104 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APP_TAGS_H__
+#define __EN50221_APP_TAGS_H__
+
+/* Resource Manager */
+#define TAG_PROFILE_ENQUIRY 0x9f8010
+#define TAG_PROFILE 0x9f8011
+#define TAG_PROFILE_CHANGE 0x9f8012
+
+/* Application Info */
+#define TAG_APP_INFO_ENQUIRY 0x9f8020
+#define TAG_APP_INFO 0x9f8021
+#define TAG_ENTER_MENU 0x9f8022
+
+/* CA Support */
+#define TAG_CA_INFO_ENQUIRY 0x9f8030
+#define TAG_CA_INFO 0x9f8031
+#define TAG_CA_PMT 0x9f8032
+#define TAG_CA_PMT_REPLY 0x9f8033
+
+/* Host Control */
+#define TAG_TUNE 0x9f8400
+#define TAG_REPLACE 0x9f8401
+#define TAG_CLEAR_REPLACE 0x9f8402
+#define TAG_ASK_RELEASE 0x9f8403
+
+/* Date and Time */
+#define TAG_DATE_TIME_ENQUIRY 0x9f8440
+#define TAG_DATE_TIME 0x9f8441
+
+/* Man Machine Interface (MMI) */
+#define TAG_CLOSE_MMI 0x9f8800
+#define TAG_DISPLAY_CONTROL 0x9f8801
+#define TAG_DISPLAY_REPLY 0x9f8802
+#define TAG_TEXT_LAST 0x9f8803
+#define TAG_TEXT_MORE 0x9f8804
+#define TAG_KEYPAD_CONTROL 0x9f8805
+#define TAG_KEYPRESS 0x9f8806
+#define TAG_ENQUIRY 0x9f8807
+#define TAG_ANSWER 0x9f8808
+#define TAG_MENU_LAST 0x9f8809
+#define TAG_MENU_MORE 0x9f880a
+#define TAG_MENU_ANSWER 0x9f880b
+#define TAG_LIST_LAST 0x9f880c
+#define TAG_LIST_MORE 0x9f880d
+#define TAG_SUBTITLE_SEGMENT_LAST 0x9f880e
+#define TAG_SUBTITLE_SEGMENT_MORE 0x9f880f
+#define TAG_DISPLAY_MESSAGE 0x9f8810
+#define TAG_SCENE_END_MARK 0x9f8811
+#define TAG_SCENE_DONE 0x9f8812
+#define TAG_SCENE_CONTROL 0x9f8813
+#define TAG_SUBTITLE_DOWNLOAD_LAST 0x9f8814
+#define TAG_SUBTITLE_DOWNLOAD_MORE 0x9f8815
+#define TAG_FLUSH_DOWNLOAD 0x9f8816
+#define TAG_DOWNLOAD_REPLY 0x9f8817
+
+/* Low Speed Communications */
+#define TAG_COMMS_COMMAND 0x9f8c00
+#define TAG_CONNECTION_DESCRIPTOR 0x9f8c01
+#define TAG_COMMS_REPLY 0x9f8c02
+#define TAG_COMMS_SEND_LAST 0x9f8c03
+#define TAG_COMMS_SEND_MORE 0x9f8c04
+#define TAG_COMMS_RECV_LAST 0x9f8c05
+#define TAG_COMMS_RECV_MORE 0x9f8c06
+
+/* Authentication */
+#define TAG_AUTH_REQ 0x9f8200
+#define TAG_AUTH_RESP 0x9f8201
+
+/* Teletext */
+#define TAG_TELETEXT_EBU 0x9f9000
+
+/* Smartcard */
+#define TAG_SMARTCARD_COMMAND 0x9f8e00
+#define TAG_SMARTCARD_REPLY 0x9f8e01
+#define TAG_SMARTCARD_SEND 0x9f8e02
+#define TAG_SMARTCARD_RCV 0x9f8e03
+
+/* EPG */
+#define TAG_EPG_ENQUIRY 0x9f8f00
+#define TAG_EPG_REPLY 0x9f8f01
+
+#endif
diff --git a/lib/libdvben50221/en50221_app_teletext.c b/lib/libdvben50221/en50221_app_teletext.c
new file mode 100644
index 0000000..b839407
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_teletext.c
@@ -0,0 +1,141 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <pthread.h>
+#include "en50221_app_teletext.h"
+#include "en50221_app_tags.h"
+#include "asn_1.h"
+
+struct en50221_app_teletext {
+ struct en50221_app_send_functions *funcs;
+
+ en50221_app_teletext_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t lock;
+};
+
+static int en50221_app_teletext_parse_ebu(struct en50221_app_teletext *teletext,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t * data,
+ uint32_t data_length);
+
+
+
+struct en50221_app_teletext *
+ en50221_app_teletext_create(struct en50221_app_send_functions *funcs)
+{
+ struct en50221_app_teletext *teletext = NULL;
+
+ // create structure and set it up
+ teletext = malloc(sizeof(struct en50221_app_teletext));
+ if (teletext == NULL) {
+ return NULL;
+ }
+ teletext->funcs = funcs;
+ teletext->callback = NULL;
+
+ pthread_mutex_init(&teletext->lock, NULL);
+
+ // done
+ return teletext;
+}
+
+void en50221_app_teletext_destroy(struct en50221_app_teletext *teletext)
+{
+ pthread_mutex_destroy(&teletext->lock);
+ free(teletext);
+}
+
+void en50221_app_teletext_register_callback(struct en50221_app_teletext *teletext,
+ en50221_app_teletext_callback callback, void *arg)
+{
+ pthread_mutex_lock(&teletext->lock);
+ teletext->callback = callback;
+ teletext->callback_arg = arg;
+ pthread_mutex_unlock(&teletext->lock);
+}
+
+int en50221_app_teletext_message(struct en50221_app_teletext *teletext,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data, uint32_t data_length)
+{
+ (void) resource_id;
+
+ // get the tag
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint32_t tag = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ switch (tag) {
+ case TAG_TELETEXT_EBU:
+ return en50221_app_teletext_parse_ebu(teletext, slot_id,
+ session_number,
+ data + 3,
+ data_length - 3);
+ }
+
+ print(LOG_LEVEL, ERROR, 1, "Received unexpected tag %x\n", tag);
+ return -1;
+}
+
+
+static int en50221_app_teletext_parse_ebu(struct en50221_app_teletext *teletext,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *data,
+ uint32_t data_length)
+{
+ // first of all, decode the length field
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data, data_length)) < 0) {
+ print(LOG_LEVEL, ERROR, 1, "ASN.1 decode error\n");
+ return -1;
+ }
+
+ // check it
+ if (asn_data_length > (data_length - length_field_len)) {
+ print(LOG_LEVEL, ERROR, 1, "Received short data\n");
+ return -1;
+ }
+ uint8_t *teletext_data = data + length_field_len;
+
+ // tell the app
+ pthread_mutex_lock(&teletext->lock);
+ en50221_app_teletext_callback cb = teletext->callback;
+ void *cb_arg = teletext->callback_arg;
+ pthread_mutex_unlock(&teletext->lock);
+ if (cb) {
+ return cb(cb_arg, slot_id, session_number, teletext_data,
+ asn_data_length);
+ }
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_app_teletext.h b/lib/libdvben50221/en50221_app_teletext.h
new file mode 100644
index 0000000..b5b85f1
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_teletext.h
@@ -0,0 +1,107 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APPLICATION_teletext_H__
+#define __EN50221_APPLICATION_teletext_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_app_utils.h>
+
+#define EN50221_APP_TELETEXT_RESOURCEID MKRID(128, 1, 1)
+
+
+/**
+ * Type definition for request - called when we receive teletext from a CAM.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number concerned.
+ * @param teletext_data Data for the request.
+ * @param teletext_data_lenghth Number of bytes.
+ * @return 0 on success, -1 on failure.
+ */
+typedef int (*en50221_app_teletext_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint8_t *teletext_data,
+ uint32_t teletext_data_length);
+
+/**
+ * Opaque type representing a teletext resource.
+ */
+struct en50221_app_teletext;
+
+/**
+ * Create an instance of the teletext resource.
+ *
+ * @param funcs Send functions to use.
+ * @return Instance, or NULL on failure.
+ */
+extern struct en50221_app_teletext *
+ en50221_app_teletext_create(struct en50221_app_send_functions *funcs);
+
+/**
+ * Destroy an instance of the teletext resource.
+ *
+ * @param teletext Instance to destroy.
+ */
+extern void en50221_app_teletext_destroy(struct en50221_app_teletext *teletext);
+
+/**
+ * Register the callback for when we receive a request.
+ *
+ * @param teletext teletext resource instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_app_teletext_register_callback(struct en50221_app_teletext *teletext,
+ en50221_app_teletext_callback callback,
+ void *arg);
+
+/**
+ * Pass data received for this resource into it for parsing.
+ *
+ * @param teletext teletext instance.
+ * @param slot_id Slot ID concerned.
+ * @param session_number Session number concerned.
+ * @param resource_id Resource ID concerned.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, -1 on failure.
+ */
+extern int en50221_app_teletext_message(struct en50221_app_teletext *teletext,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data,
+ uint32_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_app_utils.c b/lib/libdvben50221/en50221_app_utils.c
new file mode 100644
index 0000000..df2632a
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_utils.c
@@ -0,0 +1,38 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "en50221_app_utils.h"
+
+struct en50221_app_public_resource_id
+ *en50221_app_decode_public_resource_id(struct en50221_app_public_resource_id *idf,
+ uint32_t resource_id)
+{
+ // reject private resources
+ if ((resource_id & 0xc0000000) == 0xc0000000)
+ return NULL;
+
+ idf->resource_class = (resource_id >> 16) & 0xffff; // use the resource_id as the MSBs of class
+ idf->resource_type = (resource_id >> 6) & 0x3ff;
+ idf->resource_version = resource_id & 0x3f;
+ return idf;
+}
diff --git a/lib/libdvben50221/en50221_app_utils.h b/lib/libdvben50221/en50221_app_utils.h
new file mode 100644
index 0000000..5c64760
--- /dev/null
+++ b/lib/libdvben50221/en50221_app_utils.h
@@ -0,0 +1,112 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef __EN50221_APP_UTILS_H__
+#define __EN50221_APP_UTILS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/uio.h>
+
+/**
+ * A decomposed public resource structure.
+ *
+ * we will ignore private resource (resource_id_type==3),
+ * because they are not used by any modules at all and
+ * would need special code for any private resource anyway.
+ */
+struct en50221_app_public_resource_id {
+ uint16_t resource_class;
+ uint16_t resource_type;
+ uint8_t resource_version;
+};
+
+typedef int (*en50221_send_data) (void *arg,
+ uint16_t session_number,
+ uint8_t * data,
+ uint16_t data_length);
+typedef int (*en50221_send_datav) (void *arg,
+ uint16_t session_number,
+ struct iovec * vector,
+ int iov_count);
+
+/**
+ * An abstraction away from hardcoded send functions so different layers may be
+ * slotted in under the application layer.
+ */
+struct en50221_app_send_functions {
+ /**
+ * Argument to pass to these functions.
+ */
+ void *arg;
+
+ /**
+ * Send data.
+ */
+ en50221_send_data send_data;
+
+ /**
+ * Send vector data.
+ */
+ en50221_send_datav send_datav;
+};
+
+/**
+ * Make a host-endian uint32_t formatted resource id.
+ *
+ * @param CLASS Class of resource.
+ * @param TYPE Type of resource.
+ * @param VERSION Version of resource.
+ * @return Formatted resource id.
+ */
+#define MKRID(CLASS, TYPE, VERSION) ((((CLASS)&0xffff)<<16) | (((TYPE)&0x3ff)<<6) | ((VERSION)&0x3f))
+
+/**
+ * Decode a host-endian public resource_id into an en50221_app_public_resource_id structure.
+ *
+ * @param idf Structure to write decoded resource_id into.
+ * @param resource_id ID to decode.
+ * @return Pointer to idf on success, or NULL if this is not a public resource.
+ */
+struct en50221_app_public_resource_id *
+ en50221_app_decode_public_resource_id(struct en50221_app_public_resource_id *idf,
+ uint32_t resource_id);
+
+/**
+ * Encode an en50221_app_public_resource_id structure into a host-endian uint32_t.
+ *
+ * @param idf Structure to encode.
+ * @return The encoded value
+ */
+static inline uint32_t en50221_app_encode_public_resource_id(struct en50221_app_public_resource_id *idf) {
+ return MKRID(idf->resource_class, idf->resource_type, idf->resource_version);
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_errno.h b/lib/libdvben50221/en50221_errno.h
new file mode 100644
index 0000000..0b53087
--- /dev/null
+++ b/lib/libdvben50221/en50221_errno.h
@@ -0,0 +1,49 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 session layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef EN50221_ERRNO
+#define EN50221_ERRNO 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EN50221ERR_CAREAD -1 /* error during read from CA device. */
+#define EN50221ERR_CAWRITE -2 /* error during write to CA device. */
+#define EN50221ERR_TIMEOUT -3 /* timeout occured waiting for a response from a device. */
+#define EN50221ERR_BADSLOTID -4 /* bad slot ID supplied by user - the offending slot_id will not be set. */
+#define EN50221ERR_BADCONNECTIONID -5 /* bad connection ID supplied by user. */
+#define EN50221ERR_BADSTATE -6 /* slot/connection in the wrong state. */
+#define EN50221ERR_BADCAMDATA -7 /* CAM supplied an invalid request. */
+#define EN50221ERR_OUTOFMEMORY -8 /* memory allocation failed. */
+#define EN50221ERR_ASNENCODE -9 /* ASN.1 encode failure - indicates library bug. */
+#define EN50221ERR_OUTOFCONNECTIONS -10 /* no more connections available. */
+#define EN50221ERR_OUTOFSLOTS -11 /* no more slots available - the offending slot_id will not be set. */
+#define EN50221ERR_IOVLIMIT -12 /* Too many struct iovecs were used. */
+#define EN50221ERR_BADSESSIONNUMBER -13 /* Bad session number suppplied by user. */
+#define EN50221ERR_OUTOFSESSIONS -14 /* no more sessions available. */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_session.c b/lib/libdvben50221/en50221_session.c
new file mode 100644
index 0000000..3fb9902
--- /dev/null
+++ b/lib/libdvben50221/en50221_session.c
@@ -0,0 +1,1055 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <sys/uio.h>
+#include <pthread.h>
+#include "en50221_transport.h"
+#include "en50221_session.h"
+#include "en50221_errno.h"
+#include "asn_1.h"
+
+
+// these are the possible session statuses
+#define S_STATUS_OPEN 0x00 // session is opened
+#define S_STATUS_CLOSE_NO_RES 0xF0 // could not open session, no proper resource available
+#define S_STATUS_CLOSE_RES_UNAVAILABLE 0xF1 // could not open session, resource unavailable
+#define S_STATUS_CLOSE_RES_LOW_VERSION 0xF2 // could not open session, resource version too low
+#define S_STATUS_CLOSE_RES_BUSY 0xF3 // could not open session, resource is busy
+
+#define ST_OPEN_SESSION_REQ 0x91 // h<--m
+#define ST_OPEN_SESSION_RES 0x92 // h-->m
+#define ST_CREATE_SESSION 0x93 // h-->m
+#define ST_CREATE_SESSION_RES 0x94 // h<--m
+#define ST_CLOSE_SESSION_REQ 0x95 // h<->m
+#define ST_CLOSE_SESSION_RES 0x96 // h<->m
+#define ST_SESSION_NUMBER 0x90 // h<->m
+
+#define S_STATE_IDLE 0x01 // this session is not in use
+#define S_STATE_ACTIVE 0x02 // this session is in use
+#define S_STATE_IN_CREATION 0x04 // this session waits for a ST_CREATE_SESSION_RES to become active
+#define S_STATE_IN_DELETION 0x08 // this session waits for ST_CLOSE_SESSION_RES to become idle again
+
+
+// for each session we store its identifier, the resource-id
+// it is linked to and the callback of the specific resource
+struct en50221_session {
+ uint8_t state;
+ uint32_t resource_id;
+ uint8_t slot_id;
+ uint8_t connection_id;
+
+ en50221_sl_resource_callback callback;
+ void *callback_arg;
+
+ pthread_mutex_t session_lock;
+};
+
+struct en50221_session_layer {
+ uint32_t max_sessions;
+ struct en50221_transport_layer *tl;
+
+ en50221_sl_lookup_callback lookup;
+ void *lookup_arg;
+
+ en50221_sl_session_callback session;
+ void *session_arg;
+
+ pthread_mutex_t global_lock;
+ pthread_mutex_t setcallback_lock;
+
+ int error;
+
+ struct en50221_session *sessions;
+};
+
+static void en50221_sl_transport_callback(void *arg, int reason,
+ uint8_t * data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id);
+static int en50221_sl_alloc_new_session(struct en50221_session_layer *sl,
+ uint32_t resource_id,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ en50221_sl_resource_callback
+ callback, void *arg);
+
+
+
+
+struct en50221_session_layer *en50221_sl_create(struct en50221_transport_layer *tl,
+ uint32_t max_sessions)
+{
+ struct en50221_session_layer *sl = NULL;
+ uint32_t i;
+
+ // setup structure
+ sl = (struct en50221_session_layer *)
+ malloc(sizeof(struct en50221_session_layer));
+ if (sl == NULL)
+ goto error_exit;
+ sl->max_sessions = max_sessions;
+ sl->lookup = NULL;
+ sl->session = NULL;
+ sl->tl = tl;
+ sl->error = 0;
+
+ // init the mutex
+ pthread_mutex_init(&sl->global_lock, NULL);
+ pthread_mutex_init(&sl->setcallback_lock, NULL);
+
+ // create the slots
+ sl->sessions = malloc(sizeof(struct en50221_session) * max_sessions);
+ if (sl->sessions == NULL)
+ goto error_exit;
+
+ // set them up
+ for (i = 0; i < max_sessions; i++) {
+ sl->sessions[i].state = S_STATE_IDLE;
+ sl->sessions[i].callback = NULL;
+
+ pthread_mutex_init(&sl->sessions[i].session_lock, NULL);
+ }
+
+ // register ourselves with the transport layer
+ en50221_tl_register_callback(tl, en50221_sl_transport_callback, sl);
+
+ return sl;
+
+error_exit:
+ en50221_sl_destroy(sl);
+ return NULL;
+}
+
+void en50221_sl_destroy(struct en50221_session_layer *sl)
+{
+ uint32_t i;
+
+ if (sl) {
+ if (sl->sessions) {
+ for (i = 0; i < sl->max_sessions; i++) {
+ pthread_mutex_destroy(&sl->sessions[i].session_lock);
+ }
+ free(sl->sessions);
+ }
+
+ pthread_mutex_destroy(&sl->setcallback_lock);
+ pthread_mutex_destroy(&sl->global_lock);
+
+ free(sl);
+ }
+}
+
+int en50221_sl_get_error(struct en50221_session_layer *sl)
+{
+ return sl->error;
+}
+
+void en50221_sl_register_lookup_callback(struct en50221_session_layer *sl,
+ en50221_sl_lookup_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&sl->setcallback_lock);
+ sl->lookup = callback;
+ sl->lookup_arg = arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+}
+
+void en50221_sl_register_session_callback(struct en50221_session_layer *sl,
+ en50221_sl_session_callback
+ callback, void *arg)
+{
+ pthread_mutex_lock(&sl->setcallback_lock);
+ sl->session = callback;
+ sl->session_arg = arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+}
+
+int en50221_sl_create_session(struct en50221_session_layer *sl,
+ int slot_id, uint8_t connection_id,
+ uint32_t resource_id,
+ en50221_sl_resource_callback callback,
+ void *arg)
+{
+ // lookup next free session_id:
+ pthread_mutex_lock(&sl->global_lock);
+ int session_number =
+ en50221_sl_alloc_new_session(sl, resource_id, slot_id,
+ connection_id, callback, arg);
+ if (session_number == -1) {
+ pthread_mutex_unlock(&sl->global_lock);
+ return -1;
+ }
+ pthread_mutex_unlock(&sl->global_lock);
+
+ // make up the header
+ uint8_t hdr[8];
+ hdr[0] = ST_CREATE_SESSION;
+ hdr[1] = 6;
+ hdr[2] = resource_id >> 24;
+ hdr[3] = resource_id >> 16;
+ hdr[4] = resource_id >> 8;
+ hdr[5] = resource_id;
+ hdr[6] = session_number >> 8;
+ hdr[7] = session_number;
+
+ // send this command
+ if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 8)) {
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (sl->sessions[session_number].state == S_STATE_IN_CREATION) {
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ }
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ sl->error = en50221_tl_get_error(sl->tl);
+ return -1;
+ }
+ // ok.
+ return session_number;
+}
+
+int en50221_sl_destroy_session(struct en50221_session_layer *sl,
+ uint16_t session_number)
+{
+ if (session_number >= sl->max_sessions) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ return -1;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (!(sl->sessions[session_number].state & (S_STATE_ACTIVE | S_STATE_IN_DELETION))) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return -1;
+ }
+ // set the state
+ sl->sessions[session_number].state = S_STATE_IN_DELETION;
+
+ // get essential details
+ uint8_t slot_id = sl->sessions[session_number].slot_id;
+ uint8_t connection_id = sl->sessions[session_number].connection_id;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // sendit
+ uint8_t hdr[4];
+ hdr[0] = ST_CLOSE_SESSION_REQ;
+ hdr[1] = 2;
+ hdr[2] = session_number >> 8;
+ hdr[3] = session_number;
+ if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 4)) {
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (sl->sessions[session_number].state == S_STATE_IN_DELETION) {
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ }
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ sl->error = en50221_tl_get_error(sl->tl);
+ return -1;
+ }
+
+ return 0;
+}
+
+int en50221_sl_send_data(struct en50221_session_layer *sl,
+ uint16_t session_number,
+ uint8_t *data,
+ uint16_t data_length)
+{
+ if (session_number >= sl->max_sessions) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ return -1;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return -1;
+ }
+ // get essential details
+ uint8_t slot_id = sl->sessions[session_number].slot_id;
+ uint8_t connection_id = sl->sessions[session_number].connection_id;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // sendit
+ struct iovec iov[2];
+ uint8_t hdr[4];
+ hdr[0] = ST_SESSION_NUMBER;
+ hdr[1] = 2;
+ hdr[2] = session_number >> 8;
+ hdr[3] = session_number;
+ iov[0].iov_base = hdr;
+ iov[0].iov_len = 4;
+ iov[1].iov_base = data;
+ iov[1].iov_len = data_length;
+ if (en50221_tl_send_datav(sl->tl, slot_id, connection_id, iov, 2)) {
+ sl->error = en50221_tl_get_error(sl->tl);
+ return -1;
+ }
+
+ return 0;
+}
+
+int en50221_sl_send_datav(struct en50221_session_layer *sl,
+ uint16_t session_number,
+ struct iovec *vector,
+ int iov_count)
+{
+ if (session_number >= sl->max_sessions) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ return -1;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
+ sl->error = EN50221ERR_BADSESSIONNUMBER;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return -1;
+ }
+ if (iov_count > 9) {
+ sl->error = EN50221ERR_IOVLIMIT;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return -1;
+ }
+ uint8_t slot_id = sl->sessions[session_number].slot_id;
+ uint8_t connection_id = sl->sessions[session_number].connection_id;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // make up the header
+ struct iovec out_iov[10];
+ uint8_t hdr[4];
+ hdr[0] = ST_SESSION_NUMBER;
+ hdr[1] = 2;
+ hdr[2] = session_number >> 8;
+ hdr[3] = session_number;
+ out_iov[0].iov_base = hdr;
+ out_iov[0].iov_len = 4;
+
+ // make up the data
+ memcpy(&out_iov[1], vector, iov_count * sizeof(struct iovec));
+
+ // send this command
+ if (en50221_tl_send_datav(sl->tl, slot_id, connection_id, out_iov, iov_count + 1)) {
+ sl->error = en50221_tl_get_error(sl->tl);
+ return -1;
+ }
+ return 0;
+}
+
+int en50221_sl_broadcast_data(struct en50221_session_layer *sl,
+ int slot_id, uint32_t resource_id,
+ uint8_t *data, uint16_t data_length)
+{
+ uint32_t i;
+
+ for (i = 0; i < sl->max_sessions; i++) {
+ pthread_mutex_lock(&sl->sessions[i].session_lock);
+
+ if (sl->sessions[i].state != S_STATE_ACTIVE) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+ if ((slot_id != -1)
+ && (slot_id != sl->sessions[i].slot_id)) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+
+ if (sl->sessions[i].resource_id == resource_id) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ en50221_sl_send_data(sl, i, data, data_length);
+ } else {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ }
+ }
+
+ return 0;
+}
+
+
+
+static void en50221_sl_handle_open_session_request(struct en50221_session_layer *sl,
+ uint8_t *data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (data_length < 5) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ if (data[0] != 4) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ // get the resource id
+ uint32_t requested_resource_id =
+ (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+
+ // get lookup callback details
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_lookup_callback lcb = sl->lookup;
+ void *lcb_arg = sl->lookup_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ // first of all, lookup this resource id
+ int status = S_STATUS_CLOSE_NO_RES;
+ en50221_sl_resource_callback resource_callback = NULL;
+ void *resource_arg = NULL;
+ uint32_t connected_resource_id;
+ if (lcb) {
+ status =
+ lcb(lcb_arg, slot_id, requested_resource_id,
+ &resource_callback, &resource_arg,
+ &connected_resource_id);
+ switch (status) {
+ case 0:
+ status = S_STATUS_OPEN;
+ break;
+
+ case -1:
+ status = S_STATUS_CLOSE_NO_RES;
+ break;
+
+ case -2:
+ status = S_STATUS_CLOSE_RES_LOW_VERSION;
+ break;
+
+ case -3:
+ status = S_STATUS_CLOSE_RES_UNAVAILABLE;
+ break;
+ }
+ }
+ // if we found it, get a new session for it
+ int session_number = -1;
+ if (status == S_STATUS_OPEN) {
+ // lookup next free session_id:
+ pthread_mutex_lock(&sl->global_lock);
+ session_number =
+ en50221_sl_alloc_new_session(sl, connected_resource_id,
+ slot_id, connection_id,
+ resource_callback,
+ resource_arg);
+ pthread_mutex_unlock(&sl->global_lock);
+
+ if (session_number == -1) {
+ status = S_STATUS_CLOSE_NO_RES;
+ } else {
+ // inform upper layers/ check availability
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+ if (cb) {
+ if (cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTING,
+ slot_id, session_number,
+ connected_resource_id)) {
+ status = S_STATUS_CLOSE_RES_BUSY;
+ }
+ } else {
+ status = S_STATUS_CLOSE_RES_UNAVAILABLE;
+ }
+ }
+ }
+ // send response
+ uint8_t hdr[9];
+ hdr[0] = ST_OPEN_SESSION_RES;
+ hdr[1] = 7;
+ hdr[2] = status;
+ hdr[3] = connected_resource_id >> 24;
+ hdr[4] = connected_resource_id >> 16;
+ hdr[5] = connected_resource_id >> 8;
+ hdr[6] = connected_resource_id;
+ hdr[7] = session_number >> 8;
+ hdr[8] = session_number;
+ if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 9)) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Transport layer error %i occurred\n",
+ en50221_tl_get_error(sl->tl));
+ status = S_STATUS_CLOSE_NO_RES;
+ // fallthrough
+ }
+ // inform upper layers what happened
+ if (session_number != -1) {
+ // setup session state apppropriately from upper layer response
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (status != S_STATUS_OPEN) {
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ } else {
+ sl->sessions[session_number].state = S_STATE_ACTIVE;
+ }
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // tell upper layers
+ if (sl->sessions[session_number].state == S_STATE_ACTIVE) {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ if (status == S_STATUS_OPEN) {
+ if (cb)
+ cb(cb_arg,
+ S_SCALLBACK_REASON_CAMCONNECTED,
+ slot_id, session_number,
+ connected_resource_id);
+ } else {
+ sl->sessions[session_number].state =
+ S_STATE_IDLE;
+ if (cb)
+ cb(cb_arg,
+ S_SCALLBACK_REASON_CAMCONNECTFAIL,
+ slot_id, session_number,
+ connected_resource_id);
+ }
+ }
+ }
+}
+
+static void en50221_sl_handle_close_session_request(struct en50221_session_layer *sl,
+ uint8_t * data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ if (data[0] != 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ // extract session number
+ uint16_t session_number = (data[1] << 8) | data[2];
+
+ // check session number is ok
+ uint8_t code = 0x00;
+ uint32_t resource_id = 0;
+ if (session_number >= sl->max_sessions) {
+ code = 0xF0; // session close error
+ print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n",
+ slot_id);
+ } else {
+ pthread_mutex_lock(&sl->sessions[session_number].
+ session_lock);
+ if (slot_id != sl->sessions[session_number].slot_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ code = 0xF0; // session close error
+ }
+ if (connection_id != sl->sessions[session_number].connection_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ code = 0xF0; // session close error
+ }
+ if (!(sl->sessions[session_number].state & (S_STATE_ACTIVE | S_STATE_IN_DELETION))) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ code = 0xF0; // session close error
+ }
+
+ if (code == 0x00) {
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ code = 0x00; // close ok
+ }
+ resource_id = sl->sessions[session_number].resource_id;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ }
+
+ // make up the response
+ uint8_t hdr[5];
+ hdr[0] = ST_CLOSE_SESSION_RES;
+ hdr[1] = 3;
+ hdr[2] = code;
+ hdr[3] = session_number >> 8;
+ hdr[4] = session_number;
+
+ // sendit
+ if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 5)) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Transport layer reports error %i on slot %i\n",
+ en50221_tl_get_error(sl->tl), slot_id);
+ }
+ // callback to announce destruction to resource if it was ok
+ if (code == 0x00) {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id,
+ session_number, resource_id);
+ }
+}
+
+static void en50221_sl_handle_create_session_response(struct en50221_session_layer *sl,
+ uint8_t * data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (data_length < 8) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ if (data[0] != 7) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ // extract session number
+ uint16_t session_number = (data[5] << 8) | data[6];
+
+ // check session number is ok
+ if (session_number >= sl->max_sessions) {
+ print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n",
+ slot_id);
+ return;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (slot_id != sl->sessions[session_number].slot_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (connection_id != sl->sessions[session_number].connection_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (sl->sessions[session_number].state != S_STATE_IN_CREATION) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ // extract status
+ if (data[1] != S_STATUS_OPEN) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Session creation failed 0x%02x\n", data[1]);
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // inform upper layers
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_CONNECTFAIL, slot_id,
+ session_number,
+ sl->sessions[session_number].resource_id);
+ return;
+ }
+ // set it active
+ sl->sessions[session_number].state = S_STATE_ACTIVE;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // inform upper layers
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_CONNECTED, slot_id,
+ session_number,
+ sl->sessions[session_number].resource_id);
+}
+
+static void en50221_sl_handle_close_session_response(struct en50221_session_layer *sl,
+ uint8_t *data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (data_length < 5) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ if (data[0] != 4) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ return;
+ }
+ // extract session number
+ uint16_t session_number = (data[2] << 8) | data[3];
+
+ // check session number is ok
+ if (session_number >= sl->max_sessions) {
+ print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id);
+ return;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (slot_id != sl->sessions[session_number].slot_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (connection_id != sl->sessions[session_number].connection_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (sl->sessions[session_number].state != S_STATE_IN_DELETION) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ // extract status
+ if (data[1] != 0x00) {
+ print(LOG_LEVEL, ERROR, 1, "Session close failed 0x%02x\n", data[1]);
+ // just fallthrough anyway
+ }
+ // completed
+ sl->sessions[session_number].state = S_STATE_IDLE;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+}
+
+static void en50221_sl_handle_session_package(struct en50221_session_layer *sl,
+ uint8_t *data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %i\n",
+ slot_id);
+ return;
+ }
+ if (data[0] != 2) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %i\n",
+ slot_id);
+ return;
+ }
+ // get session number
+ uint16_t session_number = (data[1] << 8) | data[2];
+
+ // check it
+ if (session_number >= sl->max_sessions) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with bad session_number from module on slot %i\n",
+ slot_id);
+ return;
+ }
+
+ pthread_mutex_lock(&sl->sessions[session_number].session_lock);
+ if (slot_id != sl->sessions[session_number].slot_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (connection_id != sl->sessions[session_number].connection_id) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unexpected session on invalid slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+ if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with bad session_number from module on slot %i\n",
+ slot_id);
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+ return;
+ }
+
+ en50221_sl_resource_callback cb = sl->sessions[session_number].callback;
+ void *cb_arg = sl->sessions[session_number].callback_arg;
+ uint32_t resource_id = sl->sessions[session_number].resource_id;
+ pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
+
+ // there can be > 1 APDU following the package - all for the same session/resource_id tho.
+ data += 3;
+ data_length -= 3;
+ while (data_length) {
+ // check length field
+ if (data_length < 3) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received invalid sized session package from slot %i\n",
+ slot_id);
+ return;
+ }
+ // parse the APDU's length field
+ int length_field_len;
+ uint16_t asn_data_length;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data + 3, data_length - 3)) < 0) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received invalid sized session package from slot %i\n",
+ slot_id);
+ return;
+ }
+ uint32_t apdu_length = 3 + length_field_len + asn_data_length;
+
+ // check there is enough data
+ if (apdu_length > data_length) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received invalid sized session package from slot %i\n",
+ slot_id);
+ return;
+ }
+ // pass the APDU up to the higher layers
+ if (cb)
+ cb(cb_arg, slot_id, session_number, resource_id, data, apdu_length);
+
+ // next!
+ data += apdu_length;
+ data_length -= apdu_length;
+ }
+
+}
+
+static void en50221_sl_transport_callback(void *arg, int reason,
+ uint8_t *data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ struct en50221_session_layer *sl =
+ (struct en50221_session_layer *) arg;
+ uint32_t i;
+
+ // deal with the reason for this callback
+ switch (reason) {
+ case T_CALLBACK_REASON_DATA:
+ // fallthrough into rest of this function
+ break;
+
+ case T_CALLBACK_REASON_CONNECTIONOPEN:
+ {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_TC_CONNECT,
+ slot_id, connection_id, 0);
+ return;
+ }
+
+ case T_CALLBACK_REASON_CAMCONNECTIONOPEN:
+ {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ if (cb)
+ cb(cb_arg,
+ S_SCALLBACK_REASON_TC_CAMCONNECT,
+ slot_id, connection_id, 0);
+ return;
+ }
+
+ case T_CALLBACK_REASON_CONNECTIONCLOSE:
+ {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ for (i = 0; i < sl->max_sessions; i++) {
+ pthread_mutex_lock(&sl->sessions[i].session_lock);
+
+ if (sl->sessions[i].state == S_STATE_IDLE) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+ if (sl->sessions[i].connection_id != connection_id) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+
+ sl->sessions[i].state = S_STATE_IDLE;
+
+ uint8_t _slot_id = sl->sessions[i].slot_id;
+ uint32_t resource_id = sl->sessions[i].resource_id;
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_CLOSE, _slot_id, i, resource_id);
+ }
+ return;
+ }
+
+ case T_CALLBACK_REASON_SLOTCLOSE:
+ {
+ pthread_mutex_lock(&sl->setcallback_lock);
+ en50221_sl_session_callback cb = sl->session;
+ void *cb_arg = sl->session_arg;
+ pthread_mutex_unlock(&sl->setcallback_lock);
+
+ for (i = 0; i < sl->max_sessions; i++) {
+ pthread_mutex_lock(&sl->sessions[i].session_lock);
+
+ if (sl->sessions[i].state == S_STATE_IDLE) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+ if (sl->sessions[i].slot_id != slot_id) {
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+ continue;
+ }
+ sl->sessions[i].state = S_STATE_IDLE;
+
+ uint32_t resource_id = sl->sessions[i].resource_id;
+ pthread_mutex_unlock(&sl->sessions[i].session_lock);
+
+ if (cb)
+ cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id, i, resource_id);
+
+ }
+ return;
+ }
+ }
+
+ // sanity check data length
+ if (data_length < 1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %i\n",
+ slot_id);
+ return;
+ }
+ // deal with the data
+ uint8_t spdu_tag = data[0];
+ switch (spdu_tag) {
+ case ST_OPEN_SESSION_REQ:
+ en50221_sl_handle_open_session_request(sl, data + 1,
+ data_length - 1,
+ slot_id,
+ connection_id);
+ break;
+
+ case ST_CLOSE_SESSION_REQ:
+ en50221_sl_handle_close_session_request(sl, data + 1,
+ data_length - 1,
+ slot_id,
+ connection_id);
+ break;
+
+ case ST_SESSION_NUMBER:
+ en50221_sl_handle_session_package(sl, data + 1,
+ data_length - 1, slot_id,
+ connection_id);
+ break;
+
+ case ST_CREATE_SESSION_RES:
+ en50221_sl_handle_create_session_response(sl, data + 1,
+ data_length - 1,
+ slot_id,
+ connection_id);
+ break;
+
+ case ST_CLOSE_SESSION_RES:
+ en50221_sl_handle_close_session_response(sl, data + 1,
+ data_length - 1,
+ slot_id,
+ connection_id);
+ break;
+
+ default:
+ print(LOG_LEVEL, ERROR, 1,
+ "Received unknown session tag %02x from module on slot %i",
+ spdu_tag, slot_id);
+ break;
+ }
+}
+
+static int en50221_sl_alloc_new_session(struct en50221_session_layer *sl,
+ uint32_t resource_id,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ en50221_sl_resource_callback
+ callback, void *arg)
+{
+ int session_number = -1;
+ uint32_t i;
+ for (i = 1; i < sl->max_sessions; i++) {
+ if (sl->sessions[i].state == S_STATE_IDLE) {
+ session_number = i;
+ break;
+ }
+ }
+ if (session_number == -1) {
+ sl->error = EN50221ERR_OUTOFSESSIONS;
+ return -1;
+ }
+ // setup the session
+ sl->sessions[session_number].state = S_STATE_IN_CREATION;
+ sl->sessions[session_number].resource_id = resource_id;
+ sl->sessions[session_number].slot_id = slot_id;
+ sl->sessions[session_number].connection_id = connection_id;
+ sl->sessions[session_number].callback = callback;
+ sl->sessions[session_number].callback_arg = arg;
+
+ // ok
+ return session_number;
+}
diff --git a/lib/libdvben50221/en50221_session.h b/lib/libdvben50221/en50221_session.h
new file mode 100644
index 0000000..7b33518
--- /dev/null
+++ b/lib/libdvben50221/en50221_session.h
@@ -0,0 +1,232 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 session layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian@jusst.de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#ifndef __EN50221_SESSION_H__
+#define __EN50221_SESSION_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <libdvben50221/en50221_transport.h>
+
+#define S_SCALLBACK_REASON_CAMCONNECTING 0x00 // CAM originated session connecting to resource (check for availability)
+#define S_SCALLBACK_REASON_CAMCONNECTED 0x01 // CAM originated session connection established succesfully
+#define S_SCALLBACK_REASON_CAMCONNECTFAIL 0x02 // CAM originated session connection failed
+#define S_SCALLBACK_REASON_CONNECTED 0x03 // Host originated session ACKed by CAM.
+#define S_SCALLBACK_REASON_CONNECTFAIL 0x04 // Host originated session NACKed by CAM.
+#define S_SCALLBACK_REASON_CLOSE 0x05 // Session closed
+#define S_SCALLBACK_REASON_TC_CONNECT 0x06 // A host originated transport connection has been established.
+#define S_SCALLBACK_REASON_TC_CAMCONNECT 0x07 // A CAM originated transport connection has been established.
+
+
+/**
+ * Opaque type representing a session layer.
+ */
+struct en50221_session_layer;
+
+/**
+ * Type definition for resource callback function - called by session layer when data
+ * arrives for a particular resource.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number.
+ * @param resource_id Resource id.
+ * @param data The data.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, or -1 on failure.
+ */
+typedef int (*en50221_sl_resource_callback) (void *arg,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id,
+ uint8_t * data,
+ uint32_t data_length);
+
+/**
+ * Type definition for resource lookup callback function - used by the session layer to
+ * look up requested resources.
+ *
+ * @param arg Private argument.
+ * @param slot_id Slot id the request came from.
+ * @param requested_resource_id Resource id requested.
+ * @param callback_out Output parameter for pointer to resource callback function.
+ * @param arg_out Output parameter for arg to pass to resource callback.
+ * @param resource_id_out Set this to the resource_id connected to (e.g. may differ from resource_id due to versions).
+ * @return 0 on success,
+ * -1 if the resource was not found,
+ * -2 if it exists, but had a lower version, or
+ * -3 if it exists, but was unavailable.
+ */
+typedef int (*en50221_sl_lookup_callback) (void *arg,
+ uint8_t slot_id,
+ uint32_t requested_resource_id,
+ en50221_sl_resource_callback * callback_out,
+ void **arg_out,
+ uint32_t *resource_id_out);
+
+
+/**
+ * Type definition for session callback function - used to inform top level code when a CAM
+ * modifies a session to a resource.
+ *
+ * @param arg Private argument.
+ * @param reason One of the S_CCALLBACK_REASON_* values above.
+ * @param slot_id Slot id concerned.
+ * @param session_number Session number.
+ * @param resource_id Resource id.
+ * @return 0 on sucess, or -1 on error.
+ */
+typedef int (*en50221_sl_session_callback) (void *arg, int reason,
+ uint8_t slot_id,
+ uint16_t session_number,
+ uint32_t resource_id);
+
+/**
+ * Construct a new instance of the session layer.
+ *
+ * @param tl The en50221_transport_layer instance to use.
+ * @param max_sessions Maximum number of sessions supported.
+ * @return The en50221_session_layer instance, or NULL on error.
+ */
+extern struct en50221_session_layer *en50221_sl_create(struct en50221_transport_layer *tl,
+ uint32_t max_sessions);
+
+/**
+ * Destroy an instance of the session layer.
+ *
+ * @param tl The en50221_session_layer instance.
+ */
+extern void en50221_sl_destroy(struct en50221_session_layer *sl);
+
+/**
+ * Gets the last error.
+ *
+ * @param tl The en50221_session_layer instance.
+ * @return One of the EN50221ERR_* values.
+ */
+extern int en50221_sl_get_error(struct en50221_session_layer *tl);
+
+/**
+ * Register the callback for resource lookup.
+ *
+ * @param sl The en50221_session_layer instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_sl_register_lookup_callback(struct en50221_session_layer *sl,
+ en50221_sl_lookup_callback callback,
+ void *arg);
+
+/**
+ * Register the callback for informing about session from a cam.
+ *
+ * @param sl The en50221_session_layer instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_sl_register_session_callback(struct en50221_session_layer *sl,
+ en50221_sl_session_callback callback,
+ void *arg);
+
+/**
+ * Create a new session to a module in a slot.
+ *
+ * @param sl The en50221_session_layer instance.
+ * @param slot The slot to connect to.
+ * @param resource_id The resource_id to connect to.
+ * @param callback The callback for received data.
+ * @param arg Argument to pass to the callback.
+ * @return The new session_number, or -1 on error.
+ */
+extern int en50221_sl_create_session(struct en50221_session_layer *sl, int slot_id,
+ uint8_t connection_id,
+ uint32_t resource_id,
+ en50221_sl_resource_callback callback,
+ void *arg);
+
+/**
+ * Destroy a session.
+ *
+ * @param sl The en50221_session_layer instance.
+ * @param session_number The session to destroy.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_sl_destroy_session(struct en50221_session_layer *sl,
+ uint16_t session_number);
+
+/**
+ * this function is used to take a data-block, pack into
+ * into a SPDU (SESSION_NUMBER) and send it to the transport layer
+ *
+ * @param sl The en50221_session_layer instance to use.
+ * @param session_number Session number concerned.
+ * @param data Data to send.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_sl_send_data(struct en50221_session_layer *sl,
+ uint16_t session_number,
+ uint8_t * data,
+ uint16_t data_length);
+
+/**
+ * this function is used to take a data-block, pack into
+ * into a SPDU (SESSION_NUMBER) and send it to the transport layer
+ *
+ * @param sl The en50221_session_layer instance to use.
+ * @param session_number Session number concerned.
+ * @param vector IOVEC to send.
+ * @param iov_count Number of elements in io vector.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_sl_send_datav(struct en50221_session_layer *sl,
+ uint16_t session_number,
+ struct iovec *vector,
+ int iov_count);
+
+/**
+ * this is used to send a message to all sessions, linked
+ * to resource res
+ *
+ * @param tl The en50221_session_layer instance to use.
+ * @param slot_id Set to -1 to send to any slot. Other values will send to only that slot.
+ * @param resource_id Resource id concerned.
+ * @param data Data to send.
+ * @param data_length Length of data in bytes.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_sl_broadcast_data(struct en50221_session_layer *sl,
+ int slot_id,
+ uint32_t resource_id,
+ uint8_t * data,
+ uint16_t data_length);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libdvben50221/en50221_stdcam.c b/lib/libdvben50221/en50221_stdcam.c
new file mode 100644
index 0000000..a00a844
--- /dev/null
+++ b/lib/libdvben50221/en50221_stdcam.c
@@ -0,0 +1,54 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <libdvbapi/dvbca.h>
+#include "en50221_stdcam.h"
+
+struct en50221_stdcam *en50221_stdcam_create(int adapter, int slotnum,
+ struct en50221_transport_layer *tl,
+ struct en50221_session_layer *sl)
+{
+ struct en50221_stdcam *result = NULL;
+
+ int cafd = dvbca_open(adapter, 0);
+ if (cafd == -1)
+ return NULL;
+
+ int ca_type = dvbca_get_interface_type(cafd, slotnum);
+ switch(ca_type) {
+ case DVBCA_INTERFACE_LINK:
+ result = en50221_stdcam_llci_create(cafd, slotnum, tl, sl);
+ break;
+
+ case DVBCA_INTERFACE_HLCI:
+ result = en50221_stdcam_hlci_create(cafd, slotnum);
+ break;
+ }
+
+ if (result == NULL)
+ close(cafd);
+ return result;
+}
diff --git a/lib/libdvben50221/en50221_stdcam.h b/lib/libdvben50221/en50221_stdcam.h
new file mode 100644
index 0000000..154ff76
--- /dev/null
+++ b/lib/libdvben50221/en50221_stdcam.h
@@ -0,0 +1,102 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef EN50221_STDCAM_H
+#define EN50221_STDCAM_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdvben50221/en50221_app_ai.h>
+#include <libdvben50221/en50221_app_ca.h>
+#include <libdvben50221/en50221_app_mmi.h>
+#include <libdvben50221/en50221_session.h>
+#include <libdvben50221/en50221_transport.h>
+
+enum en50221_stdcam_status {
+ EN50221_STDCAM_CAM_NONE,
+ EN50221_STDCAM_CAM_INRESET,
+ EN50221_STDCAM_CAM_OK,
+ EN50221_STDCAM_CAM_BAD,
+};
+
+struct en50221_stdcam {
+ /* one of more of the following may be NULL if a CAM does not support it */
+ struct en50221_app_ai *ai_resource;
+ struct en50221_app_ca *ca_resource;
+ struct en50221_app_mmi *mmi_resource;
+
+ /* if any of these are -1, no connection is in place to this resource yet */
+ int ai_session_number;
+ int ca_session_number;
+ int mmi_session_number;
+
+ /* poll the stdcam instance */
+ enum en50221_stdcam_status (*poll)(struct en50221_stdcam *stdcam);
+
+ /* inform the stdcam of the current DVB time */
+ void (*dvbtime)(struct en50221_stdcam *stdcam, time_t dvbtime);
+
+ /* destroy the stdcam instance */
+ void (*destroy)(struct en50221_stdcam *stdcam, int closefd);
+};
+
+/**
+ * Create an instance of the STDCAM for an LLCI interface.
+ *
+ * @param cafd FD of the CA device.
+ * @param slotnum Slotnum on that CA device.
+ * @param tl Transport layer instance to use.
+ * @param sl Session layer instance to use.
+ * @return en50221_stdcam instance, or NULL on error.
+ */
+extern struct en50221_stdcam *en50221_stdcam_llci_create(int cafd, int slotnum,
+ struct en50221_transport_layer *tl,
+ struct en50221_session_layer *sl);
+
+/**
+ * Create an instance of the STDCAM for an HLCI interface.
+ *
+ * @param cafd FD of the CA device.
+ * @param slotnum Slotnum on that CA device.
+ * @return en50221_stdcam instance, or NULL on error.
+ */
+extern struct en50221_stdcam *en50221_stdcam_hlci_create(int cafd, int slotnum);
+
+/**
+ * Convenience method to create a STDCAM interface for a ca device on a particular adapter.
+ *
+ * @param adapter The DVB adapter concerned.
+ * @param slotnum The ca slot number on that adapter.
+ * @param tl Transport layer instance to use (unused for HLCI cams).
+ * @param sl Session layer instance to use (unused for HLCI cams).
+ * @return en50221_stdcam instance, or NULL on error.
+ */
+extern struct en50221_stdcam *en50221_stdcam_create(int adapter, int slotnum,
+ struct en50221_transport_layer *tl,
+ struct en50221_session_layer *sl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/libdvben50221/en50221_stdcam_hlci.c b/lib/libdvben50221/en50221_stdcam_hlci.c
new file mode 100644
index 0000000..f21637b
--- /dev/null
+++ b/lib/libdvben50221/en50221_stdcam_hlci.c
@@ -0,0 +1,216 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <libdvbapi/dvbca.h>
+#include "en50221_app_utils.h"
+#include "en50221_app_tags.h"
+#include "en50221_stdcam.h"
+
+
+struct en50221_stdcam_hlci {
+ struct en50221_stdcam stdcam;
+
+ int cafd;
+ int slotnum;
+ int initialised;
+ struct en50221_app_send_functions sendfuncs;
+};
+
+static void en50221_stdcam_hlci_destroy(struct en50221_stdcam *stdcam, int closefd);
+static enum en50221_stdcam_status en50221_stdcam_hlci_poll(struct en50221_stdcam *stdcam);
+static int hlci_cam_added(struct en50221_stdcam_hlci *hlci);
+static int hlci_send_data(void *arg, uint16_t session_number,
+ uint8_t * data, uint16_t data_length);
+static int hlci_send_datav(void *arg, uint16_t session_number,
+ struct iovec *vector, int iov_count);
+
+
+
+
+struct en50221_stdcam *en50221_stdcam_hlci_create(int cafd, int slotnum)
+{
+ // try and allocate space for the HLCI stdcam
+ struct en50221_stdcam_hlci *hlci =
+ malloc(sizeof(struct en50221_stdcam_hlci));
+ if (hlci == NULL) {
+ return NULL;
+ }
+ memset(hlci, 0, sizeof(struct en50221_stdcam_hlci));
+
+ // create the sendfuncs
+ hlci->sendfuncs.arg = hlci;
+ hlci->sendfuncs.send_data = hlci_send_data;
+ hlci->sendfuncs.send_datav = hlci_send_datav;
+
+ // create the resources (NOTE: we just use fake session numbers here)
+ hlci->stdcam.ai_resource = en50221_app_ai_create(&hlci->sendfuncs);
+ hlci->stdcam.ai_session_number = 0;
+ hlci->stdcam.ca_resource = en50221_app_ca_create(&hlci->sendfuncs);
+ hlci->stdcam.ca_session_number = 1;
+// hlci->stdcam.mmi_resource = en50221_app_mmi_create(&hlci->sendfuncs);
+ hlci->stdcam.mmi_session_number = -1;
+
+ // done
+ hlci->stdcam.destroy = en50221_stdcam_hlci_destroy;
+ hlci->stdcam.poll = en50221_stdcam_hlci_poll;
+ hlci->slotnum = slotnum;
+ hlci->cafd = cafd;
+ return &hlci->stdcam;
+}
+
+static void en50221_stdcam_hlci_destroy(struct en50221_stdcam *stdcam, int closefd)
+{
+ struct en50221_stdcam_hlci *hlci = (struct en50221_stdcam_hlci *) stdcam;
+
+ if (hlci->stdcam.ai_resource)
+ en50221_app_ai_destroy(hlci->stdcam.ai_resource);
+ if (hlci->stdcam.ca_resource)
+ en50221_app_ca_destroy(hlci->stdcam.ca_resource);
+ if (hlci->stdcam.mmi_resource)
+ en50221_app_mmi_destroy(hlci->stdcam.mmi_resource);
+
+ if (closefd)
+ close(hlci->cafd);
+
+ free(hlci);
+}
+
+static enum en50221_stdcam_status en50221_stdcam_hlci_poll(struct en50221_stdcam *stdcam)
+{
+ struct en50221_stdcam_hlci *hlci = (struct en50221_stdcam_hlci *) stdcam;
+
+ switch(dvbca_get_cam_state(hlci->cafd, hlci->slotnum)) {
+ case DVBCA_CAMSTATE_MISSING:
+ hlci->initialised = 0;
+ break;
+
+ case DVBCA_CAMSTATE_READY:
+ case DVBCA_CAMSTATE_INITIALISING:
+ if (!hlci->initialised)
+ hlci_cam_added(hlci);
+ break;
+ }
+
+ // delay to prevent busy loop
+ usleep(10);
+
+ if (!hlci->initialised) {
+ return EN50221_STDCAM_CAM_NONE;
+ }
+ return EN50221_STDCAM_CAM_OK;
+}
+
+
+
+static int hlci_cam_added(struct en50221_stdcam_hlci *hlci)
+{
+ uint8_t buf[256];
+ int size;
+
+ // get application information
+ if (en50221_app_ai_enquiry(hlci->stdcam.ai_resource, 0)) {
+ return -EIO;
+ }
+ if ((size = dvbca_hlci_read(hlci->cafd, TAG_APP_INFO, buf, sizeof(buf))) < 0) {
+ return size;
+ }
+ if (en50221_app_ai_message(hlci->stdcam.ai_resource, 0, 0, EN50221_APP_AI_RESOURCEID, buf, size)) {
+ return -EIO;
+ }
+
+ // we forge a fake CA_INFO here so the main app works - since it will expect a CA_INFO
+ // this will be replaced with a proper call (below) when the driver support is there
+ buf[0] = TAG_CA_INFO >> 16;
+ buf[1] = (uint8_t) (TAG_CA_INFO >> 8);
+ buf[2] = (uint8_t) TAG_CA_INFO;
+ buf[3] = 0;
+ if (en50221_app_ca_message(hlci->stdcam.ca_resource, 0, 0, EN50221_APP_CA_RESOURCEID, buf, 4)) {
+ return -EIO;
+ }
+
+ /*
+ // get CA information
+ if (en50221_app_ca_info_enq(ca_resource, 0)) {
+ fprintf(stderr, "Failed to send CA INFO enquiry\n");
+ cafd = -1;
+ return -1;
+ }
+ if ((size = dvbca_hlci_read(cafd, TAG_CA_INFO, buf, sizeof(buf))) < 0) {
+ fprintf(stderr, "Failed to read CA INFO\n");
+ cafd = -1;
+ return -1;
+ }
+ if (en50221_app_ca_message(ca_resource, 0, 0, EN50221_APP_CA_RESOURCEID, buf, size)) {
+ fprintf(stderr, "Failed to parse CA INFO\n");
+ cafd = -1;
+ return -1;
+ }
+ */
+
+ // done
+ hlci->initialised = 1;
+ return 0;
+}
+
+static int hlci_send_data(void *arg, uint16_t session_number,
+ uint8_t * data, uint16_t data_length)
+{
+ (void) session_number;
+ struct en50221_stdcam_hlci *hlci = arg;
+
+ return dvbca_hlci_write(hlci->cafd, data, data_length);
+}
+
+static int hlci_send_datav(void *arg, uint16_t session_number,
+ struct iovec *vector, int iov_count)
+{
+ (void) session_number;
+ struct en50221_stdcam_hlci *hlci = arg;
+
+ // calculate the total length of the data to send
+ uint32_t data_size = 0;
+ int i;
+ for (i = 0; i < iov_count; i++) {
+ data_size += vector[i].iov_len;
+ }
+
+ // allocate memory for it
+ uint8_t *buf = malloc(data_size);
+ if (buf == NULL) {
+ return -1;
+ }
+ // merge the iovecs
+ uint32_t pos = 0;
+ for (i = 0; i < iov_count; i++) {
+ memcpy(buf + pos, vector[i].iov_base, vector[i].iov_len);
+ pos += vector[i].iov_len;
+ }
+
+ // sendit and cleanup
+ int status = dvbca_hlci_write(hlci->cafd, buf, data_size);
+ free(buf);
+ return status;
+}
diff --git a/lib/libdvben50221/en50221_stdcam_llci.c b/lib/libdvben50221/en50221_stdcam_llci.c
new file mode 100644
index 0000000..2266bbf
--- /dev/null
+++ b/lib/libdvben50221/en50221_stdcam_llci.c
@@ -0,0 +1,437 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <libdvbapi/dvbca.h>
+#include <libdvbmisc/dvbmisc.h>
+#include "en50221_app_rm.h"
+#include "en50221_app_datetime.h"
+#include "en50221_app_utils.h"
+#include "en50221_app_tags.h"
+#include "en50221_stdcam.h"
+
+#define LLCI_RESPONSE_TIMEOUT_MS 1000
+#define LLCI_POLL_DELAY_MS 100
+
+/* resource IDs we support */
+static uint32_t resource_ids[] =
+{ EN50221_APP_RM_RESOURCEID,
+ EN50221_APP_CA_RESOURCEID,
+ EN50221_APP_AI_RESOURCEID,
+ EN50221_APP_MMI_RESOURCEID,
+ EN50221_APP_DATETIME_RESOURCEID,
+};
+#define RESOURCE_IDS_COUNT sizeof(resource_ids)/4
+
+struct llci_resource {
+ struct en50221_app_public_resource_id resid;
+ uint32_t binary_resource_id;
+ en50221_sl_resource_callback callback;
+ void *arg;
+};
+
+struct en50221_stdcam_llci {
+ struct en50221_stdcam stdcam;
+
+ int cafd;
+ int slotnum;
+ int state;
+
+ struct llci_resource resources[RESOURCE_IDS_COUNT];
+
+ struct en50221_transport_layer *tl;
+ struct en50221_session_layer *sl;
+ struct en50221_app_send_functions sendfuncs;
+ int tl_slot_id;
+
+ struct en50221_app_rm *rm_resource;
+
+ struct en50221_app_datetime *datetime_resource;
+ int datetime_session_number;
+ uint8_t datetime_response_interval;
+ time_t datetime_next_send;
+ time_t datetime_dvbtime;
+};
+
+static enum en50221_stdcam_status en50221_stdcam_llci_poll(struct en50221_stdcam *stdcam);
+static void en50221_stdcam_llci_dvbtime(struct en50221_stdcam *stdcam, time_t dvbtime);
+static void en50221_stdcam_llci_destroy(struct en50221_stdcam *stdcam, int closefd);
+static void llci_cam_added(struct en50221_stdcam_llci *llci);
+static void llci_cam_in_reset(struct en50221_stdcam_llci *llci);
+static void llci_cam_removed(struct en50221_stdcam_llci *llci);
+
+
+static int llci_lookup_callback(void *arg, uint8_t _slot_id, uint32_t requested_resource_id,
+ en50221_sl_resource_callback *callback_out, void **arg_out,
+ uint32_t *connected_resource_id);
+static int llci_session_callback(void *arg, int reason, uint8_t _slot_id, uint16_t session_number, uint32_t resource_id);
+static int llci_rm_enq_callback(void *arg, uint8_t _slot_id, uint16_t session_number);
+static int llci_rm_reply_callback(void *arg, uint8_t _slot_id, uint16_t session_number, uint32_t resource_id_count, uint32_t *_resource_ids);
+static int llci_rm_changed_callback(void *arg, uint8_t _slot_id, uint16_t session_number);
+
+static int llci_datetime_enquiry_callback(void *arg, uint8_t _slot_id, uint16_t session_number, uint8_t response_interval);
+
+
+struct en50221_stdcam *en50221_stdcam_llci_create(int cafd, int slotnum,
+ struct en50221_transport_layer *tl,
+ struct en50221_session_layer *sl)
+{
+ // try and allocate space for the LLCI stdcam
+ struct en50221_stdcam_llci *llci =
+ malloc(sizeof(struct en50221_stdcam_llci));
+ if (llci == NULL) {
+ return NULL;
+ }
+ memset(llci, 0, sizeof(struct en50221_stdcam_llci));
+
+ // create the sendfuncs
+ llci->sendfuncs.arg = sl;
+ llci->sendfuncs.send_data = (en50221_send_data) en50221_sl_send_data;
+ llci->sendfuncs.send_datav = (en50221_send_datav) en50221_sl_send_datav;
+
+ // create the resource manager resource
+ int resource_idx = 0;
+ llci->rm_resource = en50221_app_rm_create(&llci->sendfuncs);
+ en50221_app_decode_public_resource_id(&llci->resources[resource_idx].resid, EN50221_APP_RM_RESOURCEID);
+ llci->resources[resource_idx].binary_resource_id = EN50221_APP_RM_RESOURCEID;
+ llci->resources[resource_idx].callback = (en50221_sl_resource_callback) en50221_app_rm_message;
+ llci->resources[resource_idx].arg = llci->rm_resource;
+ en50221_app_rm_register_enq_callback(llci->rm_resource, llci_rm_enq_callback, llci);
+ en50221_app_rm_register_reply_callback(llci->rm_resource, llci_rm_reply_callback, llci);
+ en50221_app_rm_register_changed_callback(llci->rm_resource, llci_rm_changed_callback, llci);
+ resource_idx++;
+
+ // create the datetime resource
+ llci->datetime_resource = en50221_app_datetime_create(&llci->sendfuncs);
+ en50221_app_decode_public_resource_id(&llci->resources[resource_idx].resid, EN50221_APP_DATETIME_RESOURCEID);
+ llci->resources[resource_idx].binary_resource_id = EN50221_APP_DATETIME_RESOURCEID;
+ llci->resources[resource_idx].callback = (en50221_sl_resource_callback) en50221_app_datetime_message;
+ llci->resources[resource_idx].arg = llci->datetime_resource;
+ en50221_app_datetime_register_enquiry_callback(llci->datetime_resource, llci_datetime_enquiry_callback, llci);
+ resource_idx++;
+ llci->datetime_session_number = -1;
+ llci->datetime_response_interval = 0;
+ llci->datetime_next_send = 0;
+ llci->datetime_dvbtime = 0;
+
+ // create the application information resource
+ llci->stdcam.ai_resource = en50221_app_ai_create(&llci->sendfuncs);
+ en50221_app_decode_public_resource_id(&llci->resources[resource_idx].resid, EN50221_APP_AI_RESOURCEID);
+ llci->resources[resource_idx].binary_resource_id = EN50221_APP_AI_RESOURCEID;
+ llci->resources[resource_idx].callback = (en50221_sl_resource_callback) en50221_app_ai_message;
+ llci->resources[resource_idx].arg = llci->stdcam.ai_resource;
+ llci->stdcam.ai_session_number = -1;
+ resource_idx++;
+
+ // create the CA resource
+ llci->stdcam.ca_resource = en50221_app_ca_create(&llci->sendfuncs);
+ en50221_app_decode_public_resource_id(&llci->resources[resource_idx].resid, EN50221_APP_CA_RESOURCEID);
+ llci->resources[resource_idx].binary_resource_id = EN50221_APP_CA_RESOURCEID;
+ llci->resources[resource_idx].callback = (en50221_sl_resource_callback) en50221_app_ca_message;
+ llci->resources[resource_idx].arg = llci->stdcam.ca_resource;
+ llci->stdcam.ca_session_number = -1;
+ resource_idx++;
+
+ // create the MMI resource
+ llci->stdcam.mmi_resource = en50221_app_mmi_create(&llci->sendfuncs);
+ en50221_app_decode_public_resource_id(&llci->resources[resource_idx].resid, EN50221_APP_MMI_RESOURCEID);
+ llci->resources[resource_idx].binary_resource_id = EN50221_APP_MMI_RESOURCEID;
+ llci->resources[resource_idx].callback = (en50221_sl_resource_callback) en50221_app_mmi_message;
+ llci->resources[resource_idx].arg = llci->stdcam.mmi_resource;
+ llci->stdcam.mmi_session_number = -1;
+ resource_idx++;
+
+ // register session layer callbacks
+ en50221_sl_register_lookup_callback(sl, llci_lookup_callback, llci);
+ en50221_sl_register_session_callback(sl, llci_session_callback, llci);
+
+ // done
+ llci->stdcam.destroy = en50221_stdcam_llci_destroy;
+ llci->stdcam.poll = en50221_stdcam_llci_poll;
+ llci->stdcam.dvbtime = en50221_stdcam_llci_dvbtime;
+ llci->cafd = cafd;
+ llci->slotnum = slotnum;
+ llci->tl = tl;
+ llci->sl = sl;
+ llci->tl_slot_id = -1;
+ llci->state = EN50221_STDCAM_CAM_NONE;
+ return &llci->stdcam;
+}
+
+static void en50221_stdcam_llci_dvbtime(struct en50221_stdcam *stdcam, time_t dvbtime)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) stdcam;
+
+ llci->datetime_dvbtime = dvbtime;
+}
+
+static void en50221_stdcam_llci_destroy(struct en50221_stdcam *stdcam, int closefd)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) stdcam;
+
+ // "remove" the cam
+ llci_cam_removed(llci);
+
+ // destroy resources
+ if (llci->rm_resource)
+ en50221_app_rm_destroy(llci->rm_resource);
+ if (llci->datetime_resource)
+ en50221_app_datetime_destroy(llci->datetime_resource);
+ if (llci->stdcam.ai_resource)
+ en50221_app_ai_destroy(llci->stdcam.ai_resource);
+ if (llci->stdcam.ca_resource)
+ en50221_app_ca_destroy(llci->stdcam.ca_resource);
+ if (llci->stdcam.mmi_resource)
+ en50221_app_mmi_destroy(llci->stdcam.mmi_resource);
+
+ if (closefd)
+ close(llci->cafd);
+
+ free(llci);
+}
+
+
+
+
+static enum en50221_stdcam_status en50221_stdcam_llci_poll(struct en50221_stdcam *stdcam)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) stdcam;
+
+ switch(dvbca_get_cam_state(llci->cafd, llci->slotnum)) {
+ case DVBCA_CAMSTATE_MISSING:
+ if (llci->state != EN50221_STDCAM_CAM_NONE)
+ llci_cam_removed(llci);
+ break;
+
+ case DVBCA_CAMSTATE_READY:
+ if (llci->state == EN50221_STDCAM_CAM_NONE)
+ llci_cam_added(llci);
+ else if (llci->state == EN50221_STDCAM_CAM_INRESET)
+ llci_cam_in_reset(llci);
+ break;
+ }
+
+ // poll the stack
+ int error;
+ if ((error = en50221_tl_poll(llci->tl)) != 0) {
+ print(LOG_LEVEL, ERROR, 1, "Error reported by stack:%i\n", en50221_tl_get_error(llci->tl));
+ }
+
+ // send date/time response
+ if (llci->datetime_session_number != -1) {
+ time_t cur_time = time(NULL);
+ if (llci->datetime_response_interval && (cur_time > llci->datetime_next_send)) {
+ en50221_app_datetime_send(llci->datetime_resource,
+ llci->datetime_session_number,
+ llci->datetime_dvbtime, 0);
+ llci->datetime_next_send = cur_time + llci->datetime_response_interval;
+ }
+ }
+
+ return llci->state;
+}
+
+static void llci_cam_added(struct en50221_stdcam_llci *llci)
+{
+ // clear down any old structures
+ if (llci->tl_slot_id != -1) {
+ llci_cam_removed(llci);
+ }
+
+ // reset the CAM
+ dvbca_reset(llci->cafd, llci->slotnum);
+ llci->state = EN50221_STDCAM_CAM_INRESET;
+}
+
+static void llci_cam_in_reset(struct en50221_stdcam_llci *llci)
+{
+ if (dvbca_get_cam_state(llci->cafd, llci->slotnum) != DVBCA_CAMSTATE_READY) {
+ return;
+ }
+
+ // register the slot
+ if ((llci->tl_slot_id = en50221_tl_register_slot(llci->tl, llci->cafd, llci->slotnum,
+ LLCI_RESPONSE_TIMEOUT_MS, LLCI_POLL_DELAY_MS)) < 0) {
+ llci->state = EN50221_STDCAM_CAM_BAD;
+ return;
+ }
+
+ // create a new connection on the slot
+ if (en50221_tl_new_tc(llci->tl, llci->tl_slot_id) < 0) {
+ llci->state = EN50221_STDCAM_CAM_BAD;
+ llci->tl_slot_id = -1;
+ en50221_tl_destroy_slot(llci->tl, llci->tl_slot_id);
+ return;
+ }
+
+ llci->state = EN50221_STDCAM_CAM_OK;
+}
+
+static void llci_cam_removed(struct en50221_stdcam_llci *llci)
+{
+ if (llci->tl_slot_id != -1) {
+ en50221_tl_destroy_slot(llci->tl, llci->tl_slot_id);
+ llci->tl_slot_id = -1;
+ llci->datetime_session_number = -1;
+ llci->stdcam.ai_session_number = -1;
+ llci->stdcam.ca_session_number = -1;
+ llci->stdcam.mmi_session_number = -1;
+ }
+ llci->state = EN50221_STDCAM_CAM_NONE;
+}
+
+
+
+static int llci_lookup_callback(void *arg, uint8_t _slot_id, uint32_t requested_resource_id,
+ en50221_sl_resource_callback *callback_out, void **arg_out,
+ uint32_t *connected_resource_id)
+{
+ struct en50221_app_public_resource_id resid;
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+
+ // decode the resource id
+ if (!en50221_app_decode_public_resource_id(&resid, requested_resource_id)) {
+ return -1;
+ }
+
+ // try and find an instance of the resource
+ uint32_t i;
+ for(i=0; i<RESOURCE_IDS_COUNT; i++) {
+ if ((resid.resource_class == llci->resources[i].resid.resource_class) &&
+ (resid.resource_type == llci->resources[i].resid.resource_type)) {
+
+ // limit sessions to certain resources
+ switch(requested_resource_id) {
+ case EN50221_APP_DATETIME_RESOURCEID:
+ if (llci->datetime_session_number != -1)
+ return -3;
+ break;
+ case EN50221_APP_AI_RESOURCEID:
+ if (llci->stdcam.ai_session_number != -1)
+ return -3;
+ break;
+ case EN50221_APP_CA_RESOURCEID:
+ if (llci->stdcam.ca_session_number != -1)
+ return -3;
+ break;
+ case EN50221_APP_MMI_RESOURCEID:
+ if (llci->stdcam.mmi_session_number != -1)
+ return -3;
+ break;
+ }
+
+ // resource is ok.
+ *callback_out = llci->resources[i].callback;
+ *arg_out = llci->resources[i].arg;
+ *connected_resource_id = llci->resources[i].binary_resource_id;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int llci_session_callback(void *arg, int reason, uint8_t _slot_id, uint16_t session_number, uint32_t resource_id)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+
+ switch(reason) {
+ case S_SCALLBACK_REASON_CAMCONNECTED:
+ if (resource_id == EN50221_APP_RM_RESOURCEID) {
+ en50221_app_rm_enq(llci->rm_resource, session_number);
+ } else if (resource_id == EN50221_APP_DATETIME_RESOURCEID) {
+ llci->datetime_session_number = session_number;
+ } else if (resource_id == EN50221_APP_AI_RESOURCEID) {
+ en50221_app_ai_enquiry(llci->stdcam.ai_resource, session_number);
+ llci->stdcam.ai_session_number = session_number;
+ } else if (resource_id == EN50221_APP_CA_RESOURCEID) {
+ en50221_app_ca_info_enq(llci->stdcam.ca_resource, session_number);
+ llci->stdcam.ca_session_number = session_number;
+ } else if (resource_id == EN50221_APP_MMI_RESOURCEID) {
+ llci->stdcam.mmi_session_number = session_number;
+ }
+
+ break;
+ case S_SCALLBACK_REASON_CLOSE:
+ if (resource_id == EN50221_APP_MMI_RESOURCEID) {
+ llci->stdcam.mmi_session_number = -1;
+ }
+
+ break;
+ }
+ return 0;
+}
+
+static int llci_rm_enq_callback(void *arg, uint8_t _slot_id, uint16_t session_number)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+
+ if (en50221_app_rm_reply(llci->rm_resource, session_number, RESOURCE_IDS_COUNT, resource_ids)) {
+ print(LOG_LEVEL, ERROR, 1, "Failed to send RM ENQ on slot %02x\n", _slot_id);
+ }
+ return 0;
+}
+
+static int llci_rm_reply_callback(void *arg, uint8_t _slot_id, uint16_t session_number, uint32_t resource_id_count, uint32_t *_resource_ids)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+ (void) resource_id_count;
+ (void) _resource_ids;
+
+ if (en50221_app_rm_changed(llci->rm_resource, session_number)) {
+ print(LOG_LEVEL, ERROR, 1, "Failed to send RM REPLY on slot %02x\n", _slot_id);
+ }
+ return 0;
+}
+
+static int llci_rm_changed_callback(void *arg, uint8_t _slot_id, uint16_t session_number)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+
+ if (en50221_app_rm_enq(llci->rm_resource, session_number)) {
+ print(LOG_LEVEL, ERROR, 1, "Failed to send RM CHANGED on slot %02x\n", _slot_id);
+ }
+ return 0;
+}
+
+static int llci_datetime_enquiry_callback(void *arg, uint8_t _slot_id, uint16_t session_number, uint8_t response_interval)
+{
+ struct en50221_stdcam_llci *llci = (struct en50221_stdcam_llci *) arg;
+ (void) _slot_id;
+
+ llci->datetime_response_interval = response_interval;
+ llci->datetime_next_send = 0;
+ if (response_interval) {
+ llci->datetime_next_send = time(NULL) + response_interval;
+ }
+ en50221_app_datetime_send(llci->datetime_resource, session_number, llci->datetime_dvbtime, 0);
+
+ return 0;
+}
diff --git a/lib/libdvben50221/en50221_transport.c b/lib/libdvben50221/en50221_transport.c
new file mode 100644
index 0000000..f6f46db
--- /dev/null
+++ b/lib/libdvben50221/en50221_transport.c
@@ -0,0 +1,1296 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 transport layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <time.h>
+#include <libdvbmisc/dvbmisc.h>
+#include <libdvbapi/dvbca.h>
+#include "en50221_errno.h"
+#include "en50221_transport.h"
+#include "asn_1.h"
+
+// these are the Transport Tags, like
+// described in EN50221, Annex A.4.1.13 (pg70)
+#define T_SB 0x80 // sb primitive h<--m
+#define T_RCV 0x81 // receive primitive h-->m
+#define T_CREATE_T_C 0x82 // create transport connection primitive h-->m
+#define T_C_T_C_REPLY 0x83 // ctc reply primitive h<--m
+#define T_DELETE_T_C 0x84 // delete tc primitive h<->m
+#define T_D_T_C_REPLY 0x85 // dtc reply primitive h<->m
+#define T_REQUEST_T_C 0x86 // request transport connection primitive h<--m
+#define T_NEW_T_C 0x87 // new tc / reply to t_request primitive h-->m
+#define T_T_C_ERROR 0x77 // error creating tc primitive h-->m
+#define T_DATA_LAST 0xA0 // convey data from higher constructed h<->m
+ // layers
+#define T_DATA_MORE 0xA1 // convey data from higher constructed h<->m
+ // layers
+
+struct en50221_message {
+ struct en50221_message *next;
+ uint32_t length;
+ uint8_t data[0];
+};
+
+struct en50221_connection {
+ uint32_t state; // the current state: idle/in_delete/in_create/active
+ struct timeval tx_time; // time last request was sent from host->module, or 0 if ok
+ struct timeval last_poll_time; // time of last poll transmission
+ uint8_t *chain_buffer; // used to save parts of chained packets
+ uint32_t buffer_length;
+
+ struct en50221_message *send_queue;
+ struct en50221_message *send_queue_tail;
+};
+
+struct en50221_slot {
+ int ca_hndl;
+ uint8_t slot; // CAM slot
+ struct en50221_connection *connections;
+
+ pthread_mutex_t slot_lock;
+
+ uint32_t response_timeout;
+ uint32_t poll_delay;
+};
+
+struct en50221_transport_layer {
+ uint8_t max_slots;
+ uint8_t max_connections_per_slot;
+ struct en50221_slot *slots;
+ struct pollfd *slot_pollfds;
+ int slots_changed;
+
+ pthread_mutex_t global_lock;
+ pthread_mutex_t setcallback_lock;
+
+ int error;
+ int error_slot;
+
+ en50221_tl_callback callback;
+ void *callback_arg;
+};
+
+static int en50221_tl_process_data(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t * data,
+ uint32_t data_length);
+static int en50221_tl_poll_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id);
+static int en50221_tl_alloc_new_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id);
+static void queue_message(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ struct en50221_message *msg);
+static int en50221_tl_handle_create_tc_reply(struct en50221_transport_layer
+ *tl, uint8_t slot_id,
+ uint8_t connection_id);
+static int en50221_tl_handle_delete_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id);
+static int en50221_tl_handle_delete_tc_reply(struct en50221_transport_layer
+ *tl, uint8_t slot_id,
+ uint8_t connection_id);
+static int en50221_tl_handle_request_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id);
+static int en50221_tl_handle_data_more(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_tl_handle_data_last(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ uint8_t * data,
+ uint32_t data_length);
+static int en50221_tl_handle_sb(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ uint8_t * data, uint32_t data_length);
+
+
+struct en50221_transport_layer *en50221_tl_create(uint8_t max_slots,
+ uint8_t
+ max_connections_per_slot)
+{
+ struct en50221_transport_layer *tl = NULL;
+ int i;
+ int j;
+
+ // setup structure
+ tl = (struct en50221_transport_layer *)
+ malloc(sizeof(struct en50221_transport_layer));
+ if (tl == NULL)
+ goto error_exit;
+ tl->max_slots = max_slots;
+ tl->max_connections_per_slot = max_connections_per_slot;
+ tl->slots = NULL;
+ tl->slot_pollfds = NULL;
+ tl->slots_changed = 1;
+ tl->callback = NULL;
+ tl->callback_arg = NULL;
+ tl->error_slot = 0;
+ tl->error = 0;
+ pthread_mutex_init(&tl->global_lock, NULL);
+ pthread_mutex_init(&tl->setcallback_lock, NULL);
+
+ // create the slots
+ tl->slots = malloc(sizeof(struct en50221_slot) * max_slots);
+ if (tl->slots == NULL)
+ goto error_exit;
+
+ // set them up
+ for (i = 0; i < max_slots; i++) {
+ tl->slots[i].ca_hndl = -1;
+
+ // create the connections for this slot
+ tl->slots[i].connections =
+ malloc(sizeof(struct en50221_connection) * max_connections_per_slot);
+ if (tl->slots[i].connections == NULL)
+ goto error_exit;
+
+ // create a mutex for the slot
+ pthread_mutex_init(&tl->slots[i].slot_lock, NULL);
+
+ // set them up
+ for (j = 0; j < max_connections_per_slot; j++) {
+ tl->slots[i].connections[j].state = T_STATE_IDLE;
+ tl->slots[i].connections[j].tx_time.tv_sec = 0;
+ tl->slots[i].connections[j].last_poll_time.tv_sec = 0;
+ tl->slots[i].connections[j].last_poll_time.tv_usec = 0;
+ tl->slots[i].connections[j].chain_buffer = NULL;
+ tl->slots[i].connections[j].buffer_length = 0;
+ tl->slots[i].connections[j].send_queue = NULL;
+ tl->slots[i].connections[j].send_queue_tail = NULL;
+ }
+ }
+
+ // create the pollfds
+ tl->slot_pollfds = malloc(sizeof(struct pollfd) * max_slots);
+ if (tl->slot_pollfds == NULL) {
+ goto error_exit;
+ }
+ memset(tl->slot_pollfds, 0, sizeof(struct pollfd) * max_slots);
+
+ return tl;
+
+ error_exit:
+ en50221_tl_destroy(tl);
+ return NULL;
+}
+
+// Destroy an instance of the transport layer
+void en50221_tl_destroy(struct en50221_transport_layer *tl)
+{
+ int i, j;
+
+ if (tl) {
+ if (tl->slots) {
+ for (i = 0; i < tl->max_slots; i++) {
+ if (tl->slots[i].connections) {
+ for (j = 0; j < tl->max_connections_per_slot; j++) {
+ if (tl->slots[i].connections[j].chain_buffer) {
+ free(tl->slots[i].connections[j].chain_buffer);
+ }
+
+ struct en50221_message *cur_msg =
+ tl->slots[i].connections[j].send_queue;
+ while (cur_msg) {
+ struct en50221_message *next_msg = cur_msg->next;
+ free(cur_msg);
+ cur_msg = next_msg;
+ }
+ tl->slots[i].connections[j].send_queue = NULL;
+ tl->slots[i].connections[j].send_queue_tail = NULL;
+ }
+ free(tl->slots[i].connections);
+ pthread_mutex_destroy(&tl->slots[i].slot_lock);
+ }
+ }
+ free(tl->slots);
+ }
+ if (tl->slot_pollfds) {
+ free(tl->slot_pollfds);
+ }
+ pthread_mutex_destroy(&tl->setcallback_lock);
+ pthread_mutex_destroy(&tl->global_lock);
+ free(tl);
+ }
+}
+
+// this can be called from the user-space app to
+// register new slots that we should work with
+int en50221_tl_register_slot(struct en50221_transport_layer *tl,
+ int ca_hndl, uint8_t slot,
+ uint32_t response_timeout,
+ uint32_t poll_delay)
+{
+ // lock
+ pthread_mutex_lock(&tl->global_lock);
+
+ // we browse through the array of slots
+ // to look for the first unused one
+ int i;
+ int16_t slot_id = -1;
+ for (i = 0; i < tl->max_slots; i++) {
+ if (tl->slots[i].ca_hndl == -1) {
+ slot_id = i;
+ break;
+ }
+ }
+ if (slot_id == -1) {
+ tl->error = EN50221ERR_OUTOFSLOTS;
+ pthread_mutex_unlock(&tl->global_lock);
+ return -1;
+ }
+ // set up the slot struct
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ tl->slots[slot_id].ca_hndl = ca_hndl;
+ tl->slots[slot_id].slot = slot;
+ tl->slots[slot_id].response_timeout = response_timeout;
+ tl->slots[slot_id].poll_delay = poll_delay;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+
+ tl->slots_changed = 1;
+ pthread_mutex_unlock(&tl->global_lock);
+ return slot_id;
+}
+
+void en50221_tl_destroy_slot(struct en50221_transport_layer *tl,
+ uint8_t slot_id)
+{
+ int i;
+
+ if (slot_id >= tl->max_slots)
+ return;
+
+ // lock
+ pthread_mutex_lock(&tl->global_lock);
+
+ // clear the slot
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ tl->slots[slot_id].ca_hndl = -1;
+ for (i = 0; i < tl->max_connections_per_slot; i++) {
+ tl->slots[slot_id].connections[i].state = T_STATE_IDLE;
+ tl->slots[slot_id].connections[i].tx_time.tv_sec = 0;
+ tl->slots[slot_id].connections[i].last_poll_time.tv_sec = 0;
+ tl->slots[slot_id].connections[i].last_poll_time.tv_usec = 0;
+ if (tl->slots[slot_id].connections[i].chain_buffer) {
+ free(tl->slots[slot_id].connections[i].
+ chain_buffer);
+ }
+ tl->slots[slot_id].connections[i].chain_buffer = NULL;
+ tl->slots[slot_id].connections[i].buffer_length = 0;
+
+ struct en50221_message *cur_msg =
+ tl->slots[slot_id].connections[i].send_queue;
+ while (cur_msg) {
+ struct en50221_message *next_msg = cur_msg->next;
+ free(cur_msg);
+ cur_msg = next_msg;
+ }
+ tl->slots[slot_id].connections[i].send_queue = NULL;
+ tl->slots[slot_id].connections[i].send_queue_tail = NULL;
+ }
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+
+ // tell upper layers
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, T_CALLBACK_REASON_SLOTCLOSE, NULL, 0, slot_id, 0);
+
+ tl->slots_changed = 1;
+ pthread_mutex_unlock(&tl->global_lock);
+}
+
+int en50221_tl_poll(struct en50221_transport_layer *tl)
+{
+ uint8_t data[4096];
+ int slot_id;
+ int j;
+
+ // make up pollfds if the slots have changed
+ pthread_mutex_lock(&tl->global_lock);
+ if (tl->slots_changed) {
+ for (slot_id = 0; slot_id < tl->max_slots; slot_id++) {
+ if (tl->slots[slot_id].ca_hndl != -1) {
+ tl->slot_pollfds[slot_id].fd = tl->slots[slot_id].ca_hndl;
+ tl->slot_pollfds[slot_id].events = POLLIN | POLLPRI | POLLERR;
+ tl->slot_pollfds[slot_id].revents = 0;
+ } else {
+ tl->slot_pollfds[slot_id].fd = 0;
+ tl->slot_pollfds[slot_id].events = 0;
+ tl->slot_pollfds[slot_id].revents = 0;
+ }
+ }
+ tl->slots_changed = 0;
+ }
+ pthread_mutex_unlock(&tl->global_lock);
+
+ // anything happened?
+ if (poll(tl->slot_pollfds, tl->max_slots, 10) < 0) {
+ tl->error_slot = -1;
+ tl->error = EN50221ERR_CAREAD;
+ return -1;
+ }
+ // go through all slots (even though poll may not have reported any events
+ for (slot_id = 0; slot_id < tl->max_slots; slot_id++) {
+
+ // check if this slot is still used and get its handle
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ continue;
+ }
+ int ca_hndl = tl->slots[slot_id].ca_hndl;
+
+ if (tl->slot_pollfds[slot_id].revents & (POLLPRI | POLLIN)) {
+ // read data
+ uint8_t r_slot_id;
+ uint8_t connection_id;
+ int readcnt = dvbca_link_read(ca_hndl, &r_slot_id,
+ &connection_id,
+ data, sizeof(data));
+ if (readcnt < 0) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAREAD;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // process it if we got some
+ if (readcnt > 0) {
+ if (tl->slots[slot_id].slot != r_slot_id) {
+ // this message is for an other CAM of the same CA
+ int new_slot_id;
+ for (new_slot_id = 0; new_slot_id < tl->max_slots; new_slot_id++) {
+ if ((tl->slots[new_slot_id].ca_hndl == ca_hndl) &&
+ (tl->slots[new_slot_id].slot == r_slot_id))
+ break;
+ }
+ if (new_slot_id != tl->max_slots) {
+ // we found the requested CAM
+ pthread_mutex_lock(&tl->slots[new_slot_id].slot_lock);
+ if (en50221_tl_process_data(tl, new_slot_id, data, readcnt)) {
+ pthread_mutex_unlock(&tl->slots[new_slot_id].slot_lock);
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ pthread_mutex_unlock(&tl->slots[new_slot_id].slot_lock);
+ } else {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ } else
+ if (en50221_tl_process_data(tl, slot_id, data, readcnt)) {
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ }
+ } else if (tl->slot_pollfds[slot_id].revents & POLLERR) {
+ // an error was reported
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAREAD;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // poll the connections on this slot + check for timeouts
+ for (j = 0; j < tl->max_connections_per_slot; j++) {
+ // ignore connection if idle
+ if (tl->slots[slot_id].connections[j].state == T_STATE_IDLE) {
+ continue;
+ }
+ // send queued data
+ if (tl->slots[slot_id].connections[j].state &
+ (T_STATE_IN_CREATION | T_STATE_ACTIVE | T_STATE_ACTIVE_DELETEQUEUED)) {
+ // send data if there is some to go and we're not waiting for a response already
+ if (tl->slots[slot_id].connections[j].send_queue &&
+ (tl->slots[slot_id].connections[j].tx_time.tv_sec == 0)) {
+
+ // get the message
+ struct en50221_message *msg =
+ tl->slots[slot_id].connections[j].send_queue;
+ if (msg->next != NULL) {
+ tl->slots[slot_id].connections[j].send_queue = msg->next;
+ } else {
+ tl->slots[slot_id].connections[j].send_queue = NULL;
+ tl->slots[slot_id].connections[j].send_queue_tail = NULL;
+ }
+
+ // send the message
+ if (dvbca_link_write(tl->slots[slot_id].ca_hndl,
+ tl->slots[slot_id].slot,
+ j,
+ msg->data, msg->length) < 0) {
+ free(msg);
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ print(LOG_LEVEL, ERROR, 1, "CAWrite failed");
+ return -1;
+ }
+ gettimeofday(&tl->slots[slot_id].connections[j].tx_time, 0);
+
+ // fixup connection state for T_DELETE_T_C
+ if (msg->length && (msg->data[0] == T_DELETE_T_C)) {
+ tl->slots[slot_id].connections[j].state = T_STATE_IN_DELETION;
+ if (tl->slots[slot_id].connections[j].chain_buffer) {
+ free(tl->slots[slot_id].connections[j].chain_buffer);
+ }
+ tl->slots[slot_id].connections[j].chain_buffer = NULL;
+ tl->slots[slot_id].connections[j].buffer_length = 0;
+ }
+
+ free(msg);
+ }
+ }
+ // poll it if we're not expecting a reponse and the poll time has elapsed
+ if (tl->slots[slot_id].connections[j].state & T_STATE_ACTIVE) {
+ if ((tl->slots[slot_id].connections[j].tx_time.tv_sec == 0) &&
+ (time_after(tl->slots[slot_id].connections[j].last_poll_time,
+ tl->slots[slot_id].poll_delay))) {
+
+ gettimeofday(&tl->slots[slot_id].connections[j].last_poll_time, 0);
+ if (en50221_tl_poll_tc(tl, slot_id, j)) {
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ }
+ }
+
+ // check for timeouts - in any state
+ if (tl->slots[slot_id].connections[j].tx_time.tv_sec &&
+ (time_after(tl->slots[slot_id].connections[j].tx_time,
+ tl->slots[slot_id].response_timeout))) {
+
+ if (tl->slots[slot_id].connections[j].state &
+ (T_STATE_IN_CREATION |T_STATE_IN_DELETION)) {
+ tl->slots[slot_id].connections[j].state = T_STATE_IDLE;
+ } else if (tl->slots[slot_id].connections[j].state &
+ (T_STATE_ACTIVE | T_STATE_ACTIVE_DELETEQUEUED)) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_TIMEOUT;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ }
+ }
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ }
+
+ return 0;
+}
+
+void en50221_tl_register_callback(struct en50221_transport_layer *tl,
+ en50221_tl_callback callback, void *arg)
+{
+ pthread_mutex_lock(&tl->setcallback_lock);
+ tl->callback = callback;
+ tl->callback_arg = arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+}
+
+int en50221_tl_get_error_slot(struct en50221_transport_layer *tl)
+{
+ return tl->error_slot;
+}
+
+int en50221_tl_get_error(struct en50221_transport_layer *tl)
+{
+ return tl->error;
+}
+
+int en50221_tl_send_data(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ uint8_t * data, uint32_t data_size)
+{
+#ifdef DEBUG_TXDATA
+ printf("[[[[[[[[[[[[[[[[[[[[\n");
+ uint32_t ii = 0;
+ for (ii = 0; ii < data_size; ii++) {
+ printf("%02x: %02x\n", ii, data[ii]);
+ }
+ printf("]]]]]]]]]]]]]]]]]]]]\n");
+#endif
+
+ if (slot_id >= tl->max_slots) {
+ tl->error = EN50221ERR_BADSLOTID;
+ return -1;
+ }
+
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (connection_id >= tl->max_connections_per_slot) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (tl->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) {
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // allocate msg structure
+ struct en50221_message *msg =
+ malloc(sizeof(struct en50221_message) + data_size + 10);
+ if (msg == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // make up data to send
+ int length_field_len;
+ msg->data[0] = T_DATA_LAST;
+ if ((length_field_len = asn_1_encode(data_size + 1, msg->data + 1, 3)) < 0) {
+ free(msg);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_ASNENCODE;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ msg->data[1 + length_field_len] = connection_id;
+ memcpy(msg->data + 1 + length_field_len + 1, data, data_size);
+ msg->length = 1 + length_field_len + 1 + data_size;
+
+ // queue it for transmission
+ queue_message(tl, slot_id, connection_id, msg);
+
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return 0;
+}
+
+int en50221_tl_send_datav(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ struct iovec *vector, int iov_count)
+{
+#ifdef DEBUG_TXDATA
+ printf("[[[[[[[[[[[[[[[[[[[[\n");
+ uint32_t ii = 0;
+ uint32_t iipos = 0;
+ for (ii = 0; ii < (uint32_t) iov_count; ii++) {
+ uint32_t jj;
+ for (jj = 0; jj < vector[ii].iov_len; jj++) {
+ printf("%02x: %02x\n", jj + iipos,
+ *((uint8_t *) (vector[ii].iov_base) + jj));
+ }
+ iipos += vector[ii].iov_len;
+ }
+ printf("]]]]]]]]]]]]]]]]]]]]\n");
+#endif
+
+ if (slot_id >= tl->max_slots) {
+ tl->error = EN50221ERR_BADSLOTID;
+ return -1;
+ }
+
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (connection_id >= tl->max_connections_per_slot) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (tl->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) {
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // calculate the total length of the data to send
+ uint32_t data_size = 0;
+ int i;
+ for (i = 0; i < iov_count; i++) {
+ data_size += vector[i].iov_len;
+ }
+
+ // allocate msg structure
+ struct en50221_message *msg =
+ malloc(sizeof(struct en50221_message) + data_size + 10);
+ if (msg == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // make up data to send
+ int length_field_len;
+ msg->data[0] = T_DATA_LAST;
+ if ((length_field_len = asn_1_encode(data_size + 1, msg->data + 1, 3)) < 0) {
+ free(msg);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_ASNENCODE;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ msg->data[1 + length_field_len] = connection_id;
+ msg->length = 1 + length_field_len + 1 + data_size;
+ msg->next = NULL;
+
+ // merge the iovecs
+ uint32_t pos = 1 + length_field_len + 1;
+ for (i = 0; i < iov_count; i++) {
+ memcpy(msg->data + pos, vector[i].iov_base,
+ vector[i].iov_len);
+ pos += vector[i].iov_len;
+ }
+
+ // queue it for transmission
+ queue_message(tl, slot_id, connection_id, msg);
+
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return 0;
+}
+
+int en50221_tl_new_tc(struct en50221_transport_layer *tl, uint8_t slot_id)
+{
+ // check
+ if (slot_id >= tl->max_slots) {
+ tl->error = EN50221ERR_BADSLOTID;
+ return -1;
+ }
+
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // allocate a new connection if possible
+ int conid = en50221_tl_alloc_new_tc(tl, slot_id);
+ if (conid == -1) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFCONNECTIONS;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // allocate msg structure
+ struct en50221_message *msg =
+ malloc(sizeof(struct en50221_message) + 3);
+ if (msg == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // make up the data to send
+ msg->data[0] = T_CREATE_T_C;
+ msg->data[1] = 1;
+ msg->data[2] = conid;
+ msg->length = 3;
+ msg->next = NULL;
+
+ // queue it for transmission
+ queue_message(tl, slot_id, conid, msg);
+
+ // done
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return conid;
+}
+
+int en50221_tl_del_tc(struct en50221_transport_layer *tl, uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // check
+ if (slot_id >= tl->max_slots) {
+ tl->error = EN50221ERR_BADSLOTID;
+ return -1;
+ }
+
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (connection_id >= tl->max_connections_per_slot) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (!(tl->slots[slot_id].connections[connection_id].state &
+ (T_STATE_ACTIVE | T_STATE_IN_DELETION))) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADSTATE;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // allocate msg structure
+ struct en50221_message *msg =
+ malloc(sizeof(struct en50221_message) + 3);
+ if (msg == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ // make up the data to send
+ msg->data[0] = T_DELETE_T_C;
+ msg->data[1] = 1;
+ msg->data[2] = connection_id;
+ msg->length = 3;
+ msg->next = NULL;
+
+ // queue it for transmission
+ queue_message(tl, slot_id, connection_id, msg);
+ tl->slots[slot_id].connections[connection_id].state =
+ T_STATE_ACTIVE_DELETEQUEUED;
+
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return 0;
+}
+
+int en50221_tl_get_connection_state(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id)
+{
+ if (slot_id >= tl->max_slots) {
+ tl->error = EN50221ERR_BADSLOTID;
+ return -1;
+ }
+
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ if (tl->slots[slot_id].ca_hndl == -1) {
+ tl->error = EN50221ERR_BADSLOTID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ if (connection_id >= tl->max_connections_per_slot) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+ return -1;
+ }
+ int state = tl->slots[slot_id].connections[connection_id].state;
+ pthread_mutex_unlock(&tl->slots[slot_id].slot_lock);
+
+ return state;
+}
+
+
+
+
+// ask the module for new data
+static int en50221_tl_poll_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id)
+{
+ gettimeofday(&tl->slots[slot_id].connections[connection_id].
+ tx_time, 0);
+
+ // send command
+ uint8_t hdr[3];
+ hdr[0] = T_DATA_LAST;
+ hdr[1] = 1;
+ hdr[2] = connection_id;
+ if (dvbca_link_write(tl->slots[slot_id].ca_hndl,
+ tl->slots[slot_id].slot,
+ connection_id, hdr, 3) < 0) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ return 0;
+}
+
+// handle incoming data
+static int en50221_tl_process_data(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t * data,
+ uint32_t data_length)
+{
+ int result;
+
+#ifdef DEBUG_RXDATA
+ printf("-------------------\n");
+ uint32_t ii = 0;
+ for (ii = 0; ii < data_length; ii++) {
+ printf("%02x: %02x\n", ii, data[ii]);
+ }
+ printf("+++++++++++++++++++\n");
+#endif
+
+ // process the received data
+ while (data_length) {
+ // parse the header
+ uint8_t tpdu_tag = data[0];
+ uint16_t asn_data_length;
+ int length_field_len;
+ if ((length_field_len = asn_1_decode(&asn_data_length, data + 1, data_length - 1)) < 0) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid asn from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ if ((asn_data_length < 1) ||
+ (asn_data_length > (data_length - (1 + length_field_len)))) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received data with invalid length from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ uint8_t connection_id = data[1 + length_field_len];
+ data += 1 + length_field_len + 1;
+ data_length -= (1 + length_field_len + 1);
+ asn_data_length--;
+
+ // check the connection_id
+ if (connection_id >= tl->max_connections_per_slot) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received bad connection id %02x from module on slot %02x\n",
+ connection_id, slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCONNECTIONID;
+ return -1;
+ }
+ // process the TPDUs
+ switch (tpdu_tag) {
+ case T_C_T_C_REPLY:
+ if ((result = en50221_tl_handle_create_tc_reply(tl, slot_id, connection_id)) < 0) {
+ return -1;
+ }
+ break;
+ case T_DELETE_T_C:
+ if ((result = en50221_tl_handle_delete_tc(tl, slot_id, connection_id)) < 0) {
+ return -1;
+ }
+ break;
+ case T_D_T_C_REPLY:
+ if ((result = en50221_tl_handle_delete_tc_reply(tl, slot_id, connection_id)) < 0) {
+ return -1;
+ }
+ break;
+ case T_REQUEST_T_C:
+ if ((result = en50221_tl_handle_request_tc(tl, slot_id, connection_id)) < 0) {
+ return -1;
+ }
+ break;
+ case T_DATA_MORE:
+ if ((result = en50221_tl_handle_data_more(tl, slot_id,
+ connection_id,
+ data,
+ asn_data_length)) < 0) {
+ return -1;
+ }
+ break;
+ case T_DATA_LAST:
+ if ((result = en50221_tl_handle_data_last(tl, slot_id,
+ connection_id,
+ data,
+ asn_data_length)) < 0) {
+ return -1;
+ }
+ break;
+ case T_SB:
+ if ((result = en50221_tl_handle_sb(tl, slot_id,
+ connection_id,
+ data,
+ asn_data_length)) < 0) {
+ return -1;
+ }
+ break;
+ default:
+ print(LOG_LEVEL, ERROR, 1,
+ "Recieved unexpected TPDU tag %02x from module on slot %02x\n",
+ tpdu_tag, slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+
+ // skip over the consumed data
+ data += asn_data_length;
+ data_length -= asn_data_length;
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_create_tc_reply(struct en50221_transport_layer
+ *tl, uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // set this connection to state active
+ if (tl->slots[slot_id].connections[connection_id].state == T_STATE_IN_CREATION) {
+ tl->slots[slot_id].connections[connection_id].state = T_STATE_ACTIVE;
+ tl->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0;
+
+ // tell upper layers
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, T_CALLBACK_REASON_CONNECTIONOPEN, NULL, 0, slot_id, connection_id);
+ } else {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_C_T_C_REPLY for connection not in "
+ "T_STATE_IN_CREATION from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_delete_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // immediately delete this connection and send D_T_C_REPLY
+ if (tl->slots[slot_id].connections[connection_id].state &
+ (T_STATE_ACTIVE | T_STATE_IN_DELETION)) {
+ // clear down the slot
+ tl->slots[slot_id].connections[connection_id].state = T_STATE_IDLE;
+ if (tl->slots[slot_id].connections[connection_id].chain_buffer) {
+ free(tl->slots[slot_id].connections[connection_id].chain_buffer);
+ }
+ tl->slots[slot_id].connections[connection_id].chain_buffer = NULL;
+ tl->slots[slot_id].connections[connection_id].buffer_length = 0;
+
+ // send the reply
+ uint8_t hdr[3];
+ hdr[0] = T_D_T_C_REPLY;
+ hdr[1] = 1;
+ hdr[2] = connection_id;
+ if (dvbca_link_write(tl->slots[slot_id].ca_hndl,
+ tl->slots[slot_id].slot,
+ connection_id, hdr, 3) < 0) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ // tell upper layers
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, T_CALLBACK_REASON_CONNECTIONCLOSE, NULL, 0, slot_id, connection_id);
+ } else {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_DELETE_T_C for inactive connection from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_delete_tc_reply(struct en50221_transport_layer
+ *tl, uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // delete this connection, should be in T_STATE_IN_DELETION already
+ if (tl->slots[slot_id].connections[connection_id].state == T_STATE_IN_DELETION) {
+ tl->slots[slot_id].connections[connection_id].state = T_STATE_IDLE;
+ } else {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_D_T_C_REPLY received for connection not in "
+ "T_STATE_IN_DELETION from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_request_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id)
+{
+ // allocate a new connection if possible
+ int conid = en50221_tl_alloc_new_tc(tl, slot_id);
+ int ca_hndl = tl->slots[slot_id].ca_hndl;
+ if (conid == -1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Too many connections requested by module on slot %02x\n",
+ slot_id);
+
+ // send the error
+ uint8_t hdr[4];
+ hdr[0] = T_T_C_ERROR;
+ hdr[1] = 2;
+ hdr[2] = connection_id;
+ hdr[3] = 1;
+ if (dvbca_link_write(ca_hndl, tl->slots[slot_id].slot, connection_id, hdr, 4) < 0) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ tl->slots[slot_id].connections[connection_id].tx_time.
+ tv_sec = 0;
+ } else {
+ // send the NEW_T_C on the connection we received it on
+ uint8_t hdr[4];
+ hdr[0] = T_NEW_T_C;
+ hdr[1] = 2;
+ hdr[2] = connection_id;
+ hdr[3] = conid;
+ if (dvbca_link_write(ca_hndl, tl->slots[slot_id].slot, connection_id, hdr, 4) < 0) {
+ tl->slots[slot_id].connections[conid].state = T_STATE_IDLE;
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ tl->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0;
+
+ // send the CREATE_T_C on the new connnection
+ hdr[0] = T_CREATE_T_C;
+ hdr[1] = 1;
+ hdr[2] = conid;
+ if (dvbca_link_write(ca_hndl, tl->slots[slot_id].slot, conid, hdr, 3) < 0) {
+ tl->slots[slot_id].connections[conid].state = T_STATE_IDLE;
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ gettimeofday(&tl->slots[slot_id].connections[conid].tx_time, 0);
+
+ // tell upper layers
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+ if (cb)
+ cb(cb_arg, T_CALLBACK_REASON_CAMCONNECTIONOPEN, NULL, 0, slot_id, conid);
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_data_more(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // connection in correct state?
+ if (tl->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_DATA_MORE for connection not in "
+ "T_STATE_ACTIVE from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ // a chained data packet is coming in, save
+ // it to the buffer and wait for more
+ tl->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0;
+ int new_data_length =
+ tl->slots[slot_id].connections[connection_id].buffer_length + data_length;
+ uint8_t *new_data_buffer =
+ realloc(tl->slots[slot_id].connections[connection_id].chain_buffer, new_data_length);
+ if (new_data_buffer == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ return -1;
+ }
+ tl->slots[slot_id].connections[connection_id].chain_buffer = new_data_buffer;
+
+ memcpy(tl->slots[slot_id].connections[connection_id].chain_buffer +
+ tl->slots[slot_id].connections[connection_id].buffer_length,
+ data, data_length);
+ tl->slots[slot_id].connections[connection_id].buffer_length = new_data_length;
+
+ return 0;
+}
+
+static int en50221_tl_handle_data_last(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ uint8_t * data,
+ uint32_t data_length)
+{
+ // connection in correct state?
+ if (tl->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_DATA_LAST received for connection not in "
+ "T_STATE_ACTIVE from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ // last package of a chain or single package comes in
+ tl->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0;
+ if (tl->slots[slot_id].connections[connection_id].chain_buffer == NULL) {
+ // single package => dispatch immediately
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+
+ if (cb && data_length) {
+ pthread_mutex_unlock(&tl->slots[slot_id].
+ slot_lock);
+ cb(cb_arg, T_CALLBACK_REASON_DATA, data, data_length, slot_id, connection_id);
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ }
+ } else {
+ int new_data_length =
+ tl->slots[slot_id].connections[connection_id].buffer_length + data_length;
+ uint8_t *new_data_buffer =
+ realloc(tl->slots[slot_id].connections[connection_id].chain_buffer, new_data_length);
+ if (new_data_buffer == NULL) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_OUTOFMEMORY;
+ return -1;
+ }
+
+ memcpy(new_data_buffer +
+ tl->slots[slot_id].connections[connection_id].
+ buffer_length, data, data_length);
+
+ // clean the buffer position
+ tl->slots[slot_id].connections[connection_id].chain_buffer = NULL;
+ tl->slots[slot_id].connections[connection_id].buffer_length = 0;
+
+ // tell the upper layers
+ pthread_mutex_lock(&tl->setcallback_lock);
+ en50221_tl_callback cb = tl->callback;
+ void *cb_arg = tl->callback_arg;
+ pthread_mutex_unlock(&tl->setcallback_lock);
+ if (cb && data_length) {
+ pthread_mutex_unlock(&tl->slots[slot_id].
+ slot_lock);
+ cb(cb_arg, T_CALLBACK_REASON_DATA, new_data_buffer,
+ new_data_length, slot_id, connection_id);
+ pthread_mutex_lock(&tl->slots[slot_id].slot_lock);
+ }
+
+ free(new_data_buffer);
+ }
+
+ return 0;
+}
+
+static int en50221_tl_handle_sb(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ uint8_t * data, uint32_t data_length)
+{
+ // is the connection id ok?
+ if (tl->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Received T_SB for connection not in T_STATE_ACTIVE from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ // did we get enough data in the T_SB?
+ if (data_length != 1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "Recieved T_SB with invalid length from module on slot %02x\n",
+ slot_id);
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_BADCAMDATA;
+ return -1;
+ }
+ // tell it to send the data if it says there is some
+ if (data[0] & 0x80) {
+ int ca_hndl = tl->slots[slot_id].ca_hndl;
+
+ // send the RCV
+ uint8_t hdr[3];
+ hdr[0] = T_RCV;
+ hdr[1] = 1;
+ hdr[2] = connection_id;
+ if (dvbca_link_write(ca_hndl, tl->slots[slot_id].slot, connection_id, hdr, 3) < 0) {
+ tl->error_slot = slot_id;
+ tl->error = EN50221ERR_CAWRITE;
+ return -1;
+ }
+ gettimeofday(&tl->slots[slot_id].connections[connection_id].tx_time, 0);
+
+ } else {
+ // no data - indicate not waiting for anything now
+ tl->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0;
+ }
+
+ return 0;
+}
+
+static int en50221_tl_alloc_new_tc(struct en50221_transport_layer *tl,
+ uint8_t slot_id)
+{
+ // we browse through the array of connection
+ // types, to look for the first unused one
+ int i, conid = -1;
+ for (i = 1; i < tl->max_connections_per_slot; i++) {
+ if (tl->slots[slot_id].connections[i].state == T_STATE_IDLE) {
+ conid = i;
+ break;
+ }
+ }
+ if (conid == -1) {
+ print(LOG_LEVEL, ERROR, 1,
+ "CREATE_T_C failed: no more connections available\n");
+ return -1;
+ }
+ // set up the connection struct
+ tl->slots[slot_id].connections[conid].state = T_STATE_IN_CREATION;
+ tl->slots[slot_id].connections[conid].chain_buffer = NULL;
+ tl->slots[slot_id].connections[conid].buffer_length = 0;
+
+ return conid;
+}
+
+static void queue_message(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ struct en50221_message *msg)
+{
+ msg->next = NULL;
+ if (tl->slots[slot_id].connections[connection_id].send_queue_tail) {
+ tl->slots[slot_id].connections[connection_id].send_queue_tail->next = msg;
+ tl->slots[slot_id].connections[connection_id].send_queue_tail = msg;
+ } else {
+ tl->slots[slot_id].connections[connection_id].send_queue = msg;
+ tl->slots[slot_id].connections[connection_id].send_queue_tail = msg;
+ }
+}
diff --git a/lib/libdvben50221/en50221_transport.h b/lib/libdvben50221/en50221_transport.h
new file mode 100644
index 0000000..7882060
--- /dev/null
+++ b/lib/libdvben50221/en50221_transport.h
@@ -0,0 +1,234 @@
+/*
+ en50221 encoder An implementation for libdvb
+ an implementation for the en50221 session layer
+
+ Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
+ Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
+ Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#ifndef __EN50221_TRANSPORT_H__
+#define __EN50221_TRANSPORT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/uio.h>
+
+/**
+ * Callback reasons.
+ */
+#define T_CALLBACK_REASON_CONNECTIONOPEN 0x00 // A connection we opened _to_ the cam has been ACKed
+#define T_CALLBACK_REASON_CAMCONNECTIONOPEN 0x01 // The cam has opened a connection to _us_.
+#define T_CALLBACK_REASON_DATA 0x02 // Data received
+#define T_CALLBACK_REASON_CONNECTIONCLOSE 0x03 // The cam has told us to close a connection.
+#define T_CALLBACK_REASON_SLOTCLOSE 0x04 // The cam in the supplied slot id has been removed.
+
+// these are the states a TC can be in
+#define T_STATE_IDLE 0x01 // this transport connection is not in use
+#define T_STATE_ACTIVE 0x02 // this transport connection is in use
+#define T_STATE_ACTIVE_DELETEQUEUED 0x04 // this transport connection is about to be deleted
+#define T_STATE_IN_CREATION 0x08 // this transport waits for a T_C_T_C_REPLY to become active
+#define T_STATE_IN_DELETION 0x10 // this transport waits for T_D_T_C_REPLY to become idle again
+
+/**
+ * Opaque type representing a transport layer.
+ */
+struct en50221_transport_layer;
+
+/**
+ * Type definition for callback function - used when events are received from a module.
+ *
+ * **IMPORTANT** For all callback reasons except T_CALLBACK_REASON_DATA, an internal lock is held in the
+ * transport layer. Therefore, to avoid deadlock, you *must not* call back into the transport layer for
+ * these reasons.
+ *
+ * However, for T_CALLBACK_REASON_DATA, the internal lock is not held, so calling back into the transport
+ * layer is fine in this case.
+ *
+ * @param arg Private data.
+ * @param reason One of the T_CALLBACK_REASON_* values.
+ * @param data The data.
+ * @param data_length Length of the data.
+ * @param slot_id Slot_id the data was received from.
+ * @param connection_id Connection_id the data was received from.
+ */
+typedef void (*en50221_tl_callback) (void *arg, int reason,
+ uint8_t * data,
+ uint32_t data_length,
+ uint8_t slot_id,
+ uint8_t connection_id);
+
+
+/**
+ * Construct a new instance of the transport layer.
+ *
+ * @param max_slots Maximum number of slots to support.
+ * @param max_connections_per_slot Maximum connections per slot.
+ * @return The en50221_transport_layer instance, or NULL on error.
+ */
+extern struct en50221_transport_layer *en50221_tl_create(uint8_t max_slots,
+ uint8_t max_connections_per_slot);
+
+/**
+ * Destroy an instance of the transport layer.
+ *
+ * @param tl The en50221_transport_layer instance.
+ */
+extern void en50221_tl_destroy(struct en50221_transport_layer *tl);
+
+/**
+ * Register a new slot with the library.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param ca_hndl FD for talking to the slot.
+ * @param slot CAM slot where the requested CAM of the CA is in.
+ * @param response_timeout Maximum timeout in ms to a response we send before signalling a timeout.
+ * @param poll_delay Interval between polls in ms.
+ * @return slot_id on sucess, or -1 on error.
+ */
+extern int en50221_tl_register_slot(struct en50221_transport_layer *tl,
+ int ca_hndl, uint8_t slot,
+ uint32_t response_timeout,
+ uint32_t poll_delay);
+
+/**
+ * Destroy a registered slot - e.g. if a CAM is removed, or an error occurs. Does
+ * not attempt to reset the CAM.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id Slot to destroy.
+ */
+extern void en50221_tl_destroy_slot(struct en50221_transport_layer *tl, uint8_t slot_id);
+
+/**
+ * Performs one iteration of the transport layer poll -
+ * checking for incoming data furthermore it will handle
+ * the timeouts of certain commands like T_DELETE_T_C it
+ * should be called by the application regularly, generally
+ * faster than the poll delay.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @return 0 on succes, or -1 if there was an error of some sort.
+ */
+extern int en50221_tl_poll(struct en50221_transport_layer *tl);
+
+/**
+ * Register the callback for data reception.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param callback The callback. Set to NULL to remove the callback completely.
+ * @param arg Private data passed as arg0 of the callback.
+ */
+extern void en50221_tl_register_callback(struct en50221_transport_layer *tl,
+ en50221_tl_callback callback, void *arg);
+
+/**
+ * Gets the ID of the slot an error occurred on.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @return The offending slot id.
+ */
+extern int en50221_tl_get_error_slot(struct en50221_transport_layer *tl);
+
+/**
+ * Gets the last error.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @return One of the EN50221ERR_* values.
+ */
+extern int en50221_tl_get_error(struct en50221_transport_layer *tl);
+
+/**
+ * This function is used to take a data-block, pack into
+ * into a TPDU (DATA_LAST) and send it to the device
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id ID of the slot.
+ * @param connection_id Connection id.
+ * @param data Data to send.
+ * @param data_length Number of bytes to send.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_tl_send_data(struct en50221_transport_layer *tl,
+ uint8_t slot_id,
+ uint8_t connection_id,
+ uint8_t * data,
+ uint32_t data_length);
+
+/**
+ * This function is used to take a data-block, pack into
+ * into a TPDU (DATA_LAST) and send it to the device
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id ID of the slot.
+ * @param connection_id Connection id.
+ * @param vector iov to send.
+ * @param io_count Number of elements in vector.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_tl_send_datav(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id,
+ struct iovec *vector, int iov_count);
+
+/**
+ * Create a new transport connection to the cam.
+ *
+ * **IMPORTANT** When this function returns, it means the request to create a connection
+ * has been submitted. You will need to poll using en50221_tl_get_connection_state() to find out
+ * if/when the connection is established. A callback with T_CALLBACK_REASON_CONNECTIONOPEN reason
+ * will also be sent when it is acked by the CAM.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id ID of the slot.
+ * @return The allocated connection id on success, or -1 on error.
+ */
+extern int en50221_tl_new_tc(struct en50221_transport_layer *tl, uint8_t slot_id);
+
+/**
+ * Deallocates a transport connection.
+ *
+ * **IMPORTANT** When this function returns, it means the request to destroy a connection
+ * has been submitted. You will need to poll using en50221_tl_get_connection_state() to find out
+ * if/when the connection is destroyed.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id ID of the slot.
+ * @param connection_id Connection id to send the request _on_.
+ * @return 0 on success, or -1 on error.
+ */
+extern int en50221_tl_del_tc(struct en50221_transport_layer *tl, uint8_t slot_id, uint8_t connection_id);
+
+/**
+ * Checks the state of a connection.
+ *
+ * @param tl The en50221_transport_layer instance.
+ * @param slot_id ID of the slot.
+ * @param connection_id Connection id to send the request _on_.
+ * @return One of the T_STATE_* values.
+ */
+extern int en50221_tl_get_connection_state(struct en50221_transport_layer *tl,
+ uint8_t slot_id, uint8_t connection_id);
+
+#ifdef __cplusplus
+}
+#endif
+#endif