diff options
author | etobi <git@e-tobi.net> | 2013-09-03 09:48:41 +0200 |
---|---|---|
committer | etobi <git@e-tobi.net> | 2013-09-03 09:48:41 +0200 |
commit | ab959d7b4194715870128e616b8e29d4a101e488 (patch) | |
tree | 61a746231d30817be73416a7d67763fd677a1042 /lib/libdvben50221/en50221_app_lowspeed.c | |
parent | 6b350466c4902c5b137e0efaf1d189128a7f18f5 (diff) | |
download | linux-dvb-apps-ab959d7b4194715870128e616b8e29d4a101e488.tar.gz |
Imported Upstream version 1.1.1+rev1207upstream/1.1.1+rev1207
Diffstat (limited to 'lib/libdvben50221/en50221_app_lowspeed.c')
-rw-r--r-- | lib/libdvben50221/en50221_app_lowspeed.c | 533 |
1 files changed, 533 insertions, 0 deletions
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; +} |