mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
595 lines
15 KiB
C
595 lines
15 KiB
C
/*
|
|
* camsession.c - GStreamer CAM (EN50221) Session 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 "camsession.h"
|
|
|
|
#define GST_CAT_DEFAULT cam_debug_cat
|
|
#define I_TAG 0
|
|
#define I_LENGTH_FB 1
|
|
|
|
#define TAG_SESSION_NUMBER 0x90
|
|
#define TAG_OPEN_SESSION_REQUEST 0x91
|
|
#define TAG_OPEN_SESSION_RESPONSE 0x92
|
|
#define TAG_CREATE_SESSION 0x93
|
|
#define TAG_CREATE_SESSION_RESPONSE 0x94
|
|
#define TAG_CLOSE_SESSION_REQUEST 0x95
|
|
#define TAG_CLOSE_SESSION_RESPONSE 0x96
|
|
|
|
static CamReturn connection_data_cb (CamTL * tl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length);
|
|
|
|
static CamSLSession *
|
|
cam_sl_session_new (CamSL * sl, CamTLConnection * connection,
|
|
guint16 session_nb, guint resource_id)
|
|
{
|
|
CamSLSession *session = g_new0 (CamSLSession, 1);
|
|
|
|
session->state = CAM_SL_SESSION_STATE_IDLE;
|
|
session->sl = sl;
|
|
session->connection = connection;
|
|
session->session_nb = session_nb;
|
|
session->resource_id = resource_id;
|
|
|
|
return session;
|
|
}
|
|
|
|
static void
|
|
cam_sl_session_destroy (CamSLSession * session)
|
|
{
|
|
g_free (session);
|
|
}
|
|
|
|
CamSL *
|
|
cam_sl_new (CamTL * tl)
|
|
{
|
|
CamSL *sl = g_new0 (CamSL, 1);
|
|
|
|
sl->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
|
NULL, (GDestroyNotify) cam_sl_session_destroy);
|
|
|
|
tl->user_data = sl;
|
|
tl->connection_data = connection_data_cb;
|
|
|
|
return sl;
|
|
}
|
|
|
|
void
|
|
cam_sl_destroy (CamSL * sl)
|
|
{
|
|
g_hash_table_destroy (sl->sessions);
|
|
|
|
g_free (sl);
|
|
}
|
|
|
|
CamReturn
|
|
cam_sl_create_session (CamSL * sl,
|
|
CamTLConnection * connection, guint resource_id,
|
|
CamSLSession ** out_session)
|
|
{
|
|
CamReturn ret;
|
|
CamSLSession *session = NULL;
|
|
guint size;
|
|
guint offset;
|
|
guint8 *tpdu = NULL;
|
|
guint8 *spdu;
|
|
guint16 session_nb;
|
|
|
|
/* FIXME: implement session number allocations properly */
|
|
if (sl->session_ids == G_MAXUINT16)
|
|
return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
|
|
|
|
session_nb = ++sl->session_ids;
|
|
session = cam_sl_session_new (sl, connection, session_nb, resource_id);
|
|
|
|
/* SPDU layout (8 bytes):
|
|
* TAG_CREATE_SESSION 1 byte
|
|
* length_field () 1 byte
|
|
* resource_id 4 bytes
|
|
* session_nb 2 bytes
|
|
*/
|
|
|
|
/* get TPDU size */
|
|
cam_tl_calc_buffer_size (sl->tl, 8, &size, &offset);
|
|
|
|
tpdu = (guint8 *) g_malloc (size);
|
|
spdu = tpdu + offset;
|
|
|
|
/* SPDU header */
|
|
/* tag */
|
|
spdu[0] = TAG_CREATE_SESSION;
|
|
/* fixed length_field */
|
|
spdu[1] = 6;
|
|
|
|
/* SPDU body */
|
|
/* resource id */
|
|
GST_WRITE_UINT32_BE (&spdu[2], resource_id);
|
|
/* session_nb */
|
|
GST_WRITE_UINT16_BE (&spdu[6], session_nb);
|
|
|
|
/* write the TPDU */
|
|
ret = cam_tl_connection_write (session->connection, tpdu, size, 8);
|
|
if (CAM_FAILED (ret))
|
|
goto error;
|
|
|
|
*out_session = session;
|
|
|
|
g_free (tpdu);
|
|
return CAM_RETURN_OK;
|
|
|
|
error:
|
|
if (session)
|
|
cam_sl_session_destroy (session);
|
|
|
|
g_free (tpdu);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* send a TAG_CLOSE_SESSION SPDU */
|
|
CamReturn
|
|
cam_sl_session_close (CamSLSession * session)
|
|
{
|
|
CamReturn ret;
|
|
guint size;
|
|
guint offset;
|
|
guint8 *tpdu = NULL;
|
|
guint8 *spdu;
|
|
CamSL *sl = session->sl;
|
|
|
|
/* SPDU layout (4 bytes):
|
|
* TAG_CLOSE_SESSION 1 byte
|
|
* length_field () 1 byte
|
|
* session_nb 2 bytes
|
|
*/
|
|
|
|
/* get the size of the TPDU */
|
|
cam_tl_calc_buffer_size (sl->tl, 4, &size, &offset);
|
|
|
|
tpdu = (guint8 *) g_malloc (size);
|
|
/* the spdu header starts after the TPDU headers */
|
|
spdu = tpdu + offset;
|
|
|
|
/* SPDU header */
|
|
/* tag */
|
|
spdu[0] = TAG_CLOSE_SESSION_REQUEST;
|
|
/* fixed length_field */
|
|
spdu[1] = 2;
|
|
/* SPDU body */
|
|
/* session_nb */
|
|
GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
|
|
|
|
/* write the TPDU */
|
|
ret = cam_tl_connection_write (session->connection, tpdu, size, 4);
|
|
if (CAM_FAILED (ret))
|
|
goto error;
|
|
|
|
session->state = CAM_SL_SESSION_STATE_CLOSING;
|
|
|
|
g_free (tpdu);
|
|
|
|
return CAM_RETURN_OK;
|
|
|
|
error:
|
|
g_free (tpdu);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
cam_sl_calc_buffer_size (CamSL * sl, guint body_length,
|
|
guint * buffer_size, guint * offset)
|
|
{
|
|
/* an APDU is sent in a SESSION_NUMBER SPDU, which has a fixed header size (4
|
|
* bytes) */
|
|
cam_tl_calc_buffer_size (sl->tl, 4 + body_length, buffer_size, offset);
|
|
*offset += 4;
|
|
}
|
|
|
|
CamReturn
|
|
cam_sl_session_write (CamSLSession * session,
|
|
guint8 * buffer, guint buffer_size, guint body_length)
|
|
{
|
|
guint8 *spdu;
|
|
|
|
/* SPDU layout (4 + body_length bytes):
|
|
* TAG_SESSION_NUMBER (1 byte)
|
|
* length_field (1 byte)
|
|
* session number (2 bytes)
|
|
* one or more APDUs (body_length bytes)
|
|
*/
|
|
|
|
spdu = (buffer + buffer_size) - body_length - 4;
|
|
spdu[0] = TAG_SESSION_NUMBER;
|
|
spdu[1] = 2;
|
|
GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
|
|
|
|
/* add our header to the body length */
|
|
return cam_tl_connection_write (session->connection,
|
|
buffer, buffer_size, 4 + body_length);
|
|
}
|
|
|
|
static CamReturn
|
|
send_open_session_response (CamSL * sl, CamSLSession * session, guint8 status)
|
|
{
|
|
CamReturn ret;
|
|
guint8 *tpdu;
|
|
guint size;
|
|
guint offset;
|
|
guint8 *spdu;
|
|
|
|
/* SPDU layout (9 bytes):
|
|
* TAG_OPEN_SESSION_RESPONSE 1 byte
|
|
* length_field () 1 byte
|
|
* session_status 1 byte
|
|
* resource_id 4 bytes
|
|
* session_nb 2 bytes
|
|
*/
|
|
|
|
cam_tl_calc_buffer_size (session->sl->tl, 9, &size, &offset);
|
|
|
|
tpdu = g_malloc0 (size);
|
|
spdu = tpdu + offset;
|
|
|
|
spdu[0] = TAG_OPEN_SESSION_RESPONSE;
|
|
/* fixed length_field () */
|
|
spdu[1] = 7;
|
|
spdu[2] = status;
|
|
GST_WRITE_UINT32_BE (&spdu[3], session->resource_id);
|
|
GST_WRITE_UINT16_BE (&spdu[7], session->session_nb);
|
|
|
|
ret = cam_tl_connection_write (session->connection, tpdu, size, 9);
|
|
g_free (tpdu);
|
|
if (CAM_FAILED (ret))
|
|
return ret;
|
|
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
static CamReturn
|
|
send_close_session_response (CamSL * sl, CamSLSession * session, guint8 status)
|
|
{
|
|
CamReturn ret;
|
|
guint8 *tpdu;
|
|
guint size;
|
|
guint offset;
|
|
guint8 *spdu;
|
|
|
|
/* SPDU layout (5 bytes):
|
|
* TAG_CLOSE_SESSION_RESPONSE 1 byte
|
|
* length_field () 1 byte
|
|
* session_status 1 byte
|
|
* session_nb 2 bytes
|
|
*/
|
|
|
|
cam_tl_calc_buffer_size (session->sl->tl, 5, &size, &offset);
|
|
|
|
tpdu = g_malloc0 (size);
|
|
spdu = tpdu + offset;
|
|
|
|
spdu[0] = TAG_OPEN_SESSION_RESPONSE;
|
|
/* fixed length_field() */
|
|
spdu[1] = 3;
|
|
spdu[2] = status;
|
|
GST_WRITE_UINT16_BE (&spdu[3], session->session_nb);
|
|
|
|
ret = cam_tl_connection_write (session->connection, tpdu, size, 5);
|
|
g_free (tpdu);
|
|
if (CAM_FAILED (ret))
|
|
return ret;
|
|
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
static CamReturn
|
|
handle_open_session_request (CamSL * sl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length)
|
|
{
|
|
CamReturn ret;
|
|
guint resource_id;
|
|
guint status;
|
|
guint16 session_nb;
|
|
CamSLSession *session;
|
|
|
|
/* SPDU layout (6 bytes):
|
|
* TAG_OPEN_SESSION_REQUEST (1 byte)
|
|
* length_field() (1 byte)
|
|
* resource id (4 bytes)
|
|
*/
|
|
if (spdu_length != 6) {
|
|
GST_ERROR ("expected OPEN_SESSION_REQUEST to be 6 bytes, got %d",
|
|
spdu_length);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
/* skip tag and length_field () */
|
|
resource_id = GST_READ_UINT32_BE (&spdu[2]);
|
|
|
|
/* create a new session */
|
|
if (sl->session_ids == G_MAXUINT16) {
|
|
GST_ERROR ("too many sessions opened");
|
|
return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
|
|
}
|
|
|
|
session_nb = ++sl->session_ids;
|
|
session = cam_sl_session_new (sl, connection, session_nb, resource_id);
|
|
|
|
GST_INFO ("session request: %d %x", session_nb, session->resource_id);
|
|
|
|
if (sl->open_session_request) {
|
|
/* forward the request to the upper layer */
|
|
ret = sl->open_session_request (sl, session, &status);
|
|
if (CAM_FAILED (ret))
|
|
goto error;
|
|
} else {
|
|
status = 0xF0;
|
|
}
|
|
|
|
ret = send_open_session_response (sl, session, (guint8) status);
|
|
if (CAM_FAILED (ret))
|
|
goto error;
|
|
|
|
GST_INFO ("session request response: %d %x", session_nb, status);
|
|
|
|
if (status == CAM_SL_RESOURCE_STATUS_OPEN) {
|
|
/* if the session has been accepted add it and signal */
|
|
session->state = CAM_SL_SESSION_STATE_ACTIVE;
|
|
g_hash_table_insert (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session_nb), session);
|
|
|
|
if (sl->session_opened) {
|
|
/* notify the upper layer */
|
|
ret = sl->session_opened (sl, session);
|
|
if (CAM_FAILED (ret))
|
|
return ret;
|
|
}
|
|
} else {
|
|
/* session request wasn't accepted */
|
|
cam_sl_session_destroy (session);
|
|
}
|
|
|
|
return CAM_RETURN_OK;
|
|
|
|
error:
|
|
cam_sl_session_destroy (session);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static CamReturn
|
|
handle_create_session_response (CamSL * sl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length)
|
|
{
|
|
guint8 status;
|
|
guint resource_id;
|
|
guint16 session_nb;
|
|
CamSLSession *session;
|
|
|
|
/* SPDU layout (9 bytes):
|
|
* TAG_CREATE_SESSION_RESPONSE (1 byte)
|
|
* length_field() (1 byte)
|
|
* status (1 byte)
|
|
* resource id (4 bytes)
|
|
* session number (2 bytes)
|
|
*/
|
|
if (spdu_length != 9) {
|
|
GST_ERROR ("expected CREATE_SESSION_RESPONSE to be 9 bytes, got %d",
|
|
spdu_length);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
/* skip tag and length */
|
|
status = spdu[2];
|
|
resource_id = GST_READ_UINT32_BE (&spdu[3]);
|
|
session_nb = GST_READ_UINT16_BE (&spdu[7]);
|
|
|
|
session = g_hash_table_lookup (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session_nb));
|
|
if (session == NULL) {
|
|
GST_DEBUG ("got CREATE_SESSION_RESPONSE for unknown session: %d",
|
|
session_nb);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
|
|
GST_DEBUG ("ignoring CREATE_SESSION_RESPONSE for closing session: %d",
|
|
session_nb);
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
session->state = CAM_SL_SESSION_STATE_ACTIVE;
|
|
|
|
GST_DEBUG ("session opened %d", session->session_nb);
|
|
|
|
if (sl->session_opened)
|
|
/* notify the upper layer */
|
|
return sl->session_opened (sl, session);
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
static CamReturn
|
|
handle_close_session_request (CamSL * sl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length)
|
|
{
|
|
CamReturn ret;
|
|
guint16 session_nb;
|
|
CamSLSession *session;
|
|
guint8 status = 0;
|
|
|
|
/* SPDU layout (4 bytes):
|
|
* TAG_CLOSE_SESSION_REQUEST (1 byte)
|
|
* length_field () (1 byte)
|
|
* session number (2 bytes)
|
|
*/
|
|
if (spdu_length != 4) {
|
|
GST_ERROR ("expected CLOSE_SESSION_REQUEST to be 4 bytes, got %d",
|
|
spdu_length);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
/* skip tag and length_field() */
|
|
session_nb = GST_READ_UINT16_BE (&spdu[2]);
|
|
|
|
GST_DEBUG ("close session request %d", session_nb);
|
|
|
|
session = g_hash_table_lookup (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session_nb));
|
|
if (session == NULL) {
|
|
GST_WARNING ("got CLOSE_SESSION_REQUEST for unknown session: %d",
|
|
session_nb);
|
|
|
|
status = 0xF0;
|
|
} else if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
|
|
GST_WARNING ("got CLOSE_SESSION_REQUEST for closing session: %d",
|
|
session_nb);
|
|
|
|
status = 0xF0;
|
|
}
|
|
|
|
GST_DEBUG ("close session response: %d %d", session->session_nb, status);
|
|
|
|
ret = send_close_session_response (sl, session, status);
|
|
if (CAM_FAILED (ret))
|
|
return ret;
|
|
|
|
if (session->state != CAM_SL_SESSION_STATE_CLOSING) {
|
|
GST_DEBUG ("session closed %d", session->session_nb);
|
|
|
|
if (sl->session_closed)
|
|
ret = sl->session_closed (sl, session);
|
|
|
|
g_hash_table_remove (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session->session_nb));
|
|
|
|
if (CAM_FAILED (ret))
|
|
return ret;
|
|
}
|
|
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
static CamReturn
|
|
handle_close_session_response (CamSL * sl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length)
|
|
{
|
|
guint16 session_nb;
|
|
CamSLSession *session;
|
|
CamReturn ret = CAM_RETURN_OK;
|
|
|
|
/* SPDU layout (5 bytes):
|
|
* TAG_CLOSE_SESSION_RESPONSE (1 byte)
|
|
* length_field () (1 byte)
|
|
* status (1 byte)
|
|
* session number (2 bytes)
|
|
*/
|
|
|
|
if (spdu_length != 5) {
|
|
GST_ERROR ("expected CLOSE_SESSION_RESPONSE to be 5 bytes, got %d",
|
|
spdu_length);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
/* skip tag, length_field() and session_status */
|
|
session_nb = GST_READ_UINT16_BE (&spdu[3]);
|
|
|
|
session = g_hash_table_lookup (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session_nb));
|
|
if (session == NULL || session->state != CAM_SL_SESSION_STATE_ACTIVE) {
|
|
GST_ERROR ("unexpected CLOSED_SESSION_RESPONSE");
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
GST_DEBUG ("session closed %d", session->session_nb);
|
|
|
|
if (sl->session_closed)
|
|
ret = sl->session_closed (sl, session);
|
|
|
|
g_hash_table_remove (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session->session_nb));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static CamReturn
|
|
handle_session_data (CamSL * sl, CamTLConnection * connection,
|
|
guint8 * spdu, guint length)
|
|
{
|
|
guint16 session_nb;
|
|
CamSLSession *session;
|
|
|
|
/* SPDU layout (>= 4 bytes):
|
|
* TAG_SESSION_NUMBER (1 byte)
|
|
* length_field() (1 byte)
|
|
* session number (2 bytes)
|
|
* one or more APDUs
|
|
*/
|
|
|
|
if (length < 4) {
|
|
GST_ERROR ("invalid SESSION_NUMBER SPDU length %d", length);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
session_nb = GST_READ_UINT16_BE (&spdu[2]);
|
|
|
|
session = g_hash_table_lookup (sl->sessions,
|
|
GINT_TO_POINTER ((guint) session_nb));
|
|
if (session == NULL) {
|
|
GST_ERROR ("got SESSION_NUMBER on an unknown connection: %d", session_nb);
|
|
return CAM_RETURN_SESSION_ERROR;
|
|
}
|
|
|
|
if (sl->session_data)
|
|
/* pass the APDUs to the upper layer, removing our 4-bytes header */
|
|
return sl->session_data (sl, session, spdu + 4, length - 4);
|
|
|
|
return CAM_RETURN_OK;
|
|
}
|
|
|
|
static CamReturn
|
|
connection_data_cb (CamTL * tl, CamTLConnection * connection,
|
|
guint8 * spdu, guint spdu_length)
|
|
{
|
|
CamReturn ret;
|
|
CamSL *sl = CAM_SL (tl->user_data);
|
|
|
|
switch (spdu[I_TAG]) {
|
|
case TAG_CREATE_SESSION_RESPONSE:
|
|
ret = handle_create_session_response (sl, connection, spdu, spdu_length);
|
|
break;
|
|
case TAG_OPEN_SESSION_REQUEST:
|
|
ret = handle_open_session_request (sl, connection, spdu, spdu_length);
|
|
break;
|
|
case TAG_CLOSE_SESSION_REQUEST:
|
|
ret = handle_close_session_request (sl, connection, spdu, spdu_length);
|
|
break;
|
|
case TAG_CLOSE_SESSION_RESPONSE:
|
|
ret = handle_close_session_response (sl, connection, spdu, spdu_length);
|
|
break;
|
|
case TAG_SESSION_NUMBER:
|
|
ret = handle_session_data (sl, connection, spdu, spdu_length);
|
|
break;
|
|
default:
|
|
g_return_val_if_reached (CAM_RETURN_SESSION_ERROR);
|
|
}
|
|
|
|
return ret;
|
|
}
|