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; }
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; }