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);
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;
228
guint8 connection_id;
230
guint8 length_field_len;
233
ret = cam_tl_read (tl);
234
if (CAM_FAILED (ret))
239
/* must hold at least slot, connection_id, 1byte length_field, connection_id
241
if (tl->buffer_size < 4) {
242
GST_ERROR ("invalid TPDU length %d", tl->buffer_size);
243
return CAM_RETURN_TRANSPORT_ERROR;
248
/* LPDU connection id */
249
connection_id = tpdu[1];
251
connection = g_hash_table_lookup (tl->connections,
252
GINT_TO_POINTER ((guint) connection_id));
253
if (connection == NULL) {
255
GST_ERROR ("CAM sent a TPDU on an unknown connection: %d", connection_id);
256
return CAM_RETURN_TRANSPORT_ERROR;
259
/* read the length_field () */
260
length_field_len = cam_read_length_field (&tpdu[3], &tl->body_length);
262
if (tl->body_length + 3 > tl->buffer_size) {
263
GST_ERROR ("invalid TPDU length_field (%d) exceeds "
264
"the size of the buffer (%d)", tl->body_length, tl->buffer_size);
265
return CAM_RETURN_TRANSPORT_ERROR;
268
/* skip slot + connection id + tag + lenght_field () + connection id */
269
tl->body = tpdu + 4 + length_field_len;
270
/* do not count the connection id byte as part of the body */
271
tl->body_length -= 1;
273
if (tl->buffer[tl->buffer_size - 4] != TAG_SB) {
274
GST_ERROR ("no TAG_SB appended to TPDU");
275
return CAM_RETURN_TRANSPORT_ERROR;
278
status = tl->buffer[tl->buffer_size - 1];
280
connection->has_data = TRUE;
282
connection->has_data = FALSE;
285
GST_DEBUG ("received TPDU %x more data %d", tpdu[2], connection->has_data);
286
tl->expected_tpdus -= 1;
288
*out_connection = connection;
290
return CAM_RETURN_OK;
293
/* create a connection with the module */
295
cam_tl_create_connection (CamTL * tl, guint8 slot,
296
CamTLConnection ** connection)
299
CamTLConnection *conn = NULL;
301
if (tl->connection_ids == 255)
302
return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS;
304
conn = cam_tl_connection_new (tl, ++tl->connection_ids);
306
/* send a TAG_CREATE_T_C TPDU */
307
ret = cam_tl_connection_write_control_tpdu (conn, TAG_CREATE_T_C);
308
if (CAM_FAILED (ret))
311
g_hash_table_insert (tl->connections, GINT_TO_POINTER (conn->id), conn);
315
return CAM_RETURN_OK;
319
cam_tl_connection_destroy (conn);
325
cam_tl_connection_delete (CamTLConnection * connection)
329
ret = cam_tl_connection_write_control_tpdu (connection, TAG_DELETE_T_C);
330
if (CAM_FAILED (ret))
333
connection->state = CAM_TL_CONNECTION_STATE_IN_DELETION;
335
return CAM_RETURN_OK;
339
handle_control_tpdu (CamTL * tl, CamTLConnection * connection)
341
if (tl->body_length != 0) {
342
GST_ERROR ("got control tpdu of invalid length: %d", tl->body_length);
343
return CAM_RETURN_TRANSPORT_ERROR;
346
switch (tl->buffer[2]) {
347
/* create transport connection reply */
348
case TAG_C_T_C_REPLY:
349
/* a connection might be closed before it's acknowledged */
350
if (connection->state != CAM_TL_CONNECTION_STATE_IN_DELETION) {
351
GST_DEBUG ("connection created %d", connection->id);
352
connection->state = CAM_TL_CONNECTION_STATE_OPEN;
354
if (tl->connection_created)
355
tl->connection_created (tl, connection);
358
/* delete transport connection reply */
359
case TAG_D_T_C_REPLY:
360
connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
361
GST_DEBUG ("connection closed %d", connection->id);
363
if (tl->connection_deleted)
364
tl->connection_deleted (tl, connection);
366
g_hash_table_remove (tl->connections,
367
GINT_TO_POINTER ((guint) connection->id));
371
return CAM_RETURN_OK;
375
handle_data_tpdu (CamTL * tl, CamTLConnection * connection)
377
if (tl->body_length == 0) {
378
/* FIXME: figure out why this seems to happen from time to time with the
380
GST_WARNING ("Empty data TPDU received");
381
return CAM_RETURN_OK;
384
if (tl->connection_data)
385
return tl->connection_data (tl, connection, tl->body, tl->body_length);
387
return CAM_RETURN_OK;
391
foreach_connection_get (gpointer key, gpointer value, gpointer user_data)
393
GList **lst = (GList **) user_data;
395
*lst = g_list_append (*lst, value);
399
cam_tl_connection_poll (CamTLConnection * connection, gboolean force)
403
if (connection->last_poll == NULL) {
404
connection->last_poll = g_timer_new ();
406
g_timer_elapsed (connection->last_poll, NULL) < POLL_INTERVAL) {
407
return CAM_RETURN_TRANSPORT_POLL;
410
GST_DEBUG ("polling connection %d", connection->id);
411
ret = cam_tl_connection_write_control_tpdu (connection, TAG_DATA_LAST);
412
if (CAM_FAILED (ret))
415
g_timer_start (connection->last_poll);
417
return CAM_RETURN_OK;
420
/* read all the queued TPDUs */
422
cam_tl_read_all (CamTL * tl, gboolean poll)
424
CamReturn ret = CAM_RETURN_OK;
425
CamTLConnection *connection;
426
GList *connections = NULL;
428
gboolean done = FALSE;
431
while (tl->expected_tpdus) {
432
/* read the next TPDU from the connection */
433
ret = cam_tl_read_tpdu_next (tl, &connection);
434
if (CAM_FAILED (ret)) {
435
GST_ERROR ("error reading TPDU from module: %d", ret);
439
switch (tl->buffer[2]) {
440
case TAG_C_T_C_REPLY:
441
case TAG_D_T_C_REPLY:
442
connection->empty_data = 0;
443
ret = handle_control_tpdu (tl, connection);
447
connection->empty_data = 0;
448
ret = handle_data_tpdu (tl, connection);
451
/* this is handled by tpdu_next */
455
if (CAM_FAILED (ret))
462
g_hash_table_foreach (tl->connections,
463
foreach_connection_get, &connections);
465
for (walk = connections; walk; walk = walk->next) {
466
CamTLConnection *connection = CAM_TL_CONNECTION (walk->data);
468
if (connection->has_data == TRUE && connection->empty_data < 10) {
469
ret = cam_tl_connection_write_control_tpdu (connection, TAG_RCV);
470
if (CAM_FAILED (ret)) {
471
g_list_free (connections);
474
/* increment the empty_data counter. If we get data, this will be reset
476
connection->empty_data++;
479
ret = cam_tl_connection_poll (connection, FALSE);
480
if (ret == CAM_RETURN_TRANSPORT_POLL)
483
if (CAM_FAILED (ret)) {
484
g_list_free (connections);
492
g_list_free (connections);
500
cam_tl_connection_write (CamTLConnection * connection,
501
guint8 * buffer, guint buffer_size, guint body_length)
503
return cam_tl_connection_write_tpdu (connection,
504
TAG_DATA_LAST, buffer, buffer_size, 1 + body_length);