summaryrefslogtreecommitdiffstats
path: root/sys/dvb/camtransport.c
diff options
context:
space:
mode:
authorAlessandro Decina <alessandro@nnva.org>2007-10-16 17:38:05 +0000
committerZaheer Abbas Merali <zaheerabbas@merali.org>2007-10-16 17:38:05 +0000
commitc205b740d4e793354493c4a2663e45670255453c (patch)
tree3c5d8978e68cf860b69c279e14372ccc144a883c /sys/dvb/camtransport.c
parent389904f6248c65468175c072441747f51c5a5296 (diff)
downloadgst-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.c505
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);
+}