/* * camapplication.c - GStreamer CAM (EN50221) Application Layer * Copyright (C) 2007 Alessandro Decina * * Authors: * Alessandro Decina * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "camapplication.h" #define GST_CAT_DEFAULT cam_debug_cat /* Resource Manager */ #define TAG_PROFILE_ENQUIRY 0x9F8010 #define TAG_PROFILE_REPLY 0x9F8011 #define TAG_PROFILE_CHANGE 0x9F8012 /* Application Info */ #define TAG_APPLICATION_INFO_ENQUIRY 0x9F8020 #define TAG_APPLICATION_INFO_REPLY 0x9F8021 #define TAG_APPLICATION_INFO_ENTER_MENU 0x9F8022 /* Conditional Access */ #define TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY 0x9F8030 #define TAG_CONDITIONAL_ACCESS_INFO_REPLY 0x9F8031 #define TAG_CONDITIONAL_ACCESS_PMT 0x9F8032 #define TAG_CONDITIONAL_ACCESS_PMT_REPLY 0x9F8033 typedef struct { guint tagid; const gchar *description; } CamTagMessage; static const CamTagMessage debugmessage[] = { {TAG_PROFILE_ENQUIRY, "PROFILE_ENQUIRY"}, {TAG_PROFILE_REPLY, "PROFILE_REPLY"}, {TAG_PROFILE_CHANGE, "PROFILE_CHANGE"}, {TAG_APPLICATION_INFO_ENQUIRY, "APPLICATION_INFO_ENQUIRY"}, {TAG_APPLICATION_INFO_REPLY, "APPLICATION_INFO_REPLY"}, {TAG_APPLICATION_INFO_ENTER_MENU, "APPLICATION_INFO_ENTER_MENU"}, {TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY, "CONDITIONAL_ACCESS_INFO_ENQUIRY"}, {TAG_CONDITIONAL_ACCESS_INFO_REPLY, "CONDITIONAL_ACCESS_INFO_REPLY"}, {TAG_CONDITIONAL_ACCESS_PMT, "CONDITIONAL_ACCESS_PMT"}, {TAG_CONDITIONAL_ACCESS_PMT_REPLY, "CONDITIONAL_ACCESS_PMT_REPLY"} }; static inline const gchar * tag_get_name (guint tagid) { guint i; for (i = 0; i < G_N_ELEMENTS (debugmessage); i++) if (debugmessage[i].tagid == tagid) return debugmessage[i].description; return "UNKNOWN"; } static CamReturn open_session_request_cb (CamSL * sl, CamSLSession * session, CamSLResourceStatus * status); static CamReturn session_opened_cb (CamSL * sl, CamSLSession * session); static CamReturn session_closed_cb (CamSL * sl, CamSLSession * session); static CamReturn session_data_cb (CamSL * sl, CamSLSession * session, guint8 * data, guint length); static guint resource_id_hash (gconstpointer key) { guint resource_id = GPOINTER_TO_UINT (key); if (!CAM_AL_RESOURCE_ID_IS_PUBLIC (resource_id)) { /* private identifier, leave it as is */ return resource_id; } /* public identifier, mask out the version number */ return resource_id >> 6; } CamAL * cam_al_new (CamSL * sl) { CamAL *al = g_new0 (CamAL, 1); al->sl = sl; al->applications = g_hash_table_new (resource_id_hash, g_direct_equal); sl->user_data = al; sl->open_session_request = open_session_request_cb; sl->session_opened = session_opened_cb; sl->session_closed = session_closed_cb; sl->session_data = session_data_cb; return al; } void cam_al_destroy (CamAL * al) { g_hash_table_destroy (al->applications); g_free (al); } gboolean cam_al_install (CamAL * al, CamALApplication * application) { if (g_hash_table_lookup (al->applications, GINT_TO_POINTER (application->resource_id)) != NULL) return FALSE; application->al = al; g_hash_table_insert (al->applications, GINT_TO_POINTER (application->resource_id), application); return TRUE; } gboolean cam_al_uninstall (CamAL * al, CamALApplication * application) { gboolean ret; ret = g_hash_table_remove (al->applications, GINT_TO_POINTER (application->resource_id)); return ret; } CamALApplication * cam_al_get (CamAL * al, guint resource_id) { return CAM_AL_APPLICATION (g_hash_table_lookup (al->applications, GINT_TO_POINTER (resource_id))); } void _cam_al_application_init (CamALApplication * application) { application->sessions = NULL; } void _cam_al_application_destroy (CamALApplication * application) { g_list_free (application->sessions); } static void foreach_get_key (gpointer key, gpointer value, gpointer user_data) { GList **lst = (GList **) user_data; *lst = g_list_append (*lst, key); } GList * cam_al_get_resource_ids (CamAL * al) { GList *resource_ids = NULL; g_hash_table_foreach (al->applications, foreach_get_key, &resource_ids); return resource_ids; } void cam_al_calc_buffer_size (CamAL * al, guint body_length, guint * buffer_size, guint * offset) { guint apdu_header_length; guint8 length_field_len; /* get the length of the lenght_field() */ length_field_len = cam_calc_length_field_size (body_length); /* sum the APDU header */ apdu_header_length = 3 + length_field_len; /* chain up to the session layer to get the size of the buffer that can * contain the whole APDU */ cam_sl_calc_buffer_size (al->sl, apdu_header_length + body_length, buffer_size, offset); /* add the APDU header to the SPDU offset */ *offset += apdu_header_length; } CamReturn cam_al_application_write (CamALApplication * application, CamSLSession * session, guint tag, guint8 * buffer, guint buffer_size, guint body_length) { guint length_field_len; guint apdu_header_length; guint8 *apdu; GST_DEBUG ("tag:0x%x (%s), buffer_size:%d, body_length:%d", tag, tag_get_name (tag), buffer_size, body_length); length_field_len = cam_calc_length_field_size (body_length); apdu_header_length = 3 + length_field_len; apdu = (buffer + buffer_size) - body_length - apdu_header_length; apdu[0] = tag >> 16; apdu[1] = (tag >> 8) & 0xFF; apdu[2] = tag & 0xFF; cam_write_length_field (&apdu[3], body_length); return cam_sl_session_write (session, buffer, buffer_size, apdu_header_length + body_length); } static CamReturn open_session_request_cb (CamSL * sl, CamSLSession * session, CamSLResourceStatus * status) { CamAL *al = CAM_AL (sl->user_data); CamALApplication *application; guint resource_id = session->resource_id; CamReturn ret; application = g_hash_table_lookup (al->applications, GINT_TO_POINTER (resource_id)); if (application == NULL) { *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND; return CAM_RETURN_OK; } if (CAM_AL_RESOURCE_ID_VERSION (application->resource_id) < CAM_AL_RESOURCE_ID_VERSION (resource_id)) { *status = CAM_SL_RESOURCE_STATUS_INVALID_VERSION; return CAM_RETURN_OK; } ret = application->session_request (application, session, status); if (CAM_FAILED (ret)) { *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND; return ret; } if (*status == CAM_SL_RESOURCE_STATUS_OPEN) { session->user_data = application; application->sessions = g_list_append (application->sessions, session); } return CAM_RETURN_OK; } static CamReturn session_opened_cb (CamSL * sl, CamSLSession * session) { CamALApplication *application; application = CAM_AL_APPLICATION (session->user_data); if (application == NULL) { GST_ERROR ("session is established but has no application"); return CAM_RETURN_APPLICATION_ERROR; } return application->open (application, session); } static CamReturn session_closed_cb (CamSL * sl, CamSLSession * session) { CamALApplication *application; CamReturn ret; GList *walk; application = CAM_AL_APPLICATION (session->user_data); if (application == NULL) { GST_ERROR ("session is established but has no application"); return CAM_RETURN_APPLICATION_ERROR; } ret = application->close (application, session); for (walk = application->sessions; walk; walk = g_list_next (walk)) { CamSLSession *s = CAM_SL_SESSION (walk->data); if (s->session_nb == session->session_nb) { application->sessions = g_list_delete_link (application->sessions, walk); break; } } return ret; } static CamReturn session_data_cb (CamSL * sl, CamSLSession * session, guint8 * data, guint size) { CamALApplication *application; guint tag = 0; guint8 length_field_len; guint length; guint i; application = CAM_AL_APPLICATION (session->user_data); if (application == NULL) { GST_ERROR ("session is established but has no application"); return CAM_RETURN_APPLICATION_ERROR; } if (size < 4) { GST_ERROR ("invalid APDU length %d", size); return CAM_RETURN_APPLICATION_ERROR; } for (i = 0; i < 3; ++i) tag = (tag << 8) | data[i]; length_field_len = cam_read_length_field (&data[3], &length); if (length != size - 4) { GST_ERROR ("unexpected APDU length %d expected %d", length, size); return CAM_RETURN_APPLICATION_ERROR; } GST_DEBUG ("Got tag 0x%x (%s) , length:%d", tag, tag_get_name (tag), length); return application->data (application, session, tag, data + 3 + length_field_len, length); }