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