2007-10-16 17:38:05 +00:00
|
|
|
/*
|
|
|
|
* camapplication.c - GStreamer CAM (EN50221) Application Layer
|
|
|
|
* Copyright (C) 2007 Alessandro Decina
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Alessandro Decina <alessandro@nnva.org>
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "camapplication.h"
|
|
|
|
|
|
|
|
#define GST_CAT_DEFAULT cam_debug_cat
|
|
|
|
|
2012-05-28 13:48:33 +00:00
|
|
|
/* 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 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";
|
|
|
|
}
|
|
|
|
|
2007-10-16 17:38:05 +00:00
|
|
|
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)
|
|
|
|
{
|
2007-10-17 10:33:43 +00:00
|
|
|
guint resource_id = GPOINTER_TO_UINT (key);
|
2007-10-16 17:38:05 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2012-05-28 13:48:33 +00:00
|
|
|
GST_DEBUG ("tag:0x%x (%s), buffer_size:%d, body_length:%d", tag,
|
|
|
|
tag_get_name (tag), buffer_size, body_length);
|
|
|
|
|
2007-10-16 17:38:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-05-28 13:48:33 +00:00
|
|
|
GST_DEBUG ("Got tag 0x%x (%s) , length:%d", tag, tag_get_name (tag), length);
|
|
|
|
|
2007-10-16 17:38:05 +00:00
|
|
|
return application->data (application, session,
|
|
|
|
tag, data + 3 + length_field_len, length);
|
|
|
|
}
|