1 /*
2 * camsession.c - GStreamer CAM (EN50221) Session Layer
3 * Copyright (C) 2007 Alessandro Decina
4 *
5 * Authors:
6 * Alessandro Decina <alessandro.d@gmail.com>
7 *
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.
12 *
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.
17 *
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., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "camsession.h"
25
26 #define GST_CAT_DEFAULT cam_debug_cat
27 #define I_TAG 0
28 #define I_LENGTH_FB 1
29
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
37
38 static CamReturn connection_data_cb (CamTL * tl, CamTLConnection * connection,
39 guint8 * spdu, guint spdu_length);
40
41 static CamSLSession *
cam_sl_session_new(CamSL * sl,CamTLConnection * connection,guint16 session_nb,guint resource_id)42 cam_sl_session_new (CamSL * sl, CamTLConnection * connection,
43 guint16 session_nb, guint resource_id)
44 {
45 CamSLSession *session = g_new0 (CamSLSession, 1);
46
47 session->state = CAM_SL_SESSION_STATE_IDLE;
48 session->sl = sl;
49 session->connection = connection;
50 session->session_nb = session_nb;
51 session->resource_id = resource_id;
52
53 return session;
54 }
55
56 static void
cam_sl_session_destroy(CamSLSession * session)57 cam_sl_session_destroy (CamSLSession * session)
58 {
59 g_free (session);
60 }
61
62 CamSL *
cam_sl_new(CamTL * tl)63 cam_sl_new (CamTL * tl)
64 {
65 CamSL *sl = g_new0 (CamSL, 1);
66
67 sl->sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal,
68 NULL, (GDestroyNotify) cam_sl_session_destroy);
69
70 tl->user_data = sl;
71 tl->connection_data = connection_data_cb;
72
73 return sl;
74 }
75
76 void
cam_sl_destroy(CamSL * sl)77 cam_sl_destroy (CamSL * sl)
78 {
79 g_hash_table_destroy (sl->sessions);
80
81 g_free (sl);
82 }
83
84 CamReturn
cam_sl_create_session(CamSL * sl,CamTLConnection * connection,guint resource_id,CamSLSession ** out_session)85 cam_sl_create_session (CamSL * sl,
86 CamTLConnection * connection, guint resource_id,
87 CamSLSession ** out_session)
88 {
89 CamReturn ret;
90 CamSLSession *session = NULL;
91 guint size;
92 guint offset;
93 guint8 *tpdu = NULL;
94 guint8 *spdu;
95 guint16 session_nb;
96
97 /* FIXME: implement session number allocations properly */
98 if (sl->session_ids == G_MAXUINT16)
99 return CAM_RETURN_SESSION_TOO_MANY_SESSIONS;
100
101 session_nb = ++sl->session_ids;
102 session = cam_sl_session_new (sl, connection, session_nb, resource_id);
103
104 /* SPDU layout (8 bytes):
105 * TAG_CREATE_SESSION 1 byte
106 * length_field () 1 byte
107 * resource_id 4 bytes
108 * session_nb 2 bytes
109 */
110
111 /* get TPDU size */
112 cam_tl_calc_buffer_size (sl->tl, 8, &size, &offset);
113
114 tpdu = (guint8 *) g_malloc (size);
115 spdu = tpdu + offset;
116
117 /* SPDU header */
118 /* tag */
119 spdu[0] = TAG_CREATE_SESSION;
120 /* fixed length_field */
121 spdu[1] = 6;
122
123 /* SPDU body */
124 /* resource id */
125 GST_WRITE_UINT32_BE (&spdu[2], resource_id);
126 /* session_nb */
127 GST_WRITE_UINT16_BE (&spdu[6], session_nb);
128
129 /* write the TPDU */
130 ret = cam_tl_connection_write (session->connection, tpdu, size, 8);
131 if (CAM_FAILED (ret))
132 goto error;
133
134 *out_session = session;
135
136 g_free (tpdu);
137 return CAM_RETURN_OK;
138
139 error:
140 if (session)
141 cam_sl_session_destroy (session);
142
143 g_free (tpdu);
144
145 return ret;
146 }
147
148 /* send a TAG_CLOSE_SESSION SPDU */
149 CamReturn
cam_sl_session_close(CamSLSession * session)150 cam_sl_session_close (CamSLSession * session)
151 {
152 CamReturn ret;
153 guint size;
154 guint offset;
155 guint8 *tpdu = NULL;
156 guint8 *spdu;
157 CamSL *sl = session->sl;
158
159 /* SPDU layout (4 bytes):
160 * TAG_CLOSE_SESSION 1 byte
161 * length_field () 1 byte
162 * session_nb 2 bytes
163 */
164
165 /* get the size of the TPDU */
166 cam_tl_calc_buffer_size (sl->tl, 4, &size, &offset);
167
168 tpdu = (guint8 *) g_malloc (size);
169 /* the spdu header starts after the TPDU headers */
170 spdu = tpdu + offset;
171
172 /* SPDU header */
173 /* tag */
174 spdu[0] = TAG_CLOSE_SESSION_REQUEST;
175 /* fixed length_field */
176 spdu[1] = 2;
177 /* SPDU body */
178 /* session_nb */
179 GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
180
181 /* write the TPDU */
182 ret = cam_tl_connection_write (session->connection, tpdu, size, 4);
183 if (CAM_FAILED (ret))
184 goto error;
185
186 session->state = CAM_SL_SESSION_STATE_CLOSING;
187
188 g_free (tpdu);
189
190 return CAM_RETURN_OK;
191
192 error:
193 g_free (tpdu);
194
195 return ret;
196 }
197
198 void
cam_sl_calc_buffer_size(CamSL * sl,guint body_length,guint * buffer_size,guint * offset)199 cam_sl_calc_buffer_size (CamSL * sl, guint body_length,
200 guint * buffer_size, guint * offset)
201 {
202 /* an APDU is sent in a SESSION_NUMBER SPDU, which has a fixed header size (4
203 * bytes) */
204 cam_tl_calc_buffer_size (sl->tl, 4 + body_length, buffer_size, offset);
205 *offset += 4;
206 }
207
208 CamReturn
cam_sl_session_write(CamSLSession * session,guint8 * buffer,guint buffer_size,guint body_length)209 cam_sl_session_write (CamSLSession * session,
210 guint8 * buffer, guint buffer_size, guint body_length)
211 {
212 guint8 *spdu;
213
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)
219 */
220
221 spdu = (buffer + buffer_size) - body_length - 4;
222 spdu[0] = TAG_SESSION_NUMBER;
223 spdu[1] = 2;
224 GST_WRITE_UINT16_BE (&spdu[2], session->session_nb);
225
226 /* add our header to the body length */
227 return cam_tl_connection_write (session->connection,
228 buffer, buffer_size, 4 + body_length);
229 }
230
231 static CamReturn
send_open_session_response(CamSL * sl,CamSLSession * session,guint8 status)232 send_open_session_response (CamSL * sl, CamSLSession * session, guint8 status)
233 {
234 CamReturn ret;
235 guint8 *tpdu;
236 guint size;
237 guint offset;
238 guint8 *spdu;
239
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
245 * session_nb 2 bytes
246 */
247
248 cam_tl_calc_buffer_size (session->sl->tl, 9, &size, &offset);
249
250 tpdu = g_malloc0 (size);
251 spdu = tpdu + offset;
252
253 spdu[0] = TAG_OPEN_SESSION_RESPONSE;
254 /* fixed length_field () */
255 spdu[1] = 7;
256 spdu[2] = status;
257 GST_WRITE_UINT32_BE (&spdu[3], session->resource_id);
258 GST_WRITE_UINT16_BE (&spdu[7], session->session_nb);
259
260 ret = cam_tl_connection_write (session->connection, tpdu, size, 9);
261 g_free (tpdu);
262 if (CAM_FAILED (ret))
263 return ret;
264
265 return CAM_RETURN_OK;
266 }
267
268 static CamReturn
send_close_session_response(CamSL * sl,CamSLSession * session,guint8 status)269 send_close_session_response (CamSL * sl, CamSLSession * session, guint8 status)
270 {
271 CamReturn ret;
272 guint8 *tpdu;
273 guint size;
274 guint offset;
275 guint8 *spdu;
276
277 /* SPDU layout (5 bytes):
278 * TAG_CLOSE_SESSION_RESPONSE 1 byte
279 * length_field () 1 byte
280 * session_status 1 byte
281 * session_nb 2 bytes
282 */
283
284 cam_tl_calc_buffer_size (session->sl->tl, 5, &size, &offset);
285
286 tpdu = g_malloc0 (size);
287 spdu = tpdu + offset;
288
289 spdu[0] = TAG_OPEN_SESSION_RESPONSE;
290 /* fixed length_field() */
291 spdu[1] = 3;
292 spdu[2] = status;
293 GST_WRITE_UINT16_BE (&spdu[3], session->session_nb);
294
295 ret = cam_tl_connection_write (session->connection, tpdu, size, 5);
296 g_free (tpdu);
297 if (CAM_FAILED (ret))
298 return ret;
299
300 return CAM_RETURN_OK;
301 }
302
303 static CamReturn
handle_open_session_request(CamSL * sl,CamTLConnection * connection,guint8 * spdu,guint spdu_length)304 handle_open_session_request (CamSL * sl, CamTLConnection * connection,
305 guint8 * spdu, guint spdu_length)
306 {
307 CamReturn ret;
308 guint resource_id;
309 guint status;
310 guint16 session_nb;
311 CamSLSession *session;
312
313 /* SPDU layout (6 bytes):
314 * TAG_OPEN_SESSION_REQUEST (1 byte)
315 * length_field() (1 byte)
316 * resource id (4 bytes)
317 */
318 if (spdu_length != 6) {
319 GST_ERROR ("expected OPEN_SESSION_REQUEST to be 6 bytes, got %d",
320 spdu_length);
321 return CAM_RETURN_SESSION_ERROR;
322 }
323
324 /* skip tag and length_field () */
325 resource_id = GST_READ_UINT32_BE (&spdu[2]);
326
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;
331 }
332
333 session_nb = ++sl->session_ids;
334 session = cam_sl_session_new (sl, connection, session_nb, resource_id);
335
336 GST_INFO ("session request: %d %x", session_nb, session->resource_id);
337
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))
342 goto error;
343 } else {
344 status = 0xF0;
345 }
346
347 ret = send_open_session_response (sl, session, (guint8) status);
348 if (CAM_FAILED (ret))
349 goto error;
350
351 GST_INFO ("session request response: %d %x", session_nb, status);
352
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);
358
359 if (sl->session_opened) {
360 /* notify the upper layer */
361 ret = sl->session_opened (sl, session);
362 if (CAM_FAILED (ret))
363 return ret;
364 }
365 } else {
366 /* session request wasn't accepted */
367 cam_sl_session_destroy (session);
368 }
369
370 return CAM_RETURN_OK;
371
372 error:
373 cam_sl_session_destroy (session);
374
375 return ret;
376 }
377
378 static CamReturn
handle_create_session_response(CamSL * sl,CamTLConnection * connection,guint8 * spdu,guint spdu_length)379 handle_create_session_response (CamSL * sl, CamTLConnection * connection,
380 guint8 * spdu, guint spdu_length)
381 {
382 guint16 session_nb;
383 CamSLSession *session;
384
385 /* SPDU layout (9 bytes):
386 * TAG_CREATE_SESSION_RESPONSE (1 byte)
387 * length_field() (1 byte)
388 * status (1 byte)
389 * resource id (4 bytes)
390 * session number (2 bytes)
391 */
392 if (spdu_length != 9) {
393 GST_ERROR ("expected CREATE_SESSION_RESPONSE to be 9 bytes, got %d",
394 spdu_length);
395 return CAM_RETURN_SESSION_ERROR;
396 }
397
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]);
402
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",
407 session_nb);
408 return CAM_RETURN_SESSION_ERROR;
409 }
410
411 if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
412 GST_DEBUG ("ignoring CREATE_SESSION_RESPONSE for closing session: %d",
413 session_nb);
414 return CAM_RETURN_OK;
415 }
416
417 session->state = CAM_SL_SESSION_STATE_ACTIVE;
418
419 GST_DEBUG ("session opened %d", session->session_nb);
420
421 if (sl->session_opened)
422 /* notify the upper layer */
423 return sl->session_opened (sl, session);
424 return CAM_RETURN_OK;
425 }
426
427 static CamReturn
handle_close_session_request(CamSL * sl,CamTLConnection * connection,guint8 * spdu,guint spdu_length)428 handle_close_session_request (CamSL * sl, CamTLConnection * connection,
429 guint8 * spdu, guint spdu_length)
430 {
431 CamReturn ret;
432 guint16 session_nb;
433 CamSLSession *session;
434 guint8 status = 0;
435
436 /* SPDU layout (4 bytes):
437 * TAG_CLOSE_SESSION_REQUEST (1 byte)
438 * length_field () (1 byte)
439 * session number (2 bytes)
440 */
441 if (spdu_length != 4) {
442 GST_ERROR ("expected CLOSE_SESSION_REQUEST to be 4 bytes, got %d",
443 spdu_length);
444 return CAM_RETURN_SESSION_ERROR;
445 }
446
447 /* skip tag and length_field() */
448 session_nb = GST_READ_UINT16_BE (&spdu[2]);
449
450 GST_DEBUG ("close session request %d", session_nb);
451
452 session = g_hash_table_lookup (sl->sessions,
453 GINT_TO_POINTER ((guint) session_nb));
454
455 if (session == NULL) {
456 GST_WARNING ("got CLOSE_SESSION_REQUEST for unknown session: %d",
457 session_nb);
458 return CAM_RETURN_OK;
459 }
460
461 if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
462 GST_WARNING ("got CLOSE_SESSION_REQUEST for closing session: %d",
463 session_nb);
464 status = 0xF0;
465 }
466
467 GST_DEBUG ("close session response: %d %d", session->session_nb, status);
468
469 ret = send_close_session_response (sl, session, status);
470 if (CAM_FAILED (ret))
471 return ret;
472
473 if (session->state != CAM_SL_SESSION_STATE_CLOSING) {
474 GST_DEBUG ("session closed %d", session->session_nb);
475
476 if (sl->session_closed)
477 ret = sl->session_closed (sl, session);
478
479 g_hash_table_remove (sl->sessions,
480 GINT_TO_POINTER ((guint) session->session_nb));
481
482 if (CAM_FAILED (ret))
483 return ret;
484 }
485
486 return CAM_RETURN_OK;
487 }
488
489 static CamReturn
handle_close_session_response(CamSL * sl,CamTLConnection * connection,guint8 * spdu,guint spdu_length)490 handle_close_session_response (CamSL * sl, CamTLConnection * connection,
491 guint8 * spdu, guint spdu_length)
492 {
493 guint16 session_nb;
494 CamSLSession *session;
495 CamReturn ret = CAM_RETURN_OK;
496
497 /* SPDU layout (5 bytes):
498 * TAG_CLOSE_SESSION_RESPONSE (1 byte)
499 * length_field () (1 byte)
500 * status (1 byte)
501 * session number (2 bytes)
502 */
503
504 if (spdu_length != 5) {
505 GST_ERROR ("expected CLOSE_SESSION_RESPONSE to be 5 bytes, got %d",
506 spdu_length);
507 return CAM_RETURN_SESSION_ERROR;
508 }
509
510 /* skip tag, length_field() and session_status */
511 session_nb = GST_READ_UINT16_BE (&spdu[3]);
512
513 session = g_hash_table_lookup (sl->sessions,
514 GINT_TO_POINTER ((guint) session_nb));
515 if (session == NULL || session->state != CAM_SL_SESSION_STATE_ACTIVE) {
516 GST_ERROR ("unexpected CLOSED_SESSION_RESPONSE");
517 return CAM_RETURN_SESSION_ERROR;
518 }
519
520 GST_DEBUG ("session closed %d", session->session_nb);
521
522 if (sl->session_closed)
523 ret = sl->session_closed (sl, session);
524
525 g_hash_table_remove (sl->sessions,
526 GINT_TO_POINTER ((guint) session->session_nb));
527
528 return ret;
529 }
530
531 static CamReturn
handle_session_data(CamSL * sl,CamTLConnection * connection,guint8 * spdu,guint length)532 handle_session_data (CamSL * sl, CamTLConnection * connection,
533 guint8 * spdu, guint length)
534 {
535 guint16 session_nb;
536 CamSLSession *session;
537
538 /* SPDU layout (>= 4 bytes):
539 * TAG_SESSION_NUMBER (1 byte)
540 * length_field() (1 byte)
541 * session number (2 bytes)
542 * one or more APDUs
543 */
544
545 if (length < 4) {
546 GST_ERROR ("invalid SESSION_NUMBER SPDU length %d", length);
547 return CAM_RETURN_SESSION_ERROR;
548 }
549
550 session_nb = GST_READ_UINT16_BE (&spdu[2]);
551
552 session = g_hash_table_lookup (sl->sessions,
553 GINT_TO_POINTER ((guint) session_nb));
554 if (session == NULL) {
555 GST_ERROR ("got SESSION_NUMBER on an unknown connection: %d", session_nb);
556 return CAM_RETURN_SESSION_ERROR;
557 }
558
559 if (sl->session_data)
560 /* pass the APDUs to the upper layer, removing our 4-bytes header */
561 return sl->session_data (sl, session, spdu + 4, length - 4);
562
563 return CAM_RETURN_OK;
564 }
565
566 static CamReturn
connection_data_cb(CamTL * tl,CamTLConnection * connection,guint8 * spdu,guint spdu_length)567 connection_data_cb (CamTL * tl, CamTLConnection * connection,
568 guint8 * spdu, guint spdu_length)
569 {
570 CamReturn ret;
571 CamSL *sl = CAM_SL (tl->user_data);
572
573 switch (spdu[I_TAG]) {
574 case TAG_CREATE_SESSION_RESPONSE:
575 ret = handle_create_session_response (sl, connection, spdu, spdu_length);
576 break;
577 case TAG_OPEN_SESSION_REQUEST:
578 ret = handle_open_session_request (sl, connection, spdu, spdu_length);
579 break;
580 case TAG_CLOSE_SESSION_REQUEST:
581 ret = handle_close_session_request (sl, connection, spdu, spdu_length);
582 break;
583 case TAG_CLOSE_SESSION_RESPONSE:
584 ret = handle_close_session_response (sl, connection, spdu, spdu_length);
585 break;
586 case TAG_SESSION_NUMBER:
587 ret = handle_session_data (sl, connection, spdu, spdu_length);
588 break;
589 default:
590 g_return_val_if_reached (CAM_RETURN_SESSION_ERROR);
591 }
592
593 return ret;
594 }
595