diff options
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  | 
