aboutsummaryrefslogtreecommitdiffstats
path: root/dvb-t/se-Tranemo (follow)
AgeCommit message (Expand)AuthorFilesLines
2013-04-02Imported Upstream version 0+git20130227.3c5fe84upstream/0+git20130227.3c5fe84Jonathan McCrohan1-0/+3
278' href='#n278'>278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 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;
}