diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-10 00:59:09 +0000 |
commit | 7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b (patch) | |
tree | b5e07812e82f00c780d2c035dca102b8e364f447 /kaffeine/src/input/dvb/lib/libdvben50221 | |
download | kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.tar.gz kaffeine-7f66b9e8ba186fb14a2db598a87dfa8de7b5a47b.zip |
Added old abandoned KDE3 version of Kaffeine
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kaffeine@1088031 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kaffeine/src/input/dvb/lib/libdvben50221')
33 files changed, 9383 insertions, 0 deletions
diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/Makefile.am b/kaffeine/src/input/dvb/lib/libdvben50221/Makefile.am new file mode 100644 index 0000000..7c9764a --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/Makefile.am @@ -0,0 +1,21 @@ +noinst_LTLIBRARIES = libdvben50221.la + +INCLUDES = -I$(top_srcdir)/kaffeine/src/input/dvb/lib + +libdvben50221_la_SOURCES = asn_1.c \ + en50221_app_ai.c \ + en50221_app_auth.c \ + en50221_app_ca.c \ + en50221_app_datetime.c \ + en50221_app_dvb.c \ + en50221_app_epg.c \ + en50221_app_lowspeed.c \ + en50221_app_mmi.c \ + en50221_app_rm.c \ + en50221_app_smartcard.c \ + en50221_app_teletext.c \ + en50221_app_utils.c \ + en50221_session.c \ + en50221_transport.c + +CFLAGS = -g -O2 -DLOG_LEVEL=1 -Wall -Wshadow -Wpointer-arith -Wstrict-prototypes -fPIC diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/asn_1.c b/kaffeine/src/input/dvb/lib/libdvben50221/asn_1.c new file mode 100644 index 0000000..8cc528f --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/asn_1.c @@ -0,0 +1,81 @@ +/* + ASN.1 routines, implementation for libdvben50221 + an implementation for the High Level Common Interface + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/kaffeine/src/input/dvb/lib/libdvben50221/asn_1.h b/kaffeine/src/input/dvb/lib/libdvben50221/asn_1.h new file mode 100644 index 0000000..00d636b --- /dev/null +++ b/kaffeine/src/input/dvb/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 (manu@kromtek.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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.c new file mode 100644 index 0000000..6facef0 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.c @@ -0,0 +1,185 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + +en50221_app_ai en50221_app_ai_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_ai_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_ai_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_ai_destroy(en50221_app_ai ai) +{ + struct en50221_app_ai_private *private = (struct en50221_app_ai_private *) ai; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_ai_register_callback(en50221_app_ai ai, en50221_app_ai_callback callback, void *arg) +{ + struct en50221_app_ai_private *private = (struct en50221_app_ai_private *) ai; + + pthread_mutex_lock(&private->lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_ai_enquiry(en50221_app_ai ai, uint16_t session_number) +{ + struct en50221_app_ai_private *private = (struct en50221_app_ai_private *) ai; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 4); +} + +int en50221_app_ai_entermenu(en50221_app_ai ai, uint16_t session_number) +{ + struct en50221_app_ai_private *private = (struct en50221_app_ai_private *) ai; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 4); +} + +int en50221_app_ai_message(en50221_app_ai ai, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_ai_private *private = (struct en50221_app_ai_private *) ai; + (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(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_ai_parse_app_info(struct en50221_app_ai_private *private, + 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(&private->lock); + en50221_app_ai_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.h new file mode 100644 index 0000000..4def877 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ai.h @@ -0,0 +1,129 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_ai; + +/** + * Create an instance of an application information resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.c new file mode 100644 index 0000000..b062e3e --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.c @@ -0,0 +1,179 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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 *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + +en50221_app_auth en50221_app_auth_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_auth_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_auth_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_auth_destroy(en50221_app_auth auth) +{ + struct en50221_app_auth_private *private = (struct en50221_app_auth_private *) auth; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_auth_register_request_callback(en50221_app_auth auth, + en50221_app_auth_request_callback callback, void *arg) +{ + struct en50221_app_auth_private *private = (struct en50221_app_auth_private *) auth; + + pthread_mutex_lock(&private->lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_auth_send(en50221_app_auth auth, + uint16_t session_number, + uint16_t auth_protocol_id, uint8_t *auth_data, + uint32_t auth_data_length) +{ + struct en50221_app_auth_private *private = (struct en50221_app_auth_private *) auth; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, 2); +} + +int en50221_app_auth_message(en50221_app_auth auth, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_auth_private *private = (struct en50221_app_auth_private *) auth; + (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(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_auth_parse_request(struct en50221_app_auth_private *private, + 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(&private->lock); + en50221_app_auth_request_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.h new file mode 100644 index 0000000..30d9c55 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_auth.h @@ -0,0 +1,119 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_auth; + +/** + * Create an instance of the auth resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.c new file mode 100644 index 0000000..a1206fb --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.c @@ -0,0 +1,615 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + + +en50221_app_ca en50221_app_ca_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_ca_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_ca_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->ca_info_callback = NULL; + private->ca_pmt_reply_callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_ca_destroy(en50221_app_ca ca) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_ca_register_info_callback(en50221_app_ca ca, + en50221_app_ca_info_callback callback, void *arg) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + + pthread_mutex_lock(&private->lock); + private->ca_info_callback = callback; + private->ca_info_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_ca_register_pmt_reply_callback(en50221_app_ca ca, + en50221_app_ca_pmt_reply_callback callback, void *arg) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + + pthread_mutex_lock(&private->lock); + private->ca_pmt_reply_callback = callback; + private->ca_pmt_reply_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_ca_info_enq(en50221_app_ca ca, + uint16_t session_number) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 4); +} + +int en50221_app_ca_pmt(en50221_app_ca ca, + uint16_t session_number, + uint8_t *ca_pmt, + uint32_t ca_pmt_length) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, 2); +} + +int en50221_app_ca_message(en50221_app_ca ca, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_ca_private *private = (struct en50221_app_ca_private *) ca; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_CA_PMT_REPLY: + return en50221_app_ca_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; +} + +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; + struct ca_pmt_descriptor *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; + struct ca_pmt_descriptor *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_private *private, + 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(&private->lock); + en50221_app_ca_info_callback cb = private->ca_info_callback; + void *cb_arg = private->ca_info_callback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_ca_pmt_reply_callback cb = private->ca_pmt_reply_callback; + void *cb_arg = private->ca_pmt_reply_callback_arg; + pthread_mutex_unlock(&private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.h new file mode 100644 index 0000000..ab29649 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_ca.h @@ -0,0 +1,254 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_ca; + +/** + * Create an instance of the ca resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.c new file mode 100644 index 0000000..5686443 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.c @@ -0,0 +1,169 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + + +en50221_app_datetime en50221_app_datetime_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_datetime_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_datetime_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_datetime_destroy(en50221_app_datetime datetime) +{ + struct en50221_app_datetime_private *private = (struct en50221_app_datetime_private *) datetime; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_datetime_register_enquiry_callback(en50221_app_datetime datetime, + en50221_app_datetime_enquiry_callback callback, void *arg) +{ + struct en50221_app_datetime_private *private = (struct en50221_app_datetime_private *) datetime; + + pthread_mutex_lock(&private->lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_datetime_send(en50221_app_datetime datetime, + uint16_t session_number, + time_t utc_time, + int time_offset) +{ + struct en50221_app_datetime_private *private = (struct en50221_app_datetime_private *) datetime; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, data_length); +} + +int en50221_app_datetime_message(en50221_app_datetime datetime, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_datetime_private *private = (struct en50221_app_datetime_private *) datetime; + (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(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_datetime_parse_enquiry(struct en50221_app_datetime_private *private, + 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(&private->lock); + en50221_app_datetime_enquiry_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number, response_interval); + } + return 0; +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.h new file mode 100644 index 0000000..5819fb4 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_datetime.h @@ -0,0 +1,116 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_datetime; + +/** + * Create an instance of the datetime resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.c new file mode 100644 index 0000000..82da884 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.c @@ -0,0 +1,263 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + + +en50221_app_dvb en50221_app_dvb_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_dvb_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_dvb_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->tune_callback = NULL; + private->replace_callback = NULL; + private->clear_replace_callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_dvb_destroy(en50221_app_dvb dvb) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_dvb_register_tune_callback(en50221_app_dvb dvb, + en50221_app_dvb_tune_callback callback, void *arg) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + + pthread_mutex_lock(&private->lock); + private->tune_callback = callback; + private->tune_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_dvb_register_replace_callback(en50221_app_dvb dvb, + en50221_app_dvb_replace_callback callback, void *arg) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + + pthread_mutex_lock(&private->lock); + private->replace_callback = callback; + private->replace_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_dvb_register_clear_replace_callback(en50221_app_dvb dvb, + en50221_app_dvb_clear_replace_callback callback, void *arg) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + + pthread_mutex_lock(&private->lock); + private->clear_replace_callback = callback; + private->clear_replace_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_dvb_ask_release(en50221_app_dvb dvb, uint16_t session_number) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 4); +} + +int en50221_app_dvb_message(en50221_app_dvb dvb, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_dvb_private *private = (struct en50221_app_dvb_private *) dvb; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_REPLACE: + return en50221_app_dvb_parse_replace(private, slot_id, session_number, data+3, data_length-3); + case TAG_CLEAR_REPLACE: + return en50221_app_dvb_parse_clear_replace(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_dvb_parse_tune(struct en50221_app_dvb_private *private, + 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(&private->lock); + en50221_app_dvb_tune_callback cb = private->tune_callback; + void *cb_arg = private->tune_callback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_dvb_replace_callback cb = private->replace_callback; + void *cb_arg = private->replace_callback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_dvb_clear_replace_callback cb = private->clear_replace_callback; + void *cb_arg = private->clear_replace_callback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number, replacement_ref); + } + return 0; +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.h new file mode 100644 index 0000000..221f58a --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_dvb.h @@ -0,0 +1,164 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_dvb; + +/** + * Create an instance of the dvb resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.c new file mode 100644 index 0000000..dba09b0 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.c @@ -0,0 +1,166 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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 *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + + +en50221_app_epg en50221_app_epg_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_epg_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_epg_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_epg_destroy(en50221_app_epg epg) +{ + struct en50221_app_epg_private *private = (struct en50221_app_epg_private *) epg; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_epg_register_enquiry_callback(en50221_app_epg epg, + en50221_app_epg_reply_callback callback, void *arg) +{ + struct en50221_app_epg_private *private = (struct en50221_app_epg_private *) epg; + + pthread_mutex_lock(&private->lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_epg_enquire(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) +{ + struct en50221_app_epg_private *private = (struct en50221_app_epg_private *) epg; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 15); +} + +int en50221_app_epg_message(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 *private = (struct en50221_app_epg_private *) 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_private *private, + 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(&private->lock); + en50221_app_epg_reply_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number, event_status); + } + return 0; +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.h new file mode 100644 index 0000000..c3c4c59 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_epg.h @@ -0,0 +1,136 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_epg; + +/** + * Create an instance of the epg resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.c new file mode 100644 index 0000000..25d088f --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.c @@ -0,0 +1,514 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + uint8_t slot_id, uint16_t session_number, int more_last, + uint8_t *data, uint32_t data_length); + + + +en50221_app_lowspeed en50221_app_lowspeed_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_lowspeed_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_lowspeed_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->command_callback = NULL; + private->send_callback = NULL; + private->sessions = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_lowspeed_destroy(en50221_app_lowspeed lowspeed) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + + struct en50221_app_lowspeed_session *cur_s = private->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(&private->lock); + free(private); +} + +void en50221_app_lowspeed_clear_session(en50221_app_lowspeed lowspeed, uint16_t session_number) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + + pthread_mutex_lock(&private->lock); + struct en50221_app_lowspeed_session *cur_s = private->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 { + private->sessions = cur_s->next; + } + free(cur_s); + return; + } + + prev_s = cur_s; + cur_s=cur_s->next; + } + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_lowspeed_register_command_callback(en50221_app_lowspeed lowspeed, + en50221_app_lowspeed_command_callback callback, void *arg) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + + pthread_mutex_lock(&private->lock); + private->command_callback = callback; + private->command_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_lowspeed_register_send_callback(en50221_app_lowspeed lowspeed, + en50221_app_lowspeed_send_callback callback, void *arg) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + + pthread_mutex_lock(&private->lock); + private->send_callback = callback; + private->send_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_lowspeed_send_comms_reply(en50221_app_lowspeed lowspeed, + uint16_t session_number, + uint8_t comms_reply_id, + uint8_t return_value) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 6); +} + +int en50221_app_lowspeed_send_comms_data(en50221_app_lowspeed lowspeed, + uint16_t session_number, + uint8_t phase_id, + uint32_t tx_data_length, + uint8_t *tx_data) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, 2); +} + +int en50221_app_lowspeed_message(en50221_app_lowspeed lowspeed, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_lowspeed_private *private = (struct en50221_app_lowspeed_private *) lowspeed; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_COMMS_SEND_LAST: + return en50221_app_lowspeed_parse_send(private, slot_id, session_number, 1, data+3, data_length-3); + case TAG_COMMS_SEND_MORE: + return en50221_app_lowspeed_parse_send(private, 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_private *private, + 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(&private->lock); + en50221_app_lowspeed_command_callback cb = private->command_callback; + void *cb_arg = private->command_callback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + struct en50221_app_lowspeed_session *cur_s = private->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(&private->lock); + return -1; + } + cur_s->session_number = session_number; + cur_s->block_chain = NULL; + cur_s->block_length = 0; + cur_s->next = private->sessions; + private->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(&private->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(&private->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(&private->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(&private->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 = private->send_callback; + void *cb_arg = private->send_callback_arg; + pthread_mutex_unlock(&private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.h new file mode 100644 index 0000000..9f58ad9 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_lowspeed.h @@ -0,0 +1,210 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_lowspeed; + +/** + * Create an instance of the lowspeed resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.c new file mode 100644 index 0000000..ab5c6e4 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.c @@ -0,0 +1,1316 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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_private *private, + 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); + + + +en50221_app_mmi en50221_app_mmi_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_mmi_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_mmi_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->closecallback = NULL; + private->displaycontrolcallback = NULL; + private->keypadcontrolcallback = NULL; + private->subtitlesegmentcallback = NULL; + private->sceneendmarkcallback = NULL; + private->scenecontrolcallback = NULL; + private->subtitledownloadcallback = NULL; + private->flushdownloadcallback = NULL; + private->enqcallback = NULL; + private->menucallback = NULL; + private->listcallback = NULL; + private->sessions = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_mmi_destroy(en50221_app_mmi mmi) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + struct en50221_app_mmi_session *cur_s = private->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(&private->lock); + free(private); +} + +void en50221_app_mmi_clear_session(en50221_app_mmi mmi, uint16_t session_number) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + struct en50221_app_mmi_session *cur_s = private->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 { + private->sessions = cur_s->next; + } + free(cur_s); + return; + } + + prev_s = cur_s; + cur_s=cur_s->next; + } + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_close_callback(en50221_app_mmi mmi, + en50221_app_mmi_close_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->closecallback = callback; + private->closecallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_display_control_callback(en50221_app_mmi mmi, + en50221_app_mmi_display_control_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->displaycontrolcallback = callback; + private->displaycontrolcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_keypad_control_callback(en50221_app_mmi mmi, + en50221_app_mmi_keypad_control_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->keypadcontrolcallback = callback; + private->keypadcontrolcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_subtitle_segment_callback(en50221_app_mmi mmi, + en50221_app_mmi_subtitle_segment_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->subtitlesegmentcallback = callback; + private->subtitlesegmentcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_scene_end_mark_callback(en50221_app_mmi mmi, + en50221_app_mmi_scene_end_mark_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->sceneendmarkcallback = callback; + private->sceneendmarkcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_scene_control_callback(en50221_app_mmi mmi, + en50221_app_mmi_scene_control_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->scenecontrolcallback = callback; + private->scenecontrolcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_subtitle_download_callback(en50221_app_mmi mmi, + en50221_app_mmi_subtitle_download_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->subtitledownloadcallback = callback; + private->subtitledownloadcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_flush_download_callback(en50221_app_mmi mmi, + en50221_app_mmi_flush_download_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->flushdownloadcallback = callback; + private->flushdownloadcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_enq_callback(en50221_app_mmi mmi, + en50221_app_mmi_enq_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->enqcallback = callback; + private->enqcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_menu_callback(en50221_app_mmi mmi, + en50221_app_mmi_menu_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->menucallback = callback; + private->menucallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_mmi_register_list_callback(en50221_app_mmi mmi, + en50221_app_mmi_list_callback callback, void *arg) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + + pthread_mutex_lock(&private->lock); + private->listcallback = callback; + private->listcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_mmi_close(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t cmd_id, + uint8_t delay) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, data_length); +} + +int en50221_app_mmi_display_reply(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t reply_id, + struct en502221_app_mmi_display_reply_details *details) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, iov_count); +} + +int en50221_app_mmi_keypress(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t keycode) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 5); +} + +int en50221_app_mmi_display_message(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t display_message_id) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 5); +} + +int en50221_app_mmi_scene_done(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t decoder_continue, + uint8_t scene_reveal, + uint8_t scene_tag) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 5); +} + +int en50221_app_mmi_download_reply(en50221_app_mmi mmi, + uint16_t session_number, + uint16_t object_id, + uint8_t download_reply_id) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 7); +} + +int en50221_app_mmi_answ(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t answ_id, + uint8_t *text, + uint32_t text_count) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, iov_count); +} + +int en50221_app_mmi_menu_answ(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t choice_ref) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + 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 private->funcs->send_data(private->funcs->arg, session_number, data, 5); +} + +int en50221_app_mmi_message(en50221_app_mmi mmi, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_mmi_private *private = (struct en50221_app_mmi_private *) mmi; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_DISPLAY_CONTROL: + return en50221_app_mmi_parse_display_control(private, slot_id, session_number, data+3, data_length-3); + case TAG_KEYPAD_CONTROL: + return en50221_app_mmi_parse_keypad_control(private, slot_id, session_number, data+3, data_length-3); + case TAG_ENQUIRY: + return en50221_app_mmi_parse_enq(private, slot_id, session_number, data+3, data_length-3); + case TAG_MENU_LAST: + return en50221_app_mmi_parse_list_menu(private, slot_id, session_number, tag, 1, data+3, data_length-3); + case TAG_MENU_MORE: + return en50221_app_mmi_parse_list_menu(private, slot_id, session_number, tag, 0, data+3, data_length-3); + case TAG_LIST_LAST: + return en50221_app_mmi_parse_list_menu(private, slot_id, session_number, tag, 1, data+3, data_length-3); + case TAG_LIST_MORE: + return en50221_app_mmi_parse_list_menu(private, slot_id, session_number, tag, 0, data+3, data_length-3); + case TAG_SUBTITLE_SEGMENT_LAST: + return en50221_app_mmi_parse_subtitle(private, slot_id, session_number, tag, 1, data+3, data_length-3); + case TAG_SUBTITLE_SEGMENT_MORE: + return en50221_app_mmi_parse_subtitle(private, slot_id, session_number, tag, 0, data+3, data_length-3); + case TAG_SCENE_END_MARK: + return en50221_app_mmi_parse_scene_end_mark(private, slot_id, session_number, data+3, data_length-3); + case TAG_SCENE_CONTROL: + return en50221_app_mmi_parse_scene_control(private, slot_id, session_number, data+3, data_length-3); + case TAG_SUBTITLE_DOWNLOAD_LAST: + return en50221_app_mmi_parse_subtitle(private, slot_id, session_number, tag, 1, data+3, data_length-3); + case TAG_SUBTITLE_DOWNLOAD_MORE: + return en50221_app_mmi_parse_subtitle(private, slot_id, session_number, tag, 0, data+3, data_length-3); + case TAG_FLUSH_DOWNLOAD: + return en50221_app_mmi_parse_flush_download(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_mmi_parse_close(struct en50221_app_mmi_private *private, + 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(&private->lock); + en50221_app_mmi_close_callback cb = private->closecallback; + void *cb_arg = private->closecallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_display_control_callback cb = private->displaycontrolcallback; + void *cb_arg = private->displaycontrolcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_keypad_control_callback cb = private->keypadcontrolcallback; + void *cb_arg = private->keypadcontrolcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_enq_callback cb = private->enqcallback; + void *cb_arg = private->enqcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + uint8_t *outdata; + uint32_t outdata_length; + int dfstatus = en50221_app_mmi_defragment(private, session_number, tag_id, more_last, + data, asn_data_length, + &outdata, &outdata_length); + if (dfstatus <= 0) { + pthread_mutex_unlock(&private->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(&private->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(&private->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(&private->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(&private->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 = private->menucallback; + void *cb_arg = private->menucallback_arg; + pthread_mutex_unlock(&private->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 = private->listcallback; + void *cb_arg = private->listcallback_arg; + pthread_mutex_unlock(&private->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(&private->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_private *private, + 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(&private->lock); + uint8_t *outdata; + uint32_t outdata_length; + int dfstatus = en50221_app_mmi_defragment(private, session_number, tag_id, more_last, + data, asn_data_length, + &outdata, &outdata_length); + if (dfstatus <= 0) { + pthread_mutex_unlock(&private->lock); + return dfstatus; + } + + // do callback + int cbstatus = 0; + switch(tag_id) { + case TAG_SUBTITLE_SEGMENT_LAST: + { + en50221_app_mmi_subtitle_segment_callback cb = private->subtitlesegmentcallback; + void *cb_arg = private->subtitlesegmentcallback_arg; + pthread_mutex_unlock(&private->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 = private->subtitledownloadcallback; + void *cb_arg = private->subtitledownloadcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_scene_end_mark_callback cb = private->sceneendmarkcallback; + void *cb_arg = private->sceneendmarkcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_scene_control_callback cb = private->scenecontrolcallback; + void *cb_arg = private->scenecontrolcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_mmi_flush_download_callback cb = private->flushdownloadcallback; + void *cb_arg = private->flushdownloadcallback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number); + } + return 0; +} + +static int en50221_app_mmi_defragment(struct en50221_app_mmi_private *private, + 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 = private->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 = private->sessions; + private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.h new file mode 100644 index 0000000..b5e3abf --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_mmi.h @@ -0,0 +1,571 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 en502221_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. + */ +typedef void *en50221_app_mmi; + +/** + * Create an instance of the mmi resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(en50221_app_mmi mmi, + uint16_t session_number, + uint8_t reply_id, + struct en502221_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(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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.c new file mode 100644 index 0000000..7c57aba --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.c @@ -0,0 +1,294 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + +en50221_app_rm en50221_app_rm_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_rm_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_rm_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->enqcallback = NULL; + private->replycallback = NULL; + private->changedcallback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_rm_destroy(en50221_app_rm rm) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_rm_register_enq_callback(en50221_app_rm rm, + en50221_app_rm_enq_callback callback, void *arg) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + + pthread_mutex_lock(&private->lock); + private->enqcallback = callback; + private->enqcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_rm_register_reply_callback(en50221_app_rm rm, + en50221_app_rm_reply_callback callback, void *arg) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + + pthread_mutex_lock(&private->lock); + private->replycallback = callback; + private->replycallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_rm_register_changed_callback(en50221_app_rm rm, + en50221_app_rm_changed_callback callback, void *arg) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + + pthread_mutex_lock(&private->lock); + private->changedcallback = callback; + private->changedcallback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_rm_enq(en50221_app_rm rm, uint16_t session_number) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + 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 private->funcs->send_data(private->funcs->arg, session_number, buf, 4); +} + +int en50221_app_rm_reply(en50221_app_rm rm, uint16_t session_number, + uint32_t resource_id_count, + uint32_t *resource_ids) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, 2); +} + +int en50221_app_rm_changed(en50221_app_rm rm, uint16_t session_number) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + 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 private->funcs->send_data(private->funcs->arg, session_number, buf, 4); +} + +int en50221_app_rm_message(en50221_app_rm rm, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_rm_private *private = (struct en50221_app_rm_private *) rm; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_PROFILE: + return en50221_app_rm_parse_profile_reply(private, slot_id, session_number, data+3, data_length-3); + case TAG_PROFILE_CHANGE: + return en50221_app_rm_parse_profile_change(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_rm_parse_profile_enq(struct en50221_app_rm_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length) +{ + (void)data; + (void)data_length; + + pthread_mutex_lock(&private->lock); + en50221_app_rm_enq_callback cb = private->enqcallback; + void *cb_arg = private->enqcallback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_rm_reply_callback cb = private->replycallback; + void *cb_arg = private->replycallback_arg; + pthread_mutex_unlock(&private->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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length) +{ + (void)data; + (void)data_length; + + pthread_mutex_lock(&private->lock); + en50221_app_rm_changed_callback cb = private->changedcallback; + void *cb_arg = private->changedcallback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number); + } + return 0; +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.h new file mode 100644 index 0000000..696065d --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_rm.h @@ -0,0 +1,178 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_rm; + +/** + * Create an instance of the resource manager. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.c new file mode 100644 index 0000000..83015ae --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.c @@ -0,0 +1,293 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + +en50221_app_smartcard en50221_app_smartcard_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_smartcard_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_smartcard_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->command_callback = NULL; + private->send_callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_smartcard_destroy(en50221_app_smartcard smartcard) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_smartcard_register_command_callback(en50221_app_smartcard smartcard, + en50221_app_smartcard_command_callback callback, void *arg) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + + pthread_mutex_lock(&private->lock); + private->command_callback = callback; + private->command_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +void en50221_app_smartcard_register_send_callback(en50221_app_smartcard smartcard, + en50221_app_smartcard_send_callback callback, void *arg) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + + pthread_mutex_lock(&private->lock); + private->send_callback = callback; + private->send_callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_smartcard_command_reply(en50221_app_smartcard smartcard, + uint16_t session_number, + uint8_t reply_id, + uint8_t status, + uint8_t *data, + uint32_t data_length) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iovec, iov_count); +} + +int en50221_app_smartcard_receive(en50221_app_smartcard smartcard, + uint16_t session_number, + uint8_t *data, + uint32_t data_length, + uint8_t SW1, + uint8_t SW2) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + 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 private->funcs->send_datav(private->funcs->arg, session_number, iov, 3); +} + +int en50221_app_smartcard_message(en50221_app_smartcard smartcard, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_smartcard_private *private = (struct en50221_app_smartcard_private *) smartcard; + (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(private, slot_id, session_number, data+3, data_length-3); + case TAG_SMARTCARD_SEND: + return en50221_app_smartcard_parse_send(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_smartcard_parse_command(struct en50221_app_smartcard_private *private, + 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(&private->lock); + en50221_app_smartcard_command_callback cb = private->command_callback; + void *cb_arg = private->command_callback_arg; + pthread_mutex_unlock(&private->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_private *private, + 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(&private->lock); + en50221_app_smartcard_send_callback cb = private->send_callback; + void *cb_arg = private->send_callback_arg; + pthread_mutex_unlock(&private->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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.h new file mode 100644 index 0000000..56655c4 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_smartcard.h @@ -0,0 +1,191 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_smartcard; + +/** + * Create an instance of the smartcard resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_tags.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_tags.h new file mode 100644 index 0000000..2086a80 --- /dev/null +++ b/kaffeine/src/input/dvb/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 (manu@kromtek.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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.c new file mode 100644 index 0000000..cc2ee41 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.c @@ -0,0 +1,139 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private { + 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_private *private, + uint8_t slot_id, uint16_t session_number, + uint8_t *data, uint32_t data_length); + + + +en50221_app_teletext en50221_app_teletext_create(struct en50221_app_send_functions *funcs) +{ + struct en50221_app_teletext_private *private = NULL; + + // create structure and set it up + private = malloc(sizeof(struct en50221_app_teletext_private)); + if (private == NULL) { + return NULL; + } + private->funcs = funcs; + private->callback = NULL; + + pthread_mutex_init(&private->lock, NULL); + + // done + return private; +} + +void en50221_app_teletext_destroy(en50221_app_teletext teletext) +{ + struct en50221_app_teletext_private *private = (struct en50221_app_teletext_private *) teletext; + + pthread_mutex_destroy(&private->lock); + free(private); +} + +void en50221_app_teletext_register_callback(en50221_app_teletext teletext, + en50221_app_teletext_callback callback, void *arg) +{ + struct en50221_app_teletext_private *private = (struct en50221_app_teletext_private *) teletext; + + pthread_mutex_lock(&private->lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->lock); +} + +int en50221_app_teletext_message(en50221_app_teletext teletext, + uint8_t slot_id, + uint16_t session_number, + uint32_t resource_id, + uint8_t *data, uint32_t data_length) +{ + struct en50221_app_teletext_private *private = (struct en50221_app_teletext_private *) teletext; + (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(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_teletext_parse_ebu(struct en50221_app_teletext_private *private, + 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(&private->lock); + en50221_app_teletext_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->lock); + if (cb) { + return cb(cb_arg, slot_id, session_number, teletext_data, asn_data_length); + } + return 0; +} + diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.h new file mode 100644 index 0000000..1164d94 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_teletext.h @@ -0,0 +1,104 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *en50221_app_teletext; + +/** + * Create an instance of the teletext resource. + * + * @param funcs Send functions to use. + * @return Instance, or NULL on failure. + */ +extern 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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.c new file mode 100644 index 0000000..af68846 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.c @@ -0,0 +1,37 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.h new file mode 100644 index 0000000..34ca68f --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_app_utils.h @@ -0,0 +1,105 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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; +}; + +/** + * 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. + */ + int (*send_data)(void *arg, uint16_t session_number, uint8_t *data, uint16_t data_length); + + /** + * Send vector data. + */ + int (*send_datav)(void *arg, uint16_t session_number, struct iovec *vector, int iov_count); +}; + +/** + * 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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_errno.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_errno.h new file mode 100644 index 0000000..045a6a5 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_errno.h @@ -0,0 +1,52 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 session layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.c new file mode 100644 index 0000000..1cc9a2e --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.c @@ -0,0 +1,954 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private +{ + uint32_t max_sessions; + 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_private *private, + uint32_t resource_id, + uint8_t slot_id, + uint8_t connection_id, + en50221_sl_resource_callback callback, void* arg); + + + + +en50221_session_layer en50221_sl_create(en50221_transport_layer tl, + uint32_t max_sessions) +{ + struct en50221_session_layer_private *private = NULL; + uint32_t i; + + // setup structure + private = (struct en50221_session_layer_private*) malloc(sizeof(struct en50221_session_layer_private)); + if (private == NULL) + goto error_exit; + private->max_sessions = max_sessions; + private->lookup = NULL; + private->session = NULL; + private->tl = tl; + private->error = 0; + + // init the mutex + pthread_mutex_init(&private->global_lock, NULL); + pthread_mutex_init(&private->setcallback_lock, NULL); + + // create the slots + private->sessions = malloc(sizeof(struct en50221_session) * max_sessions); + if (private->sessions == NULL) + goto error_exit; + + // set them up + for(i=0; i< max_sessions; i++) { + private->sessions[i].state = S_STATE_IDLE; + private->sessions[i].callback = NULL; + + pthread_mutex_init(&private->sessions[i].session_lock, NULL); + } + + // register ourselves with the transport layer + en50221_tl_register_callback(tl, en50221_sl_transport_callback, private); + + return private; + +error_exit: + en50221_sl_destroy(private); + return NULL; +} + +void en50221_sl_destroy(en50221_session_layer sl) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + uint32_t i; + + if (private) { + if (private->sessions) { + for(i=0; i< private->max_sessions; i++) { + pthread_mutex_destroy(&private->sessions[i].session_lock); + } + free(private->sessions); + } + + pthread_mutex_destroy(&private->setcallback_lock); + pthread_mutex_destroy(&private->global_lock); + + free(private); + } +} + +int en50221_sl_get_error(en50221_session_layer tl) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) tl; + return private->error; +} + +void en50221_sl_register_lookup_callback(en50221_session_layer sl, en50221_sl_lookup_callback callback, void *arg) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + pthread_mutex_lock(&private->setcallback_lock); + private->lookup = callback; + private->lookup_arg = arg; + pthread_mutex_unlock(&private->setcallback_lock); +} + +void en50221_sl_register_session_callback(en50221_session_layer sl, + en50221_sl_session_callback callback, void *arg) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + pthread_mutex_lock(&private->setcallback_lock); + private->session = callback; + private->session_arg = arg; + pthread_mutex_unlock(&private->setcallback_lock); +} + +int en50221_sl_create_session(en50221_session_layer sl, int slot_id, uint8_t connection_id, uint32_t resource_id, + en50221_sl_resource_callback callback, void* arg) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + // lookup next free session_id: + pthread_mutex_lock(&private->global_lock); + int session_number = en50221_sl_alloc_new_session(private, resource_id, slot_id, connection_id, callback, arg); + if (session_number == -1) { + pthread_mutex_unlock(&private->global_lock); + return -1; + } + pthread_mutex_unlock(&private->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(private->tl, slot_id, connection_id, hdr, 8)) { + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (private->sessions[session_number].state == S_STATE_IN_CREATION) { + private->sessions[session_number].state = S_STATE_IDLE; + } + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + + private->error = en50221_tl_get_error(private->tl); + return -1; + } + + // ok. + return session_number; +} + +int en50221_sl_destroy_session(en50221_session_layer sl, uint16_t session_number) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + if (session_number >= private->max_sessions) { + private->error = EN50221ERR_BADSESSIONNUMBER; + return -1; + } + + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (!(private->sessions[session_number].state & (S_STATE_ACTIVE|S_STATE_IN_DELETION))) { + private->error = EN50221ERR_BADSESSIONNUMBER; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return -1; + } + + // set the state + private->sessions[session_number].state = S_STATE_IN_DELETION; + + // get essential details + uint8_t slot_id = private->sessions[session_number].slot_id; + uint8_t connection_id = private->sessions[session_number].connection_id; + pthread_mutex_unlock(&private->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(private->tl, slot_id, connection_id, hdr, 4)) { + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (private->sessions[session_number].state == S_STATE_IN_DELETION) { + private->sessions[session_number].state = S_STATE_IDLE; + } + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + + private->error = en50221_tl_get_error(private->tl); + return -1; + } + + return 0; +} + +int en50221_sl_send_data(en50221_session_layer sl, uint16_t session_number, uint8_t *data, uint16_t data_length) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + if (session_number >= private->max_sessions) { + private->error = EN50221ERR_BADSESSIONNUMBER; + return -1; + } + + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (private->sessions[session_number].state != S_STATE_ACTIVE) { + private->error = EN50221ERR_BADSESSIONNUMBER; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return -1; + } + + // get essential details + uint8_t slot_id = private->sessions[session_number].slot_id; + uint8_t connection_id = private->sessions[session_number].connection_id; + pthread_mutex_unlock(&private->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(private->tl, slot_id, connection_id, iov, 2)) { + private->error = en50221_tl_get_error(private->tl); + return -1; + } + + return 0; +} + +int en50221_sl_send_datav(en50221_session_layer sl, uint16_t session_number, + struct iovec *vector, int iov_count) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + + if (session_number >= private->max_sessions) { + private->error = EN50221ERR_BADSESSIONNUMBER; + return -1; + } + + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (private->sessions[session_number].state != S_STATE_ACTIVE) { + private->error = EN50221ERR_BADSESSIONNUMBER; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return -1; + } + if (iov_count > 9) { + private->error = EN50221ERR_IOVLIMIT; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return -1; + } + uint8_t slot_id = private->sessions[session_number].slot_id; + uint8_t connection_id = private->sessions[session_number].connection_id; + pthread_mutex_unlock(&private->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(private->tl, slot_id, connection_id, out_iov, iov_count+1)) { + private->error = en50221_tl_get_error(private->tl); + return -1; + } + return 0; +} + +int en50221_sl_broadcast_data(en50221_session_layer sl, int slot_id, uint32_t resource_id, + uint8_t *data, uint16_t data_length) +{ + struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl; + uint32_t i; + + for(i = 0; i < private->max_sessions; i++) + { + pthread_mutex_lock(&private->sessions[i].session_lock); + + if (private->sessions[i].state != S_STATE_ACTIVE) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + if ((slot_id != -1) && (slot_id != private->sessions[i].slot_id)) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + + if (private->sessions[i].resource_id == resource_id) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + en50221_sl_send_data(sl, i, data, data_length); + } else { + pthread_mutex_unlock(&private->sessions[i].session_lock); + } + } + + return 0; +} + + + +static void en50221_sl_handle_open_session_request(struct en50221_session_layer_private *private, + 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(&private->setcallback_lock); + en50221_sl_lookup_callback lcb = private->lookup; + void *lcb_arg = private->lookup_arg; + pthread_mutex_unlock(&private->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(&private->global_lock); + session_number = en50221_sl_alloc_new_session(private, connected_resource_id, slot_id, connection_id, + resource_callback, resource_arg); + pthread_mutex_unlock(&private->global_lock); + + if (session_number == -1) { + status = S_STATUS_CLOSE_NO_RES; + } else { + // inform upper layers/ check availability + pthread_mutex_lock(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->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(private->tl, slot_id, connection_id, hdr, 9)) { + print(LOG_LEVEL, ERROR, 1, "Transport layer error %i occurred\n", en50221_tl_get_error(private->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(&private->sessions[session_number].session_lock); + if (status != S_STATUS_OPEN) { + private->sessions[session_number].state = S_STATE_IDLE; + } else { + private->sessions[session_number].state = S_STATE_ACTIVE; + } + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + + // tell upper layers + if (private->sessions[session_number].state == S_STATE_ACTIVE) { + pthread_mutex_lock(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->setcallback_lock); + + if (status == S_STATUS_OPEN) { + if (cb) + cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTED, slot_id, session_number, connected_resource_id); + } else { + private->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_private *private, + 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 >= private->max_sessions) { + code = 0xF0; // session close error + print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id); + } else { + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (slot_id != private->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 != private->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 (!(private->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) { + private->sessions[session_number].state = S_STATE_IDLE; + code = 0x00; // close ok + } + resource_id = private->sessions[session_number].resource_id; + pthread_mutex_unlock(&private->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(private->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(private->tl), slot_id); + } + + // callback to announce destruction to resource if it was ok + if (code == 0x00) { + pthread_mutex_lock(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->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_private *private, + 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 >= private->max_sessions) { + print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id); + return; + } + + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (slot_id != private->sessions[session_number].slot_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (connection_id != private->sessions[session_number].connection_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (private->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(&private->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]); + private->sessions[session_number].state = S_STATE_IDLE; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + + // inform upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->setcallback_lock); + if (cb) + cb(cb_arg, S_SCALLBACK_REASON_CONNECTFAIL, slot_id, session_number, + private->sessions[session_number].resource_id); + return; + } + + // set it active + private->sessions[session_number].state = S_STATE_ACTIVE; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + + // inform upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->setcallback_lock); + if (cb) + cb(cb_arg, S_SCALLBACK_REASON_CONNECTED, slot_id, session_number, + private->sessions[session_number].resource_id); +} + +static void en50221_sl_handle_close_session_response(struct en50221_session_layer_private *private, + 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 >= private->max_sessions) { + print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id); + return; + } + + pthread_mutex_lock(&private->sessions[session_number].session_lock); + if (slot_id != private->sessions[session_number].slot_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (connection_id != private->sessions[session_number].connection_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (private->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(&private->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 + private->sessions[session_number].state = S_STATE_IDLE; + pthread_mutex_unlock(&private->sessions[session_number].session_lock); +} + +static void en50221_sl_handle_session_package(struct en50221_session_layer_private *private, + 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 >= private->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(&private->sessions[session_number].session_lock); + if (slot_id != private->sessions[session_number].slot_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (connection_id != private->sessions[session_number].connection_id) { + print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id); + pthread_mutex_unlock(&private->sessions[session_number].session_lock); + return; + } + if (private->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(&private->sessions[session_number].session_lock); + return; + } + + en50221_sl_resource_callback cb = private->sessions[session_number].callback; + void *cb_arg = private->sessions[session_number].callback_arg; + uint32_t resource_id = private->sessions[session_number].resource_id; + pthread_mutex_unlock(&private->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_private *private = (struct en50221_session_layer_private *) 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(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->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(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->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(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->setcallback_lock); + + for(i=0; i< private->max_sessions; i++) { + pthread_mutex_lock(&private->sessions[i].session_lock); + + if (private->sessions[i].state == S_STATE_IDLE) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + if (private->sessions[i].connection_id != connection_id) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + + private->sessions[i].state = S_STATE_IDLE; + + uint8_t slot_id = private->sessions[i].slot_id; + uint32_t resource_id = private->sessions[i].resource_id; + pthread_mutex_unlock(&private->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(&private->setcallback_lock); + en50221_sl_session_callback cb = private->session; + void *cb_arg = private->session_arg; + pthread_mutex_unlock(&private->setcallback_lock); + + for(i=0; i< private->max_sessions; i++) { + pthread_mutex_lock(&private->sessions[i].session_lock); + + if (private->sessions[i].state == S_STATE_IDLE) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + if (private->sessions[i].slot_id != slot_id) { + pthread_mutex_unlock(&private->sessions[i].session_lock); + continue; + } + private->sessions[i].state = S_STATE_IDLE; + + uint32_t resource_id = private->sessions[i].resource_id; + pthread_mutex_unlock(&private->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(private, data+1, data_length-1, slot_id, connection_id); + break; + + case ST_CLOSE_SESSION_REQ: + en50221_sl_handle_close_session_request(private, data+1, data_length-1, slot_id, connection_id); + break; + + case ST_SESSION_NUMBER: + en50221_sl_handle_session_package(private, data+1, data_length-1, slot_id, connection_id); + break; + + case ST_CREATE_SESSION_RES: + en50221_sl_handle_create_session_response(private, data+1, data_length-1, slot_id, connection_id); + break; + + case ST_CLOSE_SESSION_RES: + en50221_sl_handle_close_session_response(private, 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_private *private, + 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 < private->max_sessions; i++) { + if (private->sessions[i].state == S_STATE_IDLE) { + session_number = i; + break; + } + } + if (session_number == -1) { + private->error = EN50221ERR_OUTOFSESSIONS; + return -1; + } + + // setup the session + private->sessions[session_number].state = S_STATE_IN_CREATION; + private->sessions[session_number].resource_id = resource_id; + private->sessions[session_number].slot_id = slot_id; + private->sessions[session_number].connection_id = connection_id; + private->sessions[session_number].callback = callback; + private->sessions[session_number].callback_arg = arg; + + // ok + return session_number; +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.h new file mode 100644 index 0000000..c7cc6a8 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.h @@ -0,0 +1,212 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 session layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *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 en50221_session_layer en50221_sl_create(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(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(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(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(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(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(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(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(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(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/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.c b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.c new file mode 100644 index 0000000..ff7c936 --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.c @@ -0,0 +1,1240 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 transport layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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_private +{ + 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_private *tl, uint8_t slot_id, + uint8_t *data, uint32_t data_length); +static int en50221_tl_poll_tc(struct en50221_transport_layer_private *tl, uint8_t slot_id, uint8_t connection_id); +static int en50221_tl_alloc_new_tc(struct en50221_transport_layer_private *tl, uint8_t slot_id); +static void queue_message(struct en50221_transport_layer_private *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_private *private, + uint8_t slot_id, uint8_t connection_id); +static int en50221_tl_handle_delete_tc(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id); +static int en50221_tl_handle_delete_tc_reply(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id); +static int en50221_tl_handle_request_tc(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id); +static int en50221_tl_handle_data_more(struct en50221_transport_layer_private *private, + 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_private *private, + 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_private *private, + uint8_t slot_id, uint8_t connection_id, + uint8_t *data, uint32_t data_length); + + +en50221_transport_layer en50221_tl_create(uint8_t max_slots, uint8_t max_connections_per_slot) +{ + struct en50221_transport_layer_private *private = NULL; + int i; + int j; + + // setup structure + private = (struct en50221_transport_layer_private*) malloc(sizeof(struct en50221_transport_layer_private)); + if (private == NULL) + goto error_exit; + private->max_slots = max_slots; + private->max_connections_per_slot = max_connections_per_slot; + private->slots = NULL; + private->slot_pollfds = NULL; + private->slots_changed = 1; + private->callback = NULL; + private->callback_arg = NULL; + private->error_slot = 0; + private->error = 0; + pthread_mutex_init(&private->global_lock, NULL); + pthread_mutex_init(&private->setcallback_lock, NULL); + + // create the slots + private->slots = malloc(sizeof(struct en50221_slot) * max_slots); + if (private->slots == NULL) + goto error_exit; + + // set them up + for(i=0; i< max_slots; i++) { + private->slots[i].ca_hndl = -1; + + // create the connections for this slot + private->slots[i].connections = malloc(sizeof(struct en50221_connection) * max_connections_per_slot); + if (private->slots[i].connections == NULL) + goto error_exit; + + // create a mutex for the slot + pthread_mutex_init(&private->slots[i].slot_lock, NULL); + + // set them up + for(j = 0; j < max_connections_per_slot; j++) { + private->slots[i].connections[j].state = T_STATE_IDLE; + private->slots[i].connections[j].tx_time.tv_sec = 0; + private->slots[i].connections[j].last_poll_time.tv_sec = 0; + private->slots[i].connections[j].last_poll_time.tv_usec = 0; + private->slots[i].connections[j].chain_buffer = NULL; + private->slots[i].connections[j].buffer_length = 0; + private->slots[i].connections[j].send_queue = NULL; + private->slots[i].connections[j].send_queue_tail = NULL; + } + } + + // create the pollfds + private->slot_pollfds = malloc(sizeof(struct pollfd) * max_slots); + if (private->slot_pollfds == NULL) { + goto error_exit; + } + memset(private->slot_pollfds, 0, sizeof(struct pollfd) * max_slots); + + return private; + +error_exit: + en50221_tl_destroy(private); + return NULL; +} + +// Destroy an instance of the transport layer +void en50221_tl_destroy(en50221_transport_layer tl) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + int i, j; + + if (private) { + if (private->slots) { + for(i=0; i< private->max_slots; i++) { + if (private->slots[i].connections) { + for(j=0; j<private->max_connections_per_slot; j++) { + if (private->slots[i].connections[j].chain_buffer) { + free(private->slots[i].connections[j].chain_buffer); + } + + struct en50221_message *cur_msg = private->slots[i].connections[j].send_queue; + while(cur_msg) { + struct en50221_message *next_msg = cur_msg->next; + free(cur_msg); + cur_msg = next_msg; + } + private->slots[i].connections[j].send_queue = NULL; + private->slots[i].connections[j].send_queue_tail = NULL; + } + free(private->slots[i].connections); + pthread_mutex_destroy(&private->slots[i].slot_lock); + } + } + free(private->slots); + } + if (private->slot_pollfds) { + free(private->slot_pollfds); + } + pthread_mutex_destroy(&private->setcallback_lock); + pthread_mutex_destroy(&private->global_lock); + free(private); + } +} + +// this can be called from the user-space app to +// register new slots that we should work with +int en50221_tl_register_slot(en50221_transport_layer tl, int ca_hndl, uint8_t slot, + uint32_t response_timeout, uint32_t poll_delay) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + + // lock + pthread_mutex_lock(&private->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 < private->max_slots; i++) { + if (private->slots[i].ca_hndl == -1) { + slot_id = i; + break; + } + } + if (slot_id == -1) { + private->error = EN50221ERR_OUTOFSLOTS; + pthread_mutex_unlock(&private->global_lock); + return -1; + } + + // set up the slot struct + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + private->slots[slot_id].ca_hndl = ca_hndl; + private->slots[slot_id].slot = slot; + private->slots[slot_id].response_timeout = response_timeout; + private->slots[slot_id].poll_delay = poll_delay; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + + private->slots_changed = 1; + pthread_mutex_unlock(&private->global_lock); + return slot_id; +} + +void en50221_tl_destroy_slot(en50221_transport_layer tl, uint8_t slot_id) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + int i; + + if (slot_id >= private->max_slots) + return; + + // lock + pthread_mutex_lock(&private->global_lock); + + // clear the slot + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + private->slots[slot_id].ca_hndl = -1; + for(i=0; i<private->max_connections_per_slot; i++) { + private->slots[slot_id].connections[i].state = T_STATE_IDLE; + private->slots[slot_id].connections[i].tx_time.tv_sec = 0; + private->slots[slot_id].connections[i].last_poll_time.tv_sec = 0; + private->slots[slot_id].connections[i].last_poll_time.tv_usec = 0; + if (private->slots[slot_id].connections[i].chain_buffer) { + free(private->slots[slot_id].connections[i].chain_buffer); + } + private->slots[slot_id].connections[i].chain_buffer = NULL; + private->slots[slot_id].connections[i].buffer_length = 0; + + struct en50221_message *cur_msg = private->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; + } + private->slots[slot_id].connections[i].send_queue = NULL; + private->slots[slot_id].connections[i].send_queue_tail = NULL; + } + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + + // tell upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->setcallback_lock); + if (cb) + cb(cb_arg, T_CALLBACK_REASON_SLOTCLOSE, NULL, 0, slot_id, 0); + + private->slots_changed = 1; + pthread_mutex_unlock(&private->global_lock); +} + +int en50221_tl_poll(en50221_transport_layer tl) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + uint8_t data[4096]; + int slot_id; + int j; + + // make up pollfds if the slots have changed + pthread_mutex_lock(&private->global_lock); + if (private->slots_changed) { + for(slot_id = 0; slot_id < private->max_slots; slot_id++) { + if (private->slots[slot_id].ca_hndl != -1) { + private->slot_pollfds[slot_id].fd = private->slots[slot_id].ca_hndl; + private->slot_pollfds[slot_id].events = POLLIN|POLLPRI|POLLERR; + private->slot_pollfds[slot_id].revents = 0; + } else { + private->slot_pollfds[slot_id].fd = 0; + private->slot_pollfds[slot_id].events = 0; + private->slot_pollfds[slot_id].revents = 0; + } + } + private->slots_changed = 0; + } + pthread_mutex_unlock(&private->global_lock); + + // anything happened? + if (poll(private->slot_pollfds, private->max_slots, 10) < 0) { + private->error_slot = -1; + private->error = EN50221ERR_CAREAD; + return -1; + } + + // go through all slots (even though poll may not have reported any events + for(slot_id = 0; slot_id < private->max_slots; slot_id++) { + + // check if this slot is still used and get its handle + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + continue; + } + int ca_hndl = private->slots[slot_id].ca_hndl; + + if (private->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) { + private->error_slot = slot_id; + private->error = EN50221ERR_CAREAD; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + + // process it if we got some + if (readcnt > 0) { + if (private->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 < private->max_slots; new_slot_id++) { + if ((private->slots[new_slot_id].ca_hndl == ca_hndl) && (private->slots[new_slot_id].slot == r_slot_id)) + break; + } + if (new_slot_id != private->max_slots) { + // we found the requested CAM + pthread_mutex_lock(&private->slots[new_slot_id].slot_lock); + if (en50221_tl_process_data(private, new_slot_id, data, readcnt)) { + pthread_mutex_unlock(&private->slots[new_slot_id].slot_lock); + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + pthread_mutex_unlock(&private->slots[new_slot_id].slot_lock); + } else { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + } + else if (en50221_tl_process_data(private, slot_id, data, readcnt)) { + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + } + } else if (private->slot_pollfds[slot_id].revents & POLLERR) { + // an error was reported + private->error_slot = slot_id; + private->error = EN50221ERR_CAREAD; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + + // poll the connections on this slot + check for timeouts + for(j=0; j < private->max_connections_per_slot; j++) { + // ignore connection if idle + if (private->slots[slot_id].connections[j].state == T_STATE_IDLE) { + continue; + } + + // send queued data + if (private->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 (private->slots[slot_id].connections[j].send_queue && + (private->slots[slot_id].connections[j].tx_time.tv_sec == 0)) { + + // get the message + struct en50221_message *msg = private->slots[slot_id].connections[j].send_queue; + if (msg->next != NULL) { + private->slots[slot_id].connections[j].send_queue = msg->next; + } else { + private->slots[slot_id].connections[j].send_queue = NULL; + private->slots[slot_id].connections[j].send_queue_tail = NULL; + } + + // send the message + if (dvbca_link_write(private->slots[slot_id].ca_hndl, private->slots[slot_id].slot, j, msg->data, msg->length) < 0) { + free(msg); + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + print(LOG_LEVEL, ERROR, 1, "CAWrite failed"); + return -1; + } + gettimeofday(&private->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)) { + private->slots[slot_id].connections[j].state = T_STATE_IN_DELETION; + if (private->slots[slot_id].connections[j].chain_buffer) { + free(private->slots[slot_id].connections[j].chain_buffer); + } + private->slots[slot_id].connections[j].chain_buffer = NULL; + private->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 (private->slots[slot_id].connections[j].state & T_STATE_ACTIVE) { + if ((private->slots[slot_id].connections[j].tx_time.tv_sec == 0) && + (time_after(private->slots[slot_id].connections[j].last_poll_time, private->slots[slot_id].poll_delay))) { + + gettimeofday(&private->slots[slot_id].connections[j].last_poll_time, 0); + if (en50221_tl_poll_tc(private, slot_id, j)) { + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + } + } + + // check for timeouts - in any state + if (private->slots[slot_id].connections[j].tx_time.tv_sec && + (time_after(private->slots[slot_id].connections[j].tx_time, private->slots[slot_id].response_timeout))) { + + if (private->slots[slot_id].connections[j].state & (T_STATE_IN_CREATION|T_STATE_IN_DELETION)) { + private->slots[slot_id].connections[j].state = T_STATE_IDLE; + } else if (private->slots[slot_id].connections[j].state & (T_STATE_ACTIVE|T_STATE_ACTIVE_DELETEQUEUED)) { + private->error_slot = slot_id; + private->error = EN50221ERR_TIMEOUT; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + } + } + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + } + + return 0; +} + +void en50221_tl_register_callback(en50221_transport_layer tl, en50221_tl_callback callback, void *arg) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + + pthread_mutex_lock(&private->setcallback_lock); + private->callback = callback; + private->callback_arg = arg; + pthread_mutex_unlock(&private->setcallback_lock); +} + +int en50221_tl_get_error_slot(en50221_transport_layer tl) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + return private->error_slot; +} + +int en50221_tl_get_error(en50221_transport_layer tl) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + return private->error; +} + +int en50221_tl_send_data(en50221_transport_layer tl, uint8_t slot_id, uint8_t connection_id, + uint8_t *data, uint32_t data_size) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + +#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 >= private->max_slots) { + private->error = EN50221ERR_BADSLOTID; + return -1; + } + + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (connection_id >= private->max_connections_per_slot) { + private->error_slot = slot_id; + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (private->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) { + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->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) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + pthread_mutex_unlock(&private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_ASNENCODE; + pthread_mutex_unlock(&private->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(private, slot_id, connection_id, msg); + + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return 0; +} + +int en50221_tl_send_datav(en50221_transport_layer tl, uint8_t slot_id, uint8_t connection_id, + struct iovec *vector, int iov_count) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + +#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 >= private->max_slots) { + private->error = EN50221ERR_BADSLOTID; + return -1; + } + + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (connection_id >= private->max_connections_per_slot) { + private->error_slot = slot_id; + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (private->slots[slot_id].connections[connection_id].state != T_STATE_ACTIVE) { + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->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) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + pthread_mutex_unlock(&private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_ASNENCODE; + pthread_mutex_unlock(&private->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(private, slot_id, connection_id, msg); + + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return 0; +} + +int en50221_tl_new_tc(en50221_transport_layer tl, uint8_t slot_id) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + + // check + if (slot_id >= private->max_slots) { + private->error = EN50221ERR_BADSLOTID; + return -1; + } + + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + + // allocate a new connection if possible + int conid = en50221_tl_alloc_new_tc(private, slot_id); + if (conid == -1) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFCONNECTIONS; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + + // allocate msg structure + struct en50221_message *msg = malloc(sizeof(struct en50221_message)+3); + if (msg == NULL) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + pthread_mutex_unlock(&private->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(private, slot_id, conid, msg); + + // done + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return conid; +} + +int en50221_tl_del_tc(en50221_transport_layer tl, uint8_t slot_id, uint8_t connection_id) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + + // check + if (slot_id >= private->max_slots) { + private->error = EN50221ERR_BADSLOTID; + return -1; + } + + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (connection_id >= private->max_connections_per_slot) { + private->error_slot = slot_id; + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (!(private->slots[slot_id].connections[connection_id].state & + (T_STATE_ACTIVE|T_STATE_IN_DELETION))) { + private->error_slot = slot_id; + private->error = EN50221ERR_BADSTATE; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + + // allocate msg structure + struct en50221_message *msg = malloc(sizeof(struct en50221_message)+3); + if (msg == NULL) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + pthread_mutex_unlock(&private->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(private, slot_id, connection_id, msg); + private->slots[slot_id].connections[connection_id].state = T_STATE_ACTIVE_DELETEQUEUED; + + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return 0; +} + +int en50221_tl_get_connection_state(en50221_transport_layer tl, + uint8_t slot_id, uint8_t connection_id) +{ + struct en50221_transport_layer_private *private = (struct en50221_transport_layer_private *) tl; + + if (slot_id >= private->max_slots) { + private->error = EN50221ERR_BADSLOTID; + return -1; + } + + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + if (private->slots[slot_id].ca_hndl == -1) { + private->error = EN50221ERR_BADSLOTID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + if (connection_id >= private->max_connections_per_slot) { + private->error_slot = slot_id; + private->error = EN50221ERR_BADCONNECTIONID; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + return -1; + } + int state = private->slots[slot_id].connections[connection_id].state; + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + + return state; +} + + + + +// ask the module for new data +static int en50221_tl_poll_tc(struct en50221_transport_layer_private *private, uint8_t slot_id, uint8_t connection_id) +{ + gettimeofday(&private->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(private->slots[slot_id].ca_hndl, private->slots[slot_id].slot, connection_id, hdr, 3) < 0) { + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + return 0; +} + +// handle incoming data +static int en50221_tl_process_data(struct en50221_transport_layer_private *private, 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); + private->error_slot = slot_id; + private->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); + private->error_slot = slot_id; + private->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 >= private->max_connections_per_slot) { + print(LOG_LEVEL, ERROR, 1, "Received bad connection id %02x from module on slot %02x\n", connection_id, slot_id); + private->error_slot = slot_id; + private->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(private, slot_id, connection_id)) < 0) { + return -1; + } + break; + case T_DELETE_T_C: + if ((result = en50221_tl_handle_delete_tc(private, slot_id, connection_id)) < 0) { + return -1; + } + break; + case T_D_T_C_REPLY: + if ((result = en50221_tl_handle_delete_tc_reply(private, slot_id, connection_id)) < 0) { + return -1; + } + break; + case T_REQUEST_T_C: + if ((result = en50221_tl_handle_request_tc(private, slot_id, connection_id)) < 0) { + return -1; + } + break; + case T_DATA_MORE: + if ((result = en50221_tl_handle_data_more(private, slot_id, connection_id, data, asn_data_length)) < 0) { + return -1; + } + break; + case T_DATA_LAST: + if ((result = en50221_tl_handle_data_last(private, slot_id, connection_id, data, asn_data_length)) < 0) { + return -1; + } + break; + case T_SB: + if ((result = en50221_tl_handle_sb(private, 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); + private->error_slot = slot_id; + private->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_private *private, + uint8_t slot_id, uint8_t connection_id) +{ + // set this connection to state active + if (private->slots[slot_id].connections[connection_id].state == T_STATE_IN_CREATION) { + private->slots[slot_id].connections[connection_id].state = T_STATE_ACTIVE; + private->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0; + + // tell upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + return 0; +} + +static int en50221_tl_handle_delete_tc(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id) +{ + // immediately delete this connection and send D_T_C_REPLY + if (private->slots[slot_id].connections[connection_id].state & (T_STATE_ACTIVE|T_STATE_IN_DELETION)) + { + // clear down the slot + private->slots[slot_id].connections[connection_id].state = T_STATE_IDLE; + if (private->slots[slot_id].connections[connection_id].chain_buffer) { + free(private->slots[slot_id].connections[connection_id].chain_buffer); + } + private->slots[slot_id].connections[connection_id].chain_buffer = NULL; + private->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(private->slots[slot_id].ca_hndl, private->slots[slot_id].slot, connection_id, hdr, 3) < 0) { + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + + // tell upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + return 0; +} + +static int en50221_tl_handle_delete_tc_reply(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id) +{ + // delete this connection, should be in T_STATE_IN_DELETION already + if (private->slots[slot_id].connections[connection_id].state == T_STATE_IN_DELETION) { + private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + return 0; +} + +static int en50221_tl_handle_request_tc(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id) +{ + // allocate a new connection if possible + int conid = en50221_tl_alloc_new_tc(private, slot_id); + int ca_hndl = private->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, private->slots[slot_id].slot, connection_id, hdr, 4) < 0) { + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + private->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, private->slots[slot_id].slot, connection_id, hdr, 4) < 0) { + private->slots[slot_id].connections[conid].state = T_STATE_IDLE; + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + private->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, private->slots[slot_id].slot, conid, hdr, 3) < 0) { + private->slots[slot_id].connections[conid].state = T_STATE_IDLE; + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + gettimeofday(&private->slots[slot_id].connections[conid].tx_time, 0); + + // tell upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->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_private *private, + uint8_t slot_id, uint8_t connection_id, + uint8_t *data, uint32_t data_length) +{ + // connection in correct state? + if (private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + // a chained data packet is coming in, save + // it to the buffer and wait for more + private->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0; + int new_data_length = + private->slots[slot_id].connections[connection_id].buffer_length + data_length; + uint8_t *new_data_buffer = + realloc(private->slots[slot_id].connections[connection_id].chain_buffer, new_data_length); + if (new_data_buffer == NULL) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + return -1; + } + private->slots[slot_id].connections[connection_id].chain_buffer = new_data_buffer; + + memcpy(private->slots[slot_id].connections[connection_id].chain_buffer + + private->slots[slot_id].connections[connection_id].buffer_length, + data, data_length); + private->slots[slot_id].connections[connection_id].buffer_length = new_data_length; + + return 0; +} + +static int en50221_tl_handle_data_last(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id, + uint8_t *data, uint32_t data_length) +{ + // connection in correct state? + if (private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + // last package of a chain or single package comes in + private->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0; + if (private->slots[slot_id].connections[connection_id].chain_buffer == NULL) + { + // single package => dispatch immediately + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->setcallback_lock); + + if (cb && data_length) { + pthread_mutex_unlock(&private->slots[slot_id].slot_lock); + cb(cb_arg, T_CALLBACK_REASON_DATA, data, data_length, slot_id, connection_id); + pthread_mutex_lock(&private->slots[slot_id].slot_lock); + } + } + else + { + int new_data_length = private->slots[slot_id].connections[connection_id].buffer_length + data_length; + uint8_t *new_data_buffer = + realloc(private->slots[slot_id].connections[connection_id].chain_buffer, new_data_length); + if (new_data_buffer == NULL) { + private->error_slot = slot_id; + private->error = EN50221ERR_OUTOFMEMORY; + return -1; + } + + memcpy(new_data_buffer + private->slots[slot_id].connections[connection_id].buffer_length, data, data_length); + + // clean the buffer position + private->slots[slot_id].connections[connection_id].chain_buffer = NULL; + private->slots[slot_id].connections[connection_id].buffer_length = 0; + + // tell the upper layers + pthread_mutex_lock(&private->setcallback_lock); + en50221_tl_callback cb = private->callback; + void *cb_arg = private->callback_arg; + pthread_mutex_unlock(&private->setcallback_lock); + if (cb && data_length) { + pthread_mutex_unlock(&private->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(&private->slots[slot_id].slot_lock); + } + + free(new_data_buffer); + } + + return 0; +} + +static int en50221_tl_handle_sb(struct en50221_transport_layer_private *private, + uint8_t slot_id, uint8_t connection_id, + uint8_t *data, uint32_t data_length) +{ + // is the connection id ok? + if (private->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); + private->error_slot = slot_id; + private->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); + private->error_slot = slot_id; + private->error = EN50221ERR_BADCAMDATA; + return -1; + } + + // tell it to send the data if it says there is some + if (data[0] & 0x80) { + int ca_hndl = private->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, private->slots[slot_id].slot, connection_id, hdr, 3) < 0) { + private->error_slot = slot_id; + private->error = EN50221ERR_CAWRITE; + return -1; + } + gettimeofday(&private->slots[slot_id].connections[connection_id].tx_time, 0); + + } else { + // no data - indicate not waiting for anything now + private->slots[slot_id].connections[connection_id].tx_time.tv_sec = 0; + } + + return 0; +} + +static int en50221_tl_alloc_new_tc(struct en50221_transport_layer_private *private, 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 < private->max_connections_per_slot; i++) { + if (private->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 + private->slots[slot_id].connections[conid].state = T_STATE_IN_CREATION; + private->slots[slot_id].connections[conid].chain_buffer = NULL; + private->slots[slot_id].connections[conid].buffer_length = 0; + + return conid; +} + +static void queue_message(struct en50221_transport_layer_private *private, uint8_t slot_id, + uint8_t connection_id, struct en50221_message *msg) +{ + msg->next = NULL; + if (private->slots[slot_id].connections[connection_id].send_queue_tail) { + private->slots[slot_id].connections[connection_id].send_queue_tail->next = msg; + } else { + private->slots[slot_id].connections[connection_id].send_queue = msg; + private->slots[slot_id].connections[connection_id].send_queue_tail = msg; + } +} diff --git a/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.h b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.h new file mode 100644 index 0000000..59dc64c --- /dev/null +++ b/kaffeine/src/input/dvb/lib/libdvben50221/en50221_transport.h @@ -0,0 +1,231 @@ +/* + en50221 encoder An implementation for libdvb + an implementation for the en50221 session layer + + Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. + */ +typedef void *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 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(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(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(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(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(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(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(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(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(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(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(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(en50221_transport_layer tl, + uint8_t slot_id, uint8_t connection_id); + +#ifdef __cplusplus +} +#endif + +#endif |