Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / sys / dvb / camsession.c
1 /*
2  * camsession.c - GStreamer CAM (EN50221) Session Layer
3  * Copyright (C) 2007 Alessandro Decina
4  * 
5  * Authors:
6  *   Alessandro Decina <alessandro@nnva.org>
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., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, 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 *
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
57 cam_sl_session_destroy (CamSLSession * session)
58 {
59   g_free (session);
60 }
61
62 CamSL *
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
77 cam_sl_destroy (CamSL * sl)
78 {
79   g_hash_table_destroy (sl->sessions);
80
81   g_free (sl);
82 }
83
84 CamReturn
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
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
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
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
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
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
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
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
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   if (session == NULL) {
455     GST_WARNING ("got CLOSE_SESSION_REQUEST for unknown session: %d",
456         session_nb);
457
458     status = 0xF0;
459   } else if (session->state == CAM_SL_SESSION_STATE_CLOSING) {
460     GST_WARNING ("got CLOSE_SESSION_REQUEST for closing session: %d",
461         session_nb);
462
463     status = 0xF0;
464   }
465
466   GST_DEBUG ("close session response: %d %d", session->session_nb, status);
467
468   ret = send_close_session_response (sl, session, status);
469   if (CAM_FAILED (ret))
470     return ret;
471
472   if (session->state != CAM_SL_SESSION_STATE_CLOSING) {
473     GST_DEBUG ("session closed %d", session->session_nb);
474
475     if (sl->session_closed)
476       ret = sl->session_closed (sl, session);
477
478     g_hash_table_remove (sl->sessions,
479         GINT_TO_POINTER ((guint) session->session_nb));
480
481     if (CAM_FAILED (ret))
482       return ret;
483   }
484
485   return CAM_RETURN_OK;
486 }
487
488 static CamReturn
489 handle_close_session_response (CamSL * sl, CamTLConnection * connection,
490     guint8 * spdu, guint spdu_length)
491 {
492   guint16 session_nb;
493   CamSLSession *session;
494   CamReturn ret = CAM_RETURN_OK;
495
496   /* SPDU layout (5 bytes):
497    * TAG_CLOSE_SESSION_RESPONSE (1 byte)
498    * length_field () (1 byte)
499    * status (1 byte)
500    * session number (2 bytes)
501    */
502
503   if (spdu_length != 5) {
504     GST_ERROR ("expected CLOSE_SESSION_RESPONSE to be 5 bytes, got %d",
505         spdu_length);
506     return CAM_RETURN_SESSION_ERROR;
507   }
508
509   /* skip tag, length_field() and session_status */
510   session_nb = GST_READ_UINT16_BE (&spdu[3]);
511
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;
517   }
518
519   GST_DEBUG ("session closed %d", session->session_nb);
520
521   if (sl->session_closed)
522     ret = sl->session_closed (sl, session);
523
524   g_hash_table_remove (sl->sessions,
525       GINT_TO_POINTER ((guint) session->session_nb));
526
527   return ret;
528 }
529
530 static CamReturn
531 handle_session_data (CamSL * sl, CamTLConnection * connection,
532     guint8 * spdu, guint length)
533 {
534   guint16 session_nb;
535   CamSLSession *session;
536
537   /* SPDU layout (>= 4 bytes):
538    * TAG_SESSION_NUMBER (1 byte)
539    * length_field() (1 byte)
540    * session number (2 bytes)
541    * one or more APDUs 
542    */
543
544   if (length < 4) {
545     GST_ERROR ("invalid SESSION_NUMBER SPDU length %d", length);
546     return CAM_RETURN_SESSION_ERROR;
547   }
548
549   session_nb = GST_READ_UINT16_BE (&spdu[2]);
550
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;
556   }
557
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);
561
562   return CAM_RETURN_OK;
563 }
564
565 static CamReturn
566 connection_data_cb (CamTL * tl, CamTLConnection * connection,
567     guint8 * spdu, guint spdu_length)
568 {
569   CamReturn ret;
570   CamSL *sl = CAM_SL (tl->user_data);
571
572   switch (spdu[I_TAG]) {
573     case TAG_CREATE_SESSION_RESPONSE:
574       ret = handle_create_session_response (sl, connection, spdu, spdu_length);
575       break;
576     case TAG_OPEN_SESSION_REQUEST:
577       ret = handle_open_session_request (sl, connection, spdu, spdu_length);
578       break;
579     case TAG_CLOSE_SESSION_REQUEST:
580       ret = handle_close_session_request (sl, connection, spdu, spdu_length);
581       break;
582     case TAG_CLOSE_SESSION_RESPONSE:
583       ret = handle_close_session_response (sl, connection, spdu, spdu_length);
584       break;
585     case TAG_SESSION_NUMBER:
586       ret = handle_session_data (sl, connection, spdu, spdu_length);
587       break;
588     default:
589       g_return_val_if_reached (CAM_RETURN_SESSION_ERROR);
590   }
591
592   return ret;
593 }