2 * camsession.c - GStreamer CAM (EN50221) Session Layer
3 * Copyright (C) 2007 Alessandro Decina
6 * Alessandro Decina <alessandro@nnva.org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include "camsession.h"
26 #define GST_CAT_DEFAULT cam_debug_cat
30 #define TAG_SESSION_NUMBER 0x90
31 #define TAG_OPEN_SESSION_REQUEST 0x91
32 #define TAG_OPEN_SESSION_RESPONSE 0x92
33 #define TAG_CREATE_SESSION 0x93
34 #define TAG_CREATE_SESSION_RESPONSE 0x94
35 #define TAG_CLOSE_SESSION_REQUEST 0x95
36 #define TAG_CLOSE_SESSION_RESPONSE 0x96
38 static CamReturn connection_data_cb (CamTL * tl, CamTLConnection * connection,
39 guint8 * spdu, guint spdu_length);
42 cam_sl_session_new (CamSL * sl, CamTLConnection * connection,
43 guint16 session_nb, guint resource_id)
45 CamSLSession *session = g_new0 (CamSLSession, 1);
47 session->state = CAM_SL_SESSION_STATE_IDLE;
49 session->connection = connection;
50 session->session_nb = session_nb;
51 session->resource_id = resource_id;
57 cam_sl_session_destroy (CamSLSession * session)
63 cam_sl_new (CamTL * tl)
65 CamSL *sl = g_new0 (CamSL, 1);
67 sl->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal,
68 NULL, (GDestroyNotify) cam_sl_session_destroy);
71 tl->connection_data = connection_data_cb;
77 cam_sl_destroy (CamSL * sl)
79 g_hash_table_destroy (sl->sessions);
85 cam_sl_create_session (CamSL * sl,
86 CamTLConnection * connection, guint resource_id,
87 CamSLSession ** out_session)
90 CamSLSession *session = NULL;
97 /* FIXME: implement session number allocations properly */
98 if (sl->session_ids == G_MAXUINT16)
99 return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
101 session_nb = ++sl->session_ids;
102 session = cam_sl_session_new (sl, connection, session_nb, resource_id);
104 /* SPDU layout (8 bytes):
105 * TAG_CREATE_SESSION 1 byte
106 * length_field () 1 byte
107 * resource_id 4 bytes
112 cam_tl_calc_buffer_size (sl->tl, 8, &size, &offset);
114 tpdu = (guint8 *) g_malloc (size);
115 spdu = tpdu + offset;
119 spdu[0] = TAG_CREATE_SESSION;
120 /* fixed length_field */
125 GST_WRITE_UINT32_BE (&spdu[2], resource_id);
127 GST_WRITE_UINT16_BE (&spdu[6], session_nb);
130 ret = cam_tl_connection_write (session->connection, tpdu, size, 8);
131 if (CAM_FAILED (ret))
134 *out_session = session;
137 return CAM_RETURN_OK;
141 cam_sl_session_destroy (session);
148 /* send a TAG_CLOSE_SESSION SPDU */
150 cam_sl_session_close (CamSLSession * session)
157 CamSL *sl = session->sl;
159 /* SPDU layout (4 bytes):
160 * TAG_CLOSE_SESSION 1 byte
161 * length_field () 1 byte
165 /* get the size of the TPDU */
166 cam_tl_calc_buffer_size (sl->tl, 4, &size, &offset);
168 tpdu = (guint8 *) g_malloc (size);
169 /* the spdu header starts after the TPDU headers */
170 spdu = tpdu + offset;
174 spdu[0] = TAG_CLOSE_SESSION_REQUEST;
175 /* fixed length_field */
179 GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
182 ret = cam_tl_connection_write (session->connection, tpdu, size, 4);
183 if (CAM_FAILED (ret))
186 session->state = CAM_SL_SESSION_STATE_CLOSING;
190 return CAM_RETURN_OK;
199 cam_sl_calc_buffer_size (CamSL * sl, guint body_length,
200 guint * buffer_size, guint * offset)
202 /* an APDU is sent in a SESSION_NUMBER SPDU, which has a fixed header size (4
204 cam_tl_calc_buffer_size (sl->tl, 4 + body_length, buffer_size, offset);
209 cam_sl_session_write (CamSLSession * session,
210 guint8 * buffer, guint buffer_size, guint body_length)
214 /* SPDU layout (4 + body_length bytes):
215 * TAG_SESSION_NUMBER (1 byte)
216 * length_field (1 byte)
217 * session number (2 bytes)
218 * one or more APDUs (body_length bytes)
221 spdu = (buffer + buffer_size) - body_length - 4;
222 spdu[0] = TAG_SESSION_NUMBER;
224 GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
226 /* add our header to the body length */
227 return cam_tl_connection_write (session->connection,
228 buffer, buffer_size, 4 + body_length);
232 send_open_session_response (CamSL * sl, CamSLSession * session, guint8 status)
240 /* SPDU layout (9 bytes):
241 * TAG_OPEN_SESSION_RESPONSE 1 byte
242 * length_field () 1 byte
243 * session_status 1 byte
244 * resource_id 4 bytes
248 cam_tl_calc_buffer_size (session->sl->tl, 9, &size, &offset);
250 tpdu = g_malloc0 (size);
251 spdu = tpdu + offset;
253 spdu[0] = TAG_OPEN_SESSION_RESPONSE;
254 /* fixed length_field () */
257 GST_WRITE_UINT32_BE (&spdu[3], session->resource_id);
258 GST_WRITE_UINT16_BE (&spdu[7], session->session_nb);
260 ret = cam_tl_connection_write (session->connection, tpdu, size, 9);
262 if (CAM_FAILED (ret))
265 return CAM_RETURN_OK;
269 send_close_session_response (CamSL * sl, CamSLSession * session, guint8 status)
277 /* SPDU layout (5 bytes):
278 * TAG_CLOSE_SESSION_RESPONSE 1 byte
279 * length_field () 1 byte
280 * session_status 1 byte
284 cam_tl_calc_buffer_size (session->sl->tl, 5, &size, &offset);
286 tpdu = g_malloc0 (size);
287 spdu = tpdu + offset;
289 spdu[0] = TAG_OPEN_SESSION_RESPONSE;
290 /* fixed length_field() */
293 GST_WRITE_UINT16_BE (&spdu[3], session->session_nb);
295 ret = cam_tl_connection_write (session->connection, tpdu, size, 5);
297 if (CAM_FAILED (ret))
300 return CAM_RETURN_OK;
304 handle_open_session_request (CamSL * sl, CamTLConnection * connection,
305 guint8 * spdu, guint spdu_length)
311 CamSLSession *session;
313 /* SPDU layout (6 bytes):
314 * TAG_OPEN_SESSION_REQUEST (1 byte)
315 * length_field() (1 byte)
316 * resource id (4 bytes)
318 if (spdu_length != 6) {
319 GST_ERROR ("expected OPEN_SESSION_REQUEST to be 6 bytes, got %d",
321 return CAM_RETURN_SESSION_ERROR;
324 /* skip tag and length_field () */
325 resource_id = GST_READ_UINT32_BE (&spdu[2]);
327 /* create a new session */
328 if (sl->session_ids == G_MAXUINT16) {
329 GST_ERROR ("too many sessions opened");
330 return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
333 session_nb = ++sl->session_ids;
334 session = cam_sl_session_new (sl, connection, session_nb, resource_id);
336 GST_INFO ("session request: %d %x", session_nb, session->resource_id);
338 if (sl->open_session_request) {
339 /* forward the request to the upper layer */
340 ret = sl->open_session_request (sl, session, &status);
341 if (CAM_FAILED (ret))
347 ret = send_open_session_response (sl, session, (guint8) status);
348 if (CAM_FAILED (ret))
351 GST_INFO ("session request response: %d %x", session_nb, status);
353 if (status == CAM_SL_RESOURCE_STATUS_OPEN) {
354 /* if the session has been accepted add it and signal */
355 session->state = CAM_SL_SESSION_STATE_ACTIVE;
356 g_hash_table_insert (sl->sessions,
357 GINT_TO_POINTER ((guint) session_nb), session);
359 if (sl->session_opened) {
360 /* notify the upper layer */
361 ret = sl->session_opened (sl, session);
362 if (CAM_FAILED (ret))
366 /* session request wasn't accepted */
367 cam_sl_session_destroy (session);
370 return CAM_RETURN_OK;
373 cam_sl_session_destroy (session);
379 handle_create_session_response (CamSL * sl, CamTLConnection * connection,
380 guint8 * spdu, guint spdu_length)
383 CamSLSession *session;
385 /* SPDU layout (9 bytes):
386 * TAG_CREATE_SESSION_RESPONSE (1 byte)
387 * length_field() (1 byte)
389 * resource id (4 bytes)
390 * session number (2 bytes)
392 if (spdu_length != 9) {
393 GST_ERROR ("expected CREATE_SESSION_RESPONSE to be 9 bytes, got %d",
395 return CAM_RETURN_SESSION_ERROR;
398 /* skip tag and length */
399 /* status = spdu[2]; */
400 /* resource_id = GST_READ_UINT32_BE (&spdu[3]); */
401 session_nb = GST_READ_UINT16_BE (&spdu[7]);
403 session = g_hash_table_lookup (sl->sessions,
404 GINT_TO_POINTER ((guint) session_nb));
405 if (session == NULL) {
406 GST_DEBUG ("got CREATE_SESSION_RESPONSE for unknown session: %d",
408 return CAM_RETURN_SESSION_ERROR;
411 if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
412 GST_DEBUG ("ignoring CREATE_SESSION_RESPONSE for closing session: %d",
414 return CAM_RETURN_OK;
417 session->state = CAM_SL_SESSION_STATE_ACTIVE;
419 GST_DEBUG ("session opened %d", session->session_nb);
421 if (sl->session_opened)
422 /* notify the upper layer */
423 return sl->session_opened (sl, session);
424 return CAM_RETURN_OK;
428 handle_close_session_request (CamSL * sl, CamTLConnection * connection,
429 guint8 * spdu, guint spdu_length)
433 CamSLSession *session;
436 /* SPDU layout (4 bytes):
437 * TAG_CLOSE_SESSION_REQUEST (1 byte)
438 * length_field () (1 byte)
439 * session number (2 bytes)
441 if (spdu_length != 4) {
442 GST_ERROR ("expected CLOSE_SESSION_REQUEST to be 4 bytes, got %d",
444 return CAM_RETURN_SESSION_ERROR;
447 /* skip tag and length_field() */
448 session_nb = GST_READ_UINT16_BE (&spdu[2]);
450 GST_DEBUG ("close session request %d", session_nb);
452 session = g_hash_table_lookup (sl->sessions,
453 GINT_TO_POINTER ((guint) session_nb));
454 if (session == NULL) {
455 GST_WARNING ("got CLOSE_SESSION_REQUEST for unknown session: %d",
459 } else if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
460 GST_WARNING ("got CLOSE_SESSION_REQUEST for closing session: %d",
466 GST_DEBUG ("close session response: %d %d", session->session_nb, status);
468 ret = send_close_session_response (sl, session, status);
469 if (CAM_FAILED (ret))
472 if (session->state != CAM_SL_SESSION_STATE_CLOSING) {
473 GST_DEBUG ("session closed %d", session->session_nb);
475 if (sl->session_closed)
476 ret = sl->session_closed (sl, session);
478 g_hash_table_remove (sl->sessions,
479 GINT_TO_POINTER ((guint) session->session_nb));
481 if (CAM_FAILED (ret))
485 return CAM_RETURN_OK;
489 handle_close_session_response (CamSL * sl, CamTLConnection * connection,
490 guint8 * spdu, guint spdu_length)
493 CamSLSession *session;
494 CamReturn ret = CAM_RETURN_OK;
496 /* SPDU layout (5 bytes):
497 * TAG_CLOSE_SESSION_RESPONSE (1 byte)
498 * length_field () (1 byte)
500 * session number (2 bytes)
503 if (spdu_length != 5) {
504 GST_ERROR ("expected CLOSE_SESSION_RESPONSE to be 5 bytes, got %d",
506 return CAM_RETURN_SESSION_ERROR;
509 /* skip tag, length_field() and session_status */
510 session_nb = GST_READ_UINT16_BE (&spdu[3]);
512 session = g_hash_table_lookup (sl->sessions,
513 GINT_TO_POINTER ((guint) session_nb));
514 if (session == NULL || session->state != CAM_SL_SESSION_STATE_ACTIVE) {
515 GST_ERROR ("unexpected CLOSED_SESSION_RESPONSE");
516 return CAM_RETURN_SESSION_ERROR;
519 GST_DEBUG ("session closed %d", session->session_nb);
521 if (sl->session_closed)
522 ret = sl->session_closed (sl, session);
524 g_hash_table_remove (sl->sessions,
525 GINT_TO_POINTER ((guint) session->session_nb));
531 handle_session_data (CamSL * sl, CamTLConnection * connection,
532 guint8 * spdu, guint length)
535 CamSLSession *session;
537 /* SPDU layout (>= 4 bytes):
538 * TAG_SESSION_NUMBER (1 byte)
539 * length_field() (1 byte)
540 * session number (2 bytes)
545 GST_ERROR ("invalid SESSION_NUMBER SPDU length %d", length);
546 return CAM_RETURN_SESSION_ERROR;
549 session_nb = GST_READ_UINT16_BE (&spdu[2]);
551 session = g_hash_table_lookup (sl->sessions,
552 GINT_TO_POINTER ((guint) session_nb));
553 if (session == NULL) {
554 GST_ERROR ("got SESSION_NUMBER on an unknown connection: %d", session_nb);
555 return CAM_RETURN_SESSION_ERROR;
558 if (sl->session_data)
559 /* pass the APDUs to the upper layer, removing our 4-bytes header */
560 return sl->session_data (sl, session, spdu + 4, length - 4);
562 return CAM_RETURN_OK;
566 connection_data_cb (CamTL * tl, CamTLConnection * connection,
567 guint8 * spdu, guint spdu_length)
570 CamSL *sl = CAM_SL (tl->user_data);
572 switch (spdu[I_TAG]) {
573 case TAG_CREATE_SESSION_RESPONSE:
574 ret = handle_create_session_response (sl, connection, spdu, spdu_length);
576 case TAG_OPEN_SESSION_REQUEST:
577 ret = handle_open_session_request (sl, connection, spdu, spdu_length);
579 case TAG_CLOSE_SESSION_REQUEST:
580 ret = handle_close_session_request (sl, connection, spdu, spdu_length);
582 case TAG_CLOSE_SESSION_RESPONSE:
583 ret = handle_close_session_response (sl, connection, spdu, spdu_length);
585 case TAG_SESSION_NUMBER:
586 ret = handle_session_data (sl, connection, spdu, spdu_length);
589 g_return_val_if_reached (CAM_RETURN_SESSION_ERROR);