2 * camtransport.c - GStreamer CAM (EN50221) transport layer
3 * Copyright (C) 2007 Alessandro Decina
6 * Alessandro Decina <alessandro@nnva.org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include "camtransport.h"
25 #include <sys/select.h>
27 #include <sys/types.h>
31 #define GST_CAT_DEFAULT cam_debug_cat
32 #define READ_TIMEOUT_SEC 2
33 #define READ_TIMEOUT_USEC 0
35 #define POLL_INTERVAL 0.300
39 #define TAG_CREATE_T_C 0x82
40 #define TAG_C_T_C_REPLY 0x83
41 #define TAG_DELETE_T_C 0x84
42 #define TAG_D_T_C_REPLY 0x85
43 #define TAG_REQUEST_T_C 0x86
44 #define TAG_NEW_T_C 0x87
45 #define TAG_T_C_ERROR 0x88
46 #define TAG_DATA_MORE 0xA1
47 #define TAG_DATA_LAST 0xA0
49 /* utility struct used to store the state of the connections in cam_tl_read_next
55 } CamTLConnectionsStatus;
57 void cam_gst_util_dump_mem (const guchar * mem, guint size);
59 static CamTLConnection *
60 cam_tl_connection_new (CamTL * tl, guint8 id)
62 CamTLConnection *connection;
64 connection = g_new0 (CamTLConnection, 1);
67 connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
68 connection->has_data = FALSE;
74 cam_tl_connection_destroy (CamTLConnection * connection)
76 if (connection->last_poll)
77 g_timer_destroy (connection->last_poll);
87 tl = g_new0 (CamTL, 1);
89 tl->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
90 NULL, (GDestroyNotify) cam_tl_connection_destroy);
96 cam_tl_destroy (CamTL * tl)
98 g_hash_table_destroy (tl->connections);
103 /* read data from the module without blocking indefinitely */
105 cam_tl_read_timeout (CamTL * tl, struct timeval *timeout)
111 FD_SET (tl->fd, &read_fd);
113 sret = select (tl->fd + 1, &read_fd, NULL, NULL, timeout);
115 GST_DEBUG ("read timeout");
116 return CAM_RETURN_TRANSPORT_TIMEOUT;
119 tl->buffer_size = read (tl->fd, &tl->buffer, HOST_BUFFER_SIZE);
120 if (tl->buffer_size == -1) {
121 GST_ERROR ("error reading tpdu: %s", g_strerror (errno));
122 return CAM_RETURN_TRANSPORT_ERROR;
125 return CAM_RETURN_OK;
128 /* read data from the module using the default timeout */
130 cam_tl_read (CamTL * tl)
132 struct timeval timeout;
134 timeout.tv_sec = READ_TIMEOUT_SEC;
135 timeout.tv_usec = READ_TIMEOUT_USEC;
137 return cam_tl_read_timeout (tl, &timeout);
140 /* get the number of bytes to allocate for a TPDU with a body of body_length
141 * bytes. Also get the offset from the beginning of the buffer that marks the
142 * position of the first byte of the TPDU body */
144 cam_tl_calc_buffer_size (CamTL * tl, guint body_length,
145 guint * buffer_size, guint * offset)
147 guint length_field_len;
149 /* the size of a TPDU is:
151 * 1 byte connection id
152 * length_field_len bytes length field
153 * 1 byte connection id
154 * body_length bytes body
157 /* get the length of the lenght_field block */
158 length_field_len = cam_calc_length_field_size (body_length);
160 *offset = 3 + length_field_len + 1;
161 *buffer_size = *offset + body_length;
164 /* write the header of a TPDU
165 * NOTE: this function assumes that the buffer is large enough to contain the
166 * complete TPDU (see cam_tl_calc_buffer_size ()) and that enough space has been
167 * left from the beginning of the buffer to write the TPDU header.
170 cam_tl_connection_write_tpdu (CamTLConnection * connection,
171 guint8 tag, guint8 * buffer, guint buffer_size, guint body_length)
174 CamTL *tl = connection->tl;
175 guint8 length_field_len;
178 buffer[0] = connection->slot;
179 /* connection number */
180 buffer[1] = connection->id;
183 /* length can take 1 to 4 bytes */
184 length_field_len = cam_write_length_field (&buffer[3], body_length);
185 buffer[3 + length_field_len] = connection->id;
187 GST_DEBUG ("writing TPDU %x connection %d", buffer[2], connection->id);
189 //cam_gst_util_dump_mem (buffer, buffer_size);
191 sret = write (tl->fd, buffer, buffer_size);
193 GST_ERROR ("error witing TPDU (%d): %s", errno, g_strerror (errno));
194 return CAM_RETURN_TRANSPORT_ERROR;
197 tl->expected_tpdus += 1;
199 return CAM_RETURN_OK;
202 /* convenience function to write control TPDUs (TPDUs having a single-byte body)
205 cam_tl_connection_write_control_tpdu (CamTLConnection * connection, guint8 tag)
209 /* TPDU layout (5 bytes):
211 * slot number (1 byte)
212 * connection id (1 byte)
215 * connection id (1 byte)
218 return cam_tl_connection_write_tpdu (connection, tag, tpdu, 5, 1);
221 /* read the next TPDU from the CAM */
223 cam_tl_read_tpdu_next (CamTL * tl, CamTLConnection ** out_connection)
226 CamTLConnection *connection;
227 guint8 connection_id;
229 guint8 length_field_len;
232 ret = cam_tl_read (tl);
233 if (CAM_FAILED (ret))
238 /* must hold at least slot, connection_id, 1byte length_field, connection_id
240 if (tl->buffer_size < 4) {
241 GST_ERROR ("invalid TPDU length %d", tl->buffer_size);
242 return CAM_RETURN_TRANSPORT_ERROR;
246 /* slot = tpdu[0]; */
247 /* LPDU connection id */
248 connection_id = tpdu[1];
250 connection = g_hash_table_lookup (tl->connections,
251 GINT_TO_POINTER ((guint) connection_id));
252 if (connection == NULL) {
254 GST_ERROR ("CAM sent a TPDU on an unknown connection: %d", connection_id);
255 return CAM_RETURN_TRANSPORT_ERROR;
258 /* read the length_field () */
259 length_field_len = cam_read_length_field (&tpdu[3], &tl->body_length);
261 if (tl->body_length + 3 > tl->buffer_size) {
262 GST_ERROR ("invalid TPDU length_field (%d) exceeds "
263 "the size of the buffer (%d)", tl->body_length, tl->buffer_size);
264 return CAM_RETURN_TRANSPORT_ERROR;
267 /* skip slot + connection id + tag + lenght_field () + connection id */
268 tl->body = tpdu + 4 + length_field_len;
269 /* do not count the connection id byte as part of the body */
270 tl->body_length -= 1;
272 if (tl->buffer[tl->buffer_size - 4] != TAG_SB) {
273 GST_ERROR ("no TAG_SB appended to TPDU");
274 return CAM_RETURN_TRANSPORT_ERROR;
277 status = tl->buffer[tl->buffer_size - 1];
279 connection->has_data = TRUE;
281 connection->has_data = FALSE;
284 GST_DEBUG ("received TPDU %x more data %d", tpdu[2], connection->has_data);
285 tl->expected_tpdus -= 1;
287 *out_connection = connection;
289 return CAM_RETURN_OK;
292 /* create a connection with the module */
294 cam_tl_create_connection (CamTL * tl, guint8 slot,
295 CamTLConnection ** connection)
298 CamTLConnection *conn = NULL;
300 if (tl->connection_ids == 255)
301 return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS;
303 conn = cam_tl_connection_new (tl, ++tl->connection_ids);
305 /* send a TAG_CREATE_T_C TPDU */
306 ret = cam_tl_connection_write_control_tpdu (conn, TAG_CREATE_T_C);
307 if (CAM_FAILED (ret))
310 g_hash_table_insert (tl->connections, GINT_TO_POINTER (conn->id), conn);
314 return CAM_RETURN_OK;
318 cam_tl_connection_destroy (conn);
324 cam_tl_connection_delete (CamTLConnection * connection)
328 ret = cam_tl_connection_write_control_tpdu (connection, TAG_DELETE_T_C);
329 if (CAM_FAILED (ret))
332 connection->state = CAM_TL_CONNECTION_STATE_IN_DELETION;
334 return CAM_RETURN_OK;
338 handle_control_tpdu (CamTL * tl, CamTLConnection * connection)
340 if (tl->body_length != 0) {
341 GST_ERROR ("got control tpdu of invalid length: %d", tl->body_length);
342 return CAM_RETURN_TRANSPORT_ERROR;
345 switch (tl->buffer[2]) {
346 /* create transport connection reply */
347 case TAG_C_T_C_REPLY:
348 /* a connection might be closed before it's acknowledged */
349 if (connection->state != CAM_TL_CONNECTION_STATE_IN_DELETION) {
350 GST_DEBUG ("connection created %d", connection->id);
351 connection->state = CAM_TL_CONNECTION_STATE_OPEN;
353 if (tl->connection_created)
354 tl->connection_created (tl, connection);
357 /* delete transport connection reply */
358 case TAG_D_T_C_REPLY:
359 connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
360 GST_DEBUG ("connection closed %d", connection->id);
362 if (tl->connection_deleted)
363 tl->connection_deleted (tl, connection);
365 g_hash_table_remove (tl->connections,
366 GINT_TO_POINTER ((guint) connection->id));
370 return CAM_RETURN_OK;
374 handle_data_tpdu (CamTL * tl, CamTLConnection * connection)
376 if (tl->body_length == 0) {
377 /* FIXME: figure out why this seems to happen from time to time with the
379 GST_WARNING ("Empty data TPDU received");
380 return CAM_RETURN_OK;
383 if (tl->connection_data)
384 return tl->connection_data (tl, connection, tl->body, tl->body_length);
386 return CAM_RETURN_OK;
390 foreach_connection_get (gpointer key, gpointer value, gpointer user_data)
392 GList **lst = (GList **) user_data;
394 *lst = g_list_append (*lst, value);
398 cam_tl_connection_poll (CamTLConnection * connection, gboolean force)
402 if (connection->last_poll == NULL) {
403 connection->last_poll = g_timer_new ();
405 g_timer_elapsed (connection->last_poll, NULL) < POLL_INTERVAL) {
406 return CAM_RETURN_TRANSPORT_POLL;
409 GST_DEBUG ("polling connection %d", connection->id);
410 ret = cam_tl_connection_write_control_tpdu (connection, TAG_DATA_LAST);
411 if (CAM_FAILED (ret))
414 g_timer_start (connection->last_poll);
416 return CAM_RETURN_OK;
419 /* read all the queued TPDUs */
421 cam_tl_read_all (CamTL * tl, gboolean poll)
423 CamReturn ret = CAM_RETURN_OK;
424 CamTLConnection *connection;
425 GList *connections = NULL;
427 gboolean done = FALSE;
430 while (tl->expected_tpdus) {
431 /* read the next TPDU from the connection */
432 ret = cam_tl_read_tpdu_next (tl, &connection);
433 if (CAM_FAILED (ret)) {
434 GST_ERROR ("error reading TPDU from module: %d", ret);
438 switch (tl->buffer[2]) {
439 case TAG_C_T_C_REPLY:
440 case TAG_D_T_C_REPLY:
441 connection->empty_data = 0;
442 ret = handle_control_tpdu (tl, connection);
446 connection->empty_data = 0;
447 ret = handle_data_tpdu (tl, connection);
450 /* this is handled by tpdu_next */
454 if (CAM_FAILED (ret))
461 g_hash_table_foreach (tl->connections,
462 foreach_connection_get, &connections);
464 for (walk = connections; walk; walk = walk->next) {
465 CamTLConnection *connection = CAM_TL_CONNECTION (walk->data);
467 if (connection->has_data == TRUE && connection->empty_data < 10) {
468 ret = cam_tl_connection_write_control_tpdu (connection, TAG_RCV);
469 if (CAM_FAILED (ret)) {
470 g_list_free (connections);
473 /* increment the empty_data counter. If we get data, this will be reset
475 connection->empty_data++;
478 ret = cam_tl_connection_poll (connection, FALSE);
479 if (ret == CAM_RETURN_TRANSPORT_POLL)
482 if (CAM_FAILED (ret)) {
483 g_list_free (connections);
491 g_list_free (connections);
499 cam_tl_connection_write (CamTLConnection * connection,
500 guint8 * buffer, guint buffer_size, guint body_length)
502 return cam_tl_connection_write_tpdu (connection,
503 TAG_DATA_LAST, buffer, buffer_size, 1 + body_length);