diff options
author | Alessandro Decina <alessandro@nnva.org> | 2007-10-16 17:38:05 +0000 |
---|---|---|
committer | Zaheer Abbas Merali <zaheerabbas@merali.org> | 2007-10-16 17:38:05 +0000 |
commit | c205b740d4e793354493c4a2663e45670255453c (patch) | |
tree | 3c5d8978e68cf860b69c279e14372ccc144a883c /sys/dvb/camtransport.c | |
parent | 389904f6248c65468175c072441747f51c5a5296 (diff) | |
download | gst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.tar.gz gst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.tar.bz2 gst-plugins-bad-c205b740d4e793354493c4a2663e45670255453c.zip |
sys/dvb/: Integrate SoC work done by Alessandro for the Freevo project.
Original commit message from CVS:
patch by: Alessandro Decina
* sys/dvb/Makefile.am:
* sys/dvb/cam.c:
* sys/dvb/cam.h:
* sys/dvb/camapplication.c:
* sys/dvb/camapplication.h:
* sys/dvb/camapplicationinfo.c:
* sys/dvb/camapplicationinfo.h:
* sys/dvb/camconditionalaccess.c:
* sys/dvb/camconditionalaccess.h:
* sys/dvb/camdevice.c:
* sys/dvb/camdevice.h:
* sys/dvb/camresourcemanager.c:
* sys/dvb/camresourcemanager.h:
* sys/dvb/camsession.c:
* sys/dvb/camsession.h:
* sys/dvb/camswclient.c:
* sys/dvb/camswclient.h:
* sys/dvb/camtransport.c:
* sys/dvb/camtransport.h:
* sys/dvb/camutils.c:
* sys/dvb/camutils.h:
* sys/dvb/dvbbasebin.c:
* sys/dvb/dvbbasebin.h:
* sys/dvb/gstdvb.c:
* sys/dvb/gstdvbsrc.c:
* sys/dvb/gstdvbsrc.h:
Integrate SoC work done by Alessandro for the Freevo project.
Adds cam support to the dvb stack in GStreamer and a new
element (actually a bin) called dvbbasebin that integrates
dvbsrc and mpegtsparse to a) handle decryption and b) allow
acquiring multiple channels on same transponder without
knowing pid numbers.
Diffstat (limited to 'sys/dvb/camtransport.c')
-rw-r--r-- | sys/dvb/camtransport.c | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/sys/dvb/camtransport.c b/sys/dvb/camtransport.c new file mode 100644 index 00000000..288b2e46 --- /dev/null +++ b/sys/dvb/camtransport.c @@ -0,0 +1,505 @@ +/* + * camtransport.c - GStreamer CAM (EN50221) transport layer + * Copyright (C) 2007 Alessandro Decina + * + * Authors: + * Alessandro Decina <alessandro@nnva.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 "camtransport.h" +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> + +#define GST_CAT_DEFAULT cam_debug_cat +#define READ_TIMEOUT_SEC 2 +#define READ_TIMEOUT_USEC 0 + +#define POLL_INTERVAL 0.300 + +#define TAG_SB 0x80 +#define TAG_RCV 0x81 +#define TAG_CREATE_T_C 0x82 +#define TAG_C_T_C_REPLY 0x83 +#define TAG_DELETE_T_C 0x84 +#define TAG_D_T_C_REPLY 0x85 +#define TAG_REQUEST_T_C 0x86 +#define TAG_NEW_T_C 0x87 +#define TAG_T_C_ERROR 0x88 +#define TAG_DATA_MORE 0xA1 +#define TAG_DATA_LAST 0xA0 + +/* utility struct used to store the state of the connections in cam_tl_read_next + */ +typedef struct +{ + GList *active; + GList *idle; +} CamTLConnectionsStatus; + +void cam_gst_util_dump_mem (const guchar * mem, guint size); + +CamTLConnection * +cam_tl_connection_new (CamTL * tl, guint8 id) +{ + CamTLConnection *connection; + + connection = g_new0 (CamTLConnection, 1); + connection->tl = tl; + connection->id = id; + connection->state = CAM_TL_CONNECTION_STATE_CLOSED; + connection->has_data = FALSE; + + return connection; +} + +static void +cam_tl_connection_destroy (CamTLConnection * connection) +{ + if (connection->last_poll) + g_timer_destroy (connection->last_poll); + + g_free (connection); +} + +CamTL * +cam_tl_new (int fd) +{ + CamTL *tl; + + tl = g_new0 (CamTL, 1); + tl->fd = fd; + tl->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) cam_tl_connection_destroy); + + return tl; +} + +void +cam_tl_destroy (CamTL * tl) +{ + g_hash_table_destroy (tl->connections); + + g_free (tl); +} + +/* read data from the module without blocking indefinitely */ +static CamReturn +cam_tl_read_timeout (CamTL * tl, struct timeval *timeout) +{ + fd_set read_fd; + int sret; + + FD_ZERO (&read_fd); + FD_SET (tl->fd, &read_fd); + + sret = select (tl->fd + 1, &read_fd, NULL, NULL, timeout); + if (sret == 0) { + GST_DEBUG ("read timeout"); + return CAM_RETURN_TRANSPORT_TIMEOUT; + } + + tl->buffer_size = read (tl->fd, &tl->buffer, HOST_BUFFER_SIZE); + if (tl->buffer_size == -1) { + GST_ERROR ("error reading tpdu: %s", g_strerror (errno)); + return CAM_RETURN_TRANSPORT_ERROR; + } + + return CAM_RETURN_OK; +} + +/* read data from the module using the default timeout */ +static CamReturn +cam_tl_read (CamTL * tl) +{ + struct timeval timeout; + + timeout.tv_sec = READ_TIMEOUT_SEC; + timeout.tv_usec = READ_TIMEOUT_USEC; + + return cam_tl_read_timeout (tl, &timeout); +} + +/* get the number of bytes to allocate for a TPDU with a body of body_length + * bytes. Also get the offset from the beginning of the buffer that marks the + * position of the first byte of the TPDU body */ +void +cam_tl_calc_buffer_size (CamTL * tl, guint body_length, + guint * buffer_size, guint * offset) +{ + guint length_field_len; + + /* the size of a TPDU is: + * 1 byte slot number + * 1 byte connection id + * length_field_len bytes length field + * 1 byte connection id + * body_length bytes body + */ + + /* get the length of the lenght_field block */ + length_field_len = cam_calc_length_field_size (body_length); + + *offset = 3 + length_field_len + 1; + *buffer_size = *offset + body_length; +} + +/* write the header of a TPDU + * NOTE: this function assumes that the buffer is large enough to contain the + * complete TPDU (see cam_tl_calc_buffer_size ()) and that enough space has been + * left from the beginning of the buffer to write the TPDU header. + */ +static CamReturn +cam_tl_connection_write_tpdu (CamTLConnection * connection, + guint8 tag, guint8 * buffer, guint buffer_size, guint body_length) +{ + int sret; + CamTL *tl = connection->tl; + guint8 length_field_len; + + /* slot number */ + buffer[0] = connection->slot; + /* connection number */ + buffer[1] = connection->id; + /* tag */ + buffer[2] = tag; + /* length can take 1 to 4 bytes */ + length_field_len = cam_write_length_field (&buffer[3], body_length); + buffer[3 + length_field_len] = connection->id; + + GST_DEBUG ("writing TPDU %x connection %d", buffer[2], connection->id); + + //cam_gst_util_dump_mem (buffer, buffer_size); + + sret = write (tl->fd, buffer, buffer_size); + if (sret == -1) { + GST_ERROR ("error witing TPDU (%d): %s", errno, g_strerror (errno)); + return CAM_RETURN_TRANSPORT_ERROR; + } + + tl->expected_tpdus += 1; + + return CAM_RETURN_OK; +} + +/* convenience function to write control TPDUs (TPDUs having a single-byte body) + */ +static CamReturn +cam_tl_connection_write_control_tpdu (CamTLConnection * connection, guint8 tag) +{ + guint8 tpdu[5]; + + /* TPDU layout (5 bytes): + * + * slot number (1 byte) + * connection id (1 byte) + * tag (1 byte) + * length (1 byte) + * connection id (1 byte) + */ + + return cam_tl_connection_write_tpdu (connection, tag, tpdu, 5, 1); +} + +/* read the next TPDU from the CAM */ +static CamReturn +cam_tl_read_tpdu_next (CamTL * tl, CamTLConnection ** out_connection) +{ + CamReturn ret; + CamTLConnection *connection; + guint8 slot; + guint8 connection_id; + guint8 *tpdu; + guint8 length_field_len; + guint8 status; + + ret = cam_tl_read (tl); + if (CAM_FAILED (ret)) + return ret; + + tpdu = tl->buffer; + + /* must hold at least slot, connection_id, 1byte length_field, connection_id + */ + if (tl->buffer_size < 4) { + GST_ERROR ("invalid TPDU length %d", tl->buffer_size); + return CAM_RETURN_TRANSPORT_ERROR; + } + + /* LPDU slot */ + slot = tpdu[0]; + /* LPDU connection id */ + connection_id = tpdu[1]; + + connection = g_hash_table_lookup (tl->connections, + GINT_TO_POINTER ((guint) connection_id)); + if (connection == NULL) { + /* WHAT? */ + GST_ERROR ("CAM sent a TPDU on an unknown connection: %d", connection_id); + return CAM_RETURN_TRANSPORT_ERROR; + } + + /* read the length_field () */ + length_field_len = cam_read_length_field (&tpdu[3], &tl->body_length); + + if (tl->body_length + 3 > tl->buffer_size) { + GST_ERROR ("invalid TPDU length_field (%d) exceeds " + "the size of the buffer (%d)", tl->body_length, tl->buffer_size); + return CAM_RETURN_TRANSPORT_ERROR; + } + + /* skip slot + connection id + tag + lenght_field () + connection id */ + tl->body = tpdu + 4 + length_field_len; + /* do not count the connection id byte as part of the body */ + tl->body_length -= 1; + + if (tl->buffer[tl->buffer_size - 4] != TAG_SB) { + GST_ERROR ("no TAG_SB appended to TPDU"); + return CAM_RETURN_TRANSPORT_ERROR; + } + + status = tl->buffer[tl->buffer_size - 1]; + if (status & 0x80) { + connection->has_data = TRUE; + } else { + connection->has_data = FALSE; + } + + GST_DEBUG ("received TPDU %x more data %d", tpdu[2], connection->has_data); + tl->expected_tpdus -= 1; + + *out_connection = connection; + + return CAM_RETURN_OK; +} + +/* create a connection with the module */ +CamReturn +cam_tl_create_connection (CamTL * tl, guint8 slot, + CamTLConnection ** connection) +{ + CamReturn ret; + CamTLConnection *conn = NULL; + + if (tl->connection_ids == 255) + return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS; + + conn = cam_tl_connection_new (tl, ++tl->connection_ids); + + /* send a TAG_CREATE_T_C TPDU */ + ret = cam_tl_connection_write_control_tpdu (conn, TAG_CREATE_T_C); + if (CAM_FAILED (ret)) + goto error; + + g_hash_table_insert (tl->connections, GINT_TO_POINTER (conn->id), conn); + + *connection = conn; + + return CAM_RETURN_OK; + +error: + if (conn) + cam_tl_connection_destroy (conn); + + return ret; +} + +CamReturn +cam_tl_connection_delete (CamTLConnection * connection) +{ + CamReturn ret; + + ret = cam_tl_connection_write_control_tpdu (connection, TAG_DELETE_T_C); + if (CAM_FAILED (ret)) + return ret; + + connection->state = CAM_TL_CONNECTION_STATE_IN_DELETION; + + return CAM_RETURN_OK; +} + +CamReturn +handle_control_tpdu (CamTL * tl, CamTLConnection * connection) +{ + if (tl->body_length != 0) { + GST_ERROR ("got control tpdu of invalid length: %d", tl->body_length); + return CAM_RETURN_TRANSPORT_ERROR; + } + + switch (tl->buffer[2]) { + /* create transport connection reply */ + case TAG_C_T_C_REPLY: + /* a connection might be closed before it's acknowledged */ + if (connection->state != CAM_TL_CONNECTION_STATE_IN_DELETION) { + GST_DEBUG ("connection created %d", connection->id); + connection->state = CAM_TL_CONNECTION_STATE_OPEN; + + if (tl->connection_created) + tl->connection_created (tl, connection); + } + break; + /* delete transport connection reply */ + case TAG_D_T_C_REPLY: + connection->state = CAM_TL_CONNECTION_STATE_CLOSED; + GST_DEBUG ("connection closed %d", connection->id); + + if (tl->connection_deleted) + tl->connection_deleted (tl, connection); + + g_hash_table_remove (tl->connections, + GINT_TO_POINTER ((guint) connection->id)); + break; + } + + return CAM_RETURN_OK; +} + +CamReturn +handle_data_tpdu (CamTL * tl, CamTLConnection * connection) +{ + if (tl->body_length == 0) { + /* FIXME: figure out why this seems to happen from time to time with the + * predator cam */ + GST_WARNING ("Empty data TPDU received"); + return CAM_RETURN_OK; + } + + if (tl->connection_data) + return tl->connection_data (tl, connection, tl->body, tl->body_length); + + return CAM_RETURN_OK; +} + +static void +foreach_connection_get (gpointer key, gpointer value, gpointer user_data) +{ + GList **lst = (GList **) user_data; + + *lst = g_list_append (*lst, value); +} + +CamReturn +cam_tl_connection_poll (CamTLConnection * connection, gboolean force) +{ + CamReturn ret; + + if (connection->last_poll == NULL) { + connection->last_poll = g_timer_new (); + } else if (!force && + g_timer_elapsed (connection->last_poll, NULL) < POLL_INTERVAL) { + return CAM_RETURN_TRANSPORT_POLL; + } + + GST_DEBUG ("polling connection %d", connection->id); + ret = cam_tl_connection_write_control_tpdu (connection, TAG_DATA_LAST); + if (CAM_FAILED (ret)) + return ret; + + g_timer_start (connection->last_poll); + + return CAM_RETURN_OK; +} + +/* read all the queued TPDUs */ +CamReturn +cam_tl_read_all (CamTL * tl, gboolean poll) +{ + CamReturn ret = CAM_RETURN_OK; + CamTLConnection *connection; + GList *connections = NULL; + GList *walk; + gboolean done = FALSE; + + while (!done) { + while (tl->expected_tpdus) { + /* read the next TPDU from the connection */ + ret = cam_tl_read_tpdu_next (tl, &connection); + if (CAM_FAILED (ret)) { + GST_ERROR ("error reading TPDU from module: %d", ret); + goto out; + } + + switch (tl->buffer[2]) { + case TAG_C_T_C_REPLY: + case TAG_D_T_C_REPLY: + connection->empty_data = 0; + ret = handle_control_tpdu (tl, connection); + break; + case TAG_DATA_MORE: + case TAG_DATA_LAST: + connection->empty_data = 0; + ret = handle_data_tpdu (tl, connection); + break; + case TAG_SB: + /* this is handled by tpdu_next */ + break; + } + + if (CAM_FAILED (ret)) + goto out; + } + + done = TRUE; + + connections = NULL; + g_hash_table_foreach (tl->connections, + foreach_connection_get, &connections); + + for (walk = connections; walk; walk = walk->next) { + CamTLConnection *connection = CAM_TL_CONNECTION (walk->data); + + if (connection->has_data == TRUE && connection->empty_data < 10) { + ret = cam_tl_connection_write_control_tpdu (connection, TAG_RCV); + if (CAM_FAILED (ret)) { + g_list_free (connections); + goto out; + } + /* increment the empty_data counter. If we get data, this will be reset + * to 0 */ + connection->empty_data++; + done = FALSE; + } else if (poll) { + ret = cam_tl_connection_poll (connection, FALSE); + if (ret == CAM_RETURN_TRANSPORT_POLL) + continue; + + if (CAM_FAILED (ret)) { + g_list_free (connections); + goto out; + } + + done = FALSE; + } + } + + g_list_free (connections); + } + +out: + return ret; +} + +CamReturn +cam_tl_connection_write (CamTLConnection * connection, + guint8 * buffer, guint buffer_size, guint body_length) +{ + return cam_tl_connection_write_tpdu (connection, + TAG_DATA_LAST, buffer, buffer_size, 1 + body_length); +} |