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;
}
/* create a connection with the module */
CamReturn
cam_tl_create_connection (CamTL * tl, guint8 slot,
    CamTLConnection ** connection)
{
  CamReturn ret;
  CamTLConnection *conn = NULL;
  guint count = 10;

  if (tl->connection_ids == 255)
    return CAM_RETURN_TRANSPORT_TOO_MANY_CONNECTIONS;

  conn = cam_tl_connection_new (tl, ++tl->connection_ids);

  /* Some CA devices take a long time to set themselves up,
   * therefore retry every 250ms (for a maximum of 2.5s)
   */
  while (TRUE) {
    /* send a TAG_CREATE_T_C TPDU */
    ret = cam_tl_connection_write_control_tpdu (conn, TAG_CREATE_T_C);
    if (!CAM_FAILED (ret))
      break;
    if (!count)
      goto error;
    GST_DEBUG ("Failed sending initial connection message .. but retrying");
    count--;
    /* Wait 250ms */
    g_usleep (G_USEC_PER_SEC / 4);
  }

  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_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;
}
/* read the next TPDU from the CAM */
static CamReturn
cam_tl_read_tpdu_next (CamTL * tl, CamTLConnection ** out_connection)
{
  CamReturn ret;
  CamTLConnection *connection;
  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 (%s) more data %d", tpdu[2],
      tag_get_name (tpdu[2]), connection->has_data);
  tl->expected_tpdus -= 1;

  *out_connection = connection;

  return CAM_RETURN_OK;
}
Exemple #6
0
gboolean
cam_device_open (CamDevice * device, const char *filename)
{
  ca_caps_t ca_caps;
  int ret;
  int i;
  int count = 10;

  g_return_val_if_fail (device != NULL, FALSE);
  g_return_val_if_fail (device->state == CAM_DEVICE_STATE_CLOSED, FALSE);
  g_return_val_if_fail (filename != NULL, FALSE);

  GST_INFO ("opening ca device %s", filename);

  ret = open (filename, O_RDWR);
  if (ret == -1) {
    GST_ERROR ("can't open ca device: %s", strerror (errno));
    return FALSE;
  }

  GST_DEBUG ("Successfully opened device %s", filename);

  device->fd = ret;

  ret = ioctl (device->fd, CA_RESET);

  g_usleep (G_USEC_PER_SEC / 10);

  while (TRUE) {
    /* get the capabilities of the CA */
    ret = ioctl (device->fd, CA_GET_CAP, &ca_caps);
    if (ret == -1) {
      GST_ERROR ("CA_GET_CAP ioctl failed: %s", strerror (errno));
      reset_state (device);
      return FALSE;
    }
    if (ca_caps.slot_num > 0)
      break;
    if (!count) {
      GST_ERROR ("CA_GET_CAP succeeded but not slots");
      reset_state (device);
      return FALSE;
    }
    count--;
    g_usleep (G_USEC_PER_SEC / 5);
  }

  device->tl = cam_tl_new (device->fd);
  device->sl = cam_sl_new (device->tl);
  device->al = cam_al_new (device->sl);

  device->mgr = cam_resource_manager_new ();
  cam_al_install (device->al, CAM_AL_APPLICATION (device->mgr));

  device->info = cam_application_info_new ();
  cam_al_install (device->al, CAM_AL_APPLICATION (device->info));

  device->cas = cam_conditional_access_new ();
  cam_al_install (device->al, CAM_AL_APPLICATION (device->cas));

  /* open a connection to each slot */
  for (i = 0; i < ca_caps.slot_num; ++i) {
    CamTLConnection *connection;

    ret = cam_tl_create_connection (device->tl, i, &connection);
    if (CAM_FAILED (ret)) {
      /* just ignore the slot, error out later only if no connection has been
       * established */
      GST_WARNING ("connection to slot %d failed, error: %d", i, ret);
      continue;
    }
  }

  if (g_hash_table_size (device->tl->connections) == 0) {
    GST_ERROR ("couldn't connect to any slot");

    reset_state (device);
    return FALSE;
  }

  device->state = CAM_DEVICE_STATE_OPEN;
  device->filename = g_strdup (filename);

  /* poll each connection to initiate the protocol */
  cam_tl_read_all (device->tl, TRUE);

  return TRUE;
}