~oah-dev/oah/gst-plugins-bad

« back to all changes in this revision

Viewing changes to sys/dvb/camtransport.c

  • Committer: Haakon Sporsheim
  • Date: 2009-03-12 13:52:03 UTC
  • Revision ID: haakon.sporsheim@tandberg.com-20090312135203-i5k294hgkushb0mt
Initial import of git repository: git://anongit.freedesktop.org/gstreamer/gst-plugins-bad (tag: RELEASE-0_10_10)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * camtransport.c - GStreamer CAM (EN50221) transport layer
 
3
 * Copyright (C) 2007 Alessandro Decina
 
4
 * 
 
5
 * Authors:
 
6
 *   Alessandro Decina <alessandro@nnva.org>
 
7
 *
 
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.
 
12
 *
 
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.
 
17
 *
 
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.
 
22
 */
 
23
 
 
24
#include "camtransport.h"
 
25
#include <sys/select.h>
 
26
#include <sys/time.h>
 
27
#include <sys/types.h>
 
28
#include <unistd.h>
 
29
#include <errno.h>
 
30
 
 
31
#define GST_CAT_DEFAULT cam_debug_cat
 
32
#define READ_TIMEOUT_SEC 2
 
33
#define READ_TIMEOUT_USEC 0
 
34
 
 
35
#define POLL_INTERVAL 0.300
 
36
 
 
37
#define TAG_SB 0x80
 
38
#define TAG_RCV 0x81
 
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
 
48
 
 
49
/* utility struct used to store the state of the connections in cam_tl_read_next
 
50
 */
 
51
typedef struct
 
52
{
 
53
  GList *active;
 
54
  GList *idle;
 
55
} CamTLConnectionsStatus;
 
56
 
 
57
void cam_gst_util_dump_mem (const guchar * mem, guint size);
 
58
 
 
59
CamTLConnection *
 
60
cam_tl_connection_new (CamTL * tl, guint8 id)
 
61
{
 
62
  CamTLConnection *connection;
 
63
 
 
64
  connection = g_new0 (CamTLConnection, 1);
 
65
  connection->tl = tl;
 
66
  connection->id = id;
 
67
  connection->state = CAM_TL_CONNECTION_STATE_CLOSED;
 
68
  connection->has_data = FALSE;
 
69
 
 
70
  return connection;
 
71
}
 
72
 
 
73
static void
 
74
cam_tl_connection_destroy (CamTLConnection * connection)
 
75
{
 
76
  if (connection->last_poll)
 
77
    g_timer_destroy (connection->last_poll);
 
78
 
 
79
  g_free (connection);
 
80
}
 
81
 
 
82
CamTL *
 
83
cam_tl_new (int fd)
 
84
{
 
85
  CamTL *tl;
 
86
 
 
87
  tl = g_new0 (CamTL, 1);
 
88
  tl->fd = fd;
 
89
  tl->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
 
90
      NULL, (GDestroyNotify) cam_tl_connection_destroy);
 
91
 
 
92
  return tl;
 
93
}
 
94
 
 
95
void
 
96
cam_tl_destroy (CamTL * tl)
 
97
{
 
98
  g_hash_table_destroy (tl->connections);
 
99
 
 
100
  g_free (tl);
 
101
}
 
102
 
 
103
/* read data from the module without blocking indefinitely */
 
104
static CamReturn
 
105
cam_tl_read_timeout (CamTL * tl, struct timeval *timeout)
 
106
{
 
107
  fd_set read_fd;
 
108
  int sret;
 
109
 
 
110
  FD_ZERO (&read_fd);
 
111
  FD_SET (tl->fd, &read_fd);
 
112
 
 
113
  sret = select (tl->fd + 1, &read_fd, NULL, NULL, timeout);
 
114
  if (sret == 0) {
 
115
    GST_DEBUG ("read timeout");
 
116
    return CAM_RETURN_TRANSPORT_TIMEOUT;
 
117
  }
 
118
 
 
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;
 
123
  }
 
124
 
 
125
  return CAM_RETURN_OK;
 
126
}
 
127
 
 
128
/* read data from the module using the default timeout */
 
129
static CamReturn
 
130
cam_tl_read (CamTL * tl)
 
131
{
 
132
  struct timeval timeout;
 
133
 
 
134
  timeout.tv_sec = READ_TIMEOUT_SEC;
 
135
  timeout.tv_usec = READ_TIMEOUT_USEC;
 
136
 
 
137
  return cam_tl_read_timeout (tl, &timeout);
 
138
}
 
139
 
 
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 */
 
143
void
 
144
cam_tl_calc_buffer_size (CamTL * tl, guint body_length,
 
145
    guint * buffer_size, guint * offset)
 
146
{
 
147
  guint length_field_len;
 
148
 
 
149
  /* the size of a TPDU is:
 
150
   * 1 byte slot number
 
151
   * 1 byte connection id 
 
152
   * length_field_len bytes length field 
 
153
   * 1 byte connection id
 
154
   * body_length bytes body
 
155
   */
 
156
 
 
157
  /* get the length of the lenght_field block */
 
158
  length_field_len = cam_calc_length_field_size (body_length);
 
159
 
 
160
  *offset = 3 + length_field_len + 1;
 
161
  *buffer_size = *offset + body_length;
 
162
}
 
163
 
 
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.
 
168
 */
 
169
static CamReturn
 
170
cam_tl_connection_write_tpdu (CamTLConnection * connection,
 
171
    guint8 tag, guint8 * buffer, guint buffer_size, guint body_length)
 
172
{
 
173
  int sret;
 
174
  CamTL *tl = connection->tl;
 
175
  guint8 length_field_len;
 
176
 
 
177
  /* slot number */
 
178
  buffer[0] = connection->slot;
 
179
  /* connection number */
 
180
  buffer[1] = connection->id;
 
181
  /* tag */
 
182
  buffer[2] = tag;
 
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;
 
186
 
 
187
  GST_DEBUG ("writing TPDU %x connection %d", buffer[2], connection->id);
 
188
 
 
189
  //cam_gst_util_dump_mem (buffer, buffer_size);
 
190
 
 
191
  sret = write (tl->fd, buffer, buffer_size);
 
192
  if (sret == -1) {
 
193
    GST_ERROR ("error witing TPDU (%d): %s", errno, g_strerror (errno));
 
194
    return CAM_RETURN_TRANSPORT_ERROR;
 
195
  }
 
196
 
 
197
  tl->expected_tpdus += 1;
 
198
 
 
199
  return CAM_RETURN_OK;
 
200
}
 
201
 
 
202
/* convenience function to write control TPDUs (TPDUs having a single-byte body)
 
203
 */
 
204
static CamReturn
 
205
cam_tl_connection_write_control_tpdu (CamTLConnection * connection, guint8 tag)
 
206
{
 
207
  guint8 tpdu[5];
 
208
 
 
209
  /* TPDU layout (5 bytes):
 
210
   *
 
211
   * slot number (1 byte)
 
212
   * connection id (1 byte)
 
213
   * tag (1 byte)
 
214
   * length (1 byte)
 
215
   * connection id (1 byte)
 
216
   */
 
217
 
 
218
  return cam_tl_connection_write_tpdu (connection, tag, tpdu, 5, 1);
 
219
}
 
220
 
 
221
/* read the next TPDU from the CAM */
 
222
static CamReturn
 
223
cam_tl_read_tpdu_next (CamTL * tl, CamTLConnection ** out_connection)
 
224
{
 
225
  CamReturn ret;
 
226
  CamTLConnection *connection;
 
227
  guint8 slot;
 
228
  guint8 connection_id;
 
229
  guint8 *tpdu;
 
230
  guint8 length_field_len;
 
231
  guint8 status;
 
232
 
 
233
  ret = cam_tl_read (tl);
 
234
  if (CAM_FAILED (ret))
 
235
    return ret;
 
236
 
 
237
  tpdu = tl->buffer;
 
238
 
 
239
  /* must hold at least slot, connection_id, 1byte length_field, connection_id
 
240
   */
 
241
  if (tl->buffer_size < 4) {
 
242
    GST_ERROR ("invalid TPDU length %d", tl->buffer_size);
 
243
    return CAM_RETURN_TRANSPORT_ERROR;
 
244
  }
 
245
 
 
246
  /* LPDU slot */
 
247
  slot = tpdu[0];
 
248
  /* LPDU connection id */
 
249
  connection_id = tpdu[1];
 
250
 
 
251
  connection = g_hash_table_lookup (tl->connections,
 
252
      GINT_TO_POINTER ((guint) connection_id));
 
253
  if (connection == NULL) {
 
254
    /* WHAT? */
 
255
    GST_ERROR ("CAM sent a TPDU on an unknown connection: %d", connection_id);
 
256
    return CAM_RETURN_TRANSPORT_ERROR;
 
257
  }
 
258
 
 
259
  /* read the length_field () */
 
260
  length_field_len = cam_read_length_field (&tpdu[3], &tl->body_length);
 
261
 
 
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;
 
266
  }
 
267
 
 
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;
 
272
 
 
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;
 
276
  }
 
277
 
 
278
  status = tl->buffer[tl->buffer_size - 1];
 
279
  if (status & 0x80) {
 
280
    connection->has_data = TRUE;
 
281
  } else {
 
282
    connection->has_data = FALSE;
 
283
  }
 
284
 
 
285
  GST_DEBUG ("received TPDU %x more data %d", tpdu[2], connection->has_data);
 
286
  tl->expected_tpdus -= 1;
 
287
 
 
288
  *out_connection = connection;
 
289
 
 
290
  return CAM_RETURN_OK;
 
291
}
 
292
 
 
293
/* create a connection with the module */
 
294
CamReturn
 
295
cam_tl_create_connection (CamTL * tl, guint8 slot,
 
296
    CamTLConnection ** connection)
 
297
{
 
298
  CamReturn ret;
 
299
  CamTLConnection *conn = NULL;
 
300
 
 
301
  if (tl->connection_ids == 255)
 
302
    return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS;
 
303
 
 
304
  conn = cam_tl_connection_new (tl, ++tl->connection_ids);
 
305
 
 
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))
 
309
    goto error;
 
310
 
 
311
  g_hash_table_insert (tl->connections, GINT_TO_POINTER (conn->id), conn);
 
312
 
 
313
  *connection = conn;
 
314
 
 
315
  return CAM_RETURN_OK;
 
316
 
 
317
error:
 
318
  if (conn)
 
319
    cam_tl_connection_destroy (conn);
 
320
 
 
321
  return ret;
 
322
}
 
323
 
 
324
CamReturn
 
325
cam_tl_connection_delete (CamTLConnection * connection)
 
326
{
 
327
  CamReturn ret;
 
328
 
 
329
  ret = cam_tl_connection_write_control_tpdu (connection, TAG_DELETE_T_C);
 
330
  if (CAM_FAILED (ret))
 
331
    return ret;
 
332
 
 
333
  connection->state = CAM_TL_CONNECTION_STATE_IN_DELETION;
 
334
 
 
335
  return CAM_RETURN_OK;
 
336
}
 
337
 
 
338
CamReturn
 
339
handle_control_tpdu (CamTL * tl, CamTLConnection * connection)
 
340
{
 
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;
 
344
  }
 
345
 
 
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;
 
353
 
 
354
        if (tl->connection_created)
 
355
          tl->connection_created (tl, connection);
 
356
      }
 
357
      break;
 
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);
 
362
 
 
363
      if (tl->connection_deleted)
 
364
        tl->connection_deleted (tl, connection);
 
365
 
 
366
      g_hash_table_remove (tl->connections,
 
367
          GINT_TO_POINTER ((guint) connection->id));
 
368
      break;
 
369
  }
 
370
 
 
371
  return CAM_RETURN_OK;
 
372
}
 
373
 
 
374
CamReturn
 
375
handle_data_tpdu (CamTL * tl, CamTLConnection * connection)
 
376
{
 
377
  if (tl->body_length == 0) {
 
378
    /* FIXME: figure out why this seems to happen from time to time with the
 
379
     * predator cam */
 
380
    GST_WARNING ("Empty data TPDU received");
 
381
    return CAM_RETURN_OK;
 
382
  }
 
383
 
 
384
  if (tl->connection_data)
 
385
    return tl->connection_data (tl, connection, tl->body, tl->body_length);
 
386
 
 
387
  return CAM_RETURN_OK;
 
388
}
 
389
 
 
390
static void
 
391
foreach_connection_get (gpointer key, gpointer value, gpointer user_data)
 
392
{
 
393
  GList **lst = (GList **) user_data;
 
394
 
 
395
  *lst = g_list_append (*lst, value);
 
396
}
 
397
 
 
398
CamReturn
 
399
cam_tl_connection_poll (CamTLConnection * connection, gboolean force)
 
400
{
 
401
  CamReturn ret;
 
402
 
 
403
  if (connection->last_poll == NULL) {
 
404
    connection->last_poll = g_timer_new ();
 
405
  } else if (!force &&
 
406
      g_timer_elapsed (connection->last_poll, NULL) < POLL_INTERVAL) {
 
407
    return CAM_RETURN_TRANSPORT_POLL;
 
408
  }
 
409
 
 
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))
 
413
    return ret;
 
414
 
 
415
  g_timer_start (connection->last_poll);
 
416
 
 
417
  return CAM_RETURN_OK;
 
418
}
 
419
 
 
420
/* read all the queued TPDUs */
 
421
CamReturn
 
422
cam_tl_read_all (CamTL * tl, gboolean poll)
 
423
{
 
424
  CamReturn ret = CAM_RETURN_OK;
 
425
  CamTLConnection *connection;
 
426
  GList *connections = NULL;
 
427
  GList *walk;
 
428
  gboolean done = FALSE;
 
429
 
 
430
  while (!done) {
 
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);
 
436
        goto out;
 
437
      }
 
438
 
 
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);
 
444
          break;
 
445
        case TAG_DATA_MORE:
 
446
        case TAG_DATA_LAST:
 
447
          connection->empty_data = 0;
 
448
          ret = handle_data_tpdu (tl, connection);
 
449
          break;
 
450
        case TAG_SB:
 
451
          /* this is handled by tpdu_next */
 
452
          break;
 
453
      }
 
454
 
 
455
      if (CAM_FAILED (ret))
 
456
        goto out;
 
457
    }
 
458
 
 
459
    done = TRUE;
 
460
 
 
461
    connections = NULL;
 
462
    g_hash_table_foreach (tl->connections,
 
463
        foreach_connection_get, &connections);
 
464
 
 
465
    for (walk = connections; walk; walk = walk->next) {
 
466
      CamTLConnection *connection = CAM_TL_CONNECTION (walk->data);
 
467
 
 
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);
 
472
          goto out;
 
473
        }
 
474
        /* increment the empty_data counter. If we get data, this will be reset
 
475
         * to 0 */
 
476
        connection->empty_data++;
 
477
        done = FALSE;
 
478
      } else if (poll) {
 
479
        ret = cam_tl_connection_poll (connection, FALSE);
 
480
        if (ret == CAM_RETURN_TRANSPORT_POLL)
 
481
          continue;
 
482
 
 
483
        if (CAM_FAILED (ret)) {
 
484
          g_list_free (connections);
 
485
          goto out;
 
486
        }
 
487
 
 
488
        done = FALSE;
 
489
      }
 
490
    }
 
491
 
 
492
    g_list_free (connections);
 
493
  }
 
494
 
 
495
out:
 
496
  return ret;
 
497
}
 
498
 
 
499
CamReturn
 
500
cam_tl_connection_write (CamTLConnection * connection,
 
501
    guint8 * buffer, guint buffer_size, guint body_length)
 
502
{
 
503
  return cam_tl_connection_write_tpdu (connection,
 
504
      TAG_DATA_LAST, buffer, buffer_size, 1 + body_length);
 
505
}