Added check against NIT dvbpsi support.
[vlc.git] / modules / access / dvb / en50221.c
1 /*****************************************************************************
2  * en50221.c : implementation of the transport, session and applications
3  * layers of EN 50 221
4  *****************************************************************************
5  * Copyright (C) 2004-2005 the VideoLAN team
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_access.h>
31
32 #include <sys/ioctl.h>
33 #include <errno.h>
34
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <sys/stat.h>
41 #include <sys/poll.h>
42 #include <netinet/in.h>
43
44 /* DVB Card Drivers */
45 #include <linux/dvb/version.h>
46 #include <linux/dvb/dmx.h>
47 #include <linux/dvb/frontend.h>
48 #include <linux/dvb/ca.h>
49
50 /* Include dvbpsi headers */
51 #ifdef HAVE_DVBPSI_DR_H
52 #   include <dvbpsi/dvbpsi.h>
53 #   include <dvbpsi/descriptor.h>
54 #   include <dvbpsi/pat.h>
55 #   include <dvbpsi/pmt.h>
56 #   include <dvbpsi/dr.h>
57 #   include <dvbpsi/psi.h>
58 #   include <dvbpsi/demux.h>
59 #   include <dvbpsi/sdt.h>
60 #else
61 #   include "dvbpsi.h"
62 #   include "descriptor.h"
63 #   include "tables/pat.h"
64 #   include "tables/pmt.h"
65 #   include "descriptors/dr.h"
66 #   include "psi.h"
67 #   include "demux.h"
68 #   include "sdt.h"
69 #endif
70
71 #ifdef ENABLE_HTTPD
72 #   include "vlc_httpd.h"
73 #endif
74
75 #include "dvb.h"
76
77 #include <vlc_charset.h>
78
79 #undef DEBUG_TPDU
80 #define HLCI_WAIT_CAM_READY 0
81 #define CAM_PROG_MAX MAX_PROGRAMS
82
83 static void ResourceManagerOpen( access_t * p_access, int i_session_id );
84 static void ApplicationInformationOpen( access_t * p_access, int i_session_id );
85 static void ConditionalAccessOpen( access_t * p_access, int i_session_id );
86 static void DateTimeOpen( access_t * p_access, int i_session_id );
87 static void MMIOpen( access_t * p_access, int i_session_id );
88
89 /*****************************************************************************
90  * Utility functions
91  *****************************************************************************/
92 #define SIZE_INDICATOR 0x80
93
94 static uint8_t *GetLength( uint8_t *p_data, int *pi_length )
95 {
96     *pi_length = *p_data++;
97
98     if ( (*pi_length & SIZE_INDICATOR) != 0 )
99     {
100         int l = *pi_length & ~SIZE_INDICATOR;
101         int i;
102
103         *pi_length = 0;
104         for ( i = 0; i < l; i++ )
105             *pi_length = (*pi_length << 8) | *p_data++;
106     }
107
108     return p_data;
109 }
110
111 static uint8_t *SetLength( uint8_t *p_data, int i_length )
112 {
113     uint8_t *p = p_data;
114
115     if ( i_length < 128 )
116     {
117         *p++ = i_length;
118     }
119     else if ( i_length < 256 )
120     {
121         *p++ = SIZE_INDICATOR | 0x1;
122         *p++ = i_length;
123     }
124     else if ( i_length < 65536 )
125     {
126         *p++ = SIZE_INDICATOR | 0x2;
127         *p++ = i_length >> 8;
128         *p++ = i_length & 0xff;
129     }
130     else if ( i_length < 16777216 )
131     {
132         *p++ = SIZE_INDICATOR | 0x3;
133         *p++ = i_length >> 16;
134         *p++ = (i_length >> 8) & 0xff;
135         *p++ = i_length & 0xff;
136     }
137     else
138     {
139         *p++ = SIZE_INDICATOR | 0x4;
140         *p++ = i_length >> 24;
141         *p++ = (i_length >> 16) & 0xff;
142         *p++ = (i_length >> 8) & 0xff;
143         *p++ = i_length & 0xff;
144     }
145
146     return p;
147 }
148
149
150 /*
151  * Transport layer
152  */
153
154 #define MAX_TPDU_SIZE  2048
155 #define MAX_TPDU_DATA  (MAX_TPDU_SIZE - 4)
156
157 #define DATA_INDICATOR 0x80
158
159 #define T_SB           0x80
160 #define T_RCV          0x81
161 #define T_CREATE_TC    0x82
162 #define T_CTC_REPLY    0x83
163 #define T_DELETE_TC    0x84
164 #define T_DTC_REPLY    0x85
165 #define T_REQUEST_TC   0x86
166 #define T_NEW_TC       0x87
167 #define T_TC_ERROR     0x88
168 #define T_DATA_LAST    0xA0
169 #define T_DATA_MORE    0xA1
170
171 static void Dump( bool b_outgoing, uint8_t *p_data, int i_size )
172 {
173 #ifdef DEBUG_TPDU
174     int i;
175 #define MAX_DUMP 256
176     fprintf(stderr, "%s ", b_outgoing ? "-->" : "<--");
177     for ( i = 0; i < i_size && i < MAX_DUMP; i++)
178         fprintf(stderr, "%02X ", p_data[i]);
179     fprintf(stderr, "%s\n", i_size >= MAX_DUMP ? "..." : "");
180 #else
181     VLC_UNUSED(b_outgoing); VLC_UNUSED(p_data); VLC_UNUSED(i_size);
182 #endif
183 }
184
185 /*****************************************************************************
186  * TPDUSend
187  *****************************************************************************/
188 static int TPDUSend( access_t * p_access, uint8_t i_slot, uint8_t i_tag,
189                      const uint8_t *p_content, int i_length )
190 {
191     access_sys_t *p_sys = p_access->p_sys;
192     uint8_t i_tcid = i_slot + 1;
193     uint8_t p_data[MAX_TPDU_SIZE];
194     int i_size;
195
196     i_size = 0;
197     p_data[0] = i_slot;
198     p_data[1] = i_tcid;
199     p_data[2] = i_tag;
200
201     switch ( i_tag )
202     {
203     case T_RCV:
204     case T_CREATE_TC:
205     case T_CTC_REPLY:
206     case T_DELETE_TC:
207     case T_DTC_REPLY:
208     case T_REQUEST_TC:
209         p_data[3] = 1; /* length */
210         p_data[4] = i_tcid;
211         i_size = 5;
212         break;
213
214     case T_NEW_TC:
215     case T_TC_ERROR:
216         p_data[3] = 2; /* length */
217         p_data[4] = i_tcid;
218         p_data[5] = p_content[0];
219         i_size = 6;
220         break;
221
222     case T_DATA_LAST:
223     case T_DATA_MORE:
224     {
225         /* i_length <= MAX_TPDU_DATA */
226         uint8_t *p = p_data + 3;
227         p = SetLength( p, i_length + 1 );
228         *p++ = i_tcid;
229
230         if ( i_length )
231             memcpy( p, p_content, i_length );
232             i_size = i_length + (p - p_data);
233         }
234         break;
235
236     default:
237         break;
238     }
239     Dump( true, p_data, i_size );
240
241     if ( write( p_sys->i_ca_handle, p_data, i_size ) != i_size )
242     {
243         msg_Err( p_access, "cannot write to CAM device (%m)" );
244         return VLC_EGENERIC;
245     }
246
247     return VLC_SUCCESS;
248 }
249
250
251 /*****************************************************************************
252  * TPDURecv
253  *****************************************************************************/
254 #define CAM_READ_TIMEOUT  3500 // ms
255
256 static int TPDURecv( access_t * p_access, uint8_t i_slot, uint8_t *pi_tag,
257                      uint8_t *p_data, int *pi_size )
258 {
259     access_sys_t *p_sys = p_access->p_sys;
260     uint8_t i_tcid = i_slot + 1;
261     int i_size;
262     struct pollfd pfd[1];
263
264     pfd[0].fd = p_sys->i_ca_handle;
265     pfd[0].events = POLLIN;
266     if ( !(poll(pfd, 1, CAM_READ_TIMEOUT) > 0 && (pfd[0].revents & POLLIN)) )
267     {
268         msg_Err( p_access, "cannot poll from CAM device" );
269         return VLC_EGENERIC;
270     }
271
272     if ( pi_size == NULL )
273     {
274         p_data = malloc( MAX_TPDU_SIZE );
275     }
276
277     for ( ; ; )
278     {
279         i_size = read( p_sys->i_ca_handle, p_data, MAX_TPDU_SIZE );
280
281         if ( i_size >= 0 || errno != EINTR )
282             break;
283     }
284
285     if ( i_size < 5 )
286     {
287         msg_Err( p_access, "cannot read from CAM device (%d:%m)", i_size );
288         return VLC_EGENERIC;
289     }
290
291     if ( p_data[1] != i_tcid )
292     {
293         msg_Err( p_access, "invalid read from CAM device (%d instead of %d)",
294                  p_data[1], i_tcid );
295         return VLC_EGENERIC;
296     }
297
298     *pi_tag = p_data[2];
299     p_sys->pb_tc_has_data[i_slot] = (i_size >= 4
300                                       && p_data[i_size - 4] == T_SB
301                                       && p_data[i_size - 3] == 2
302                                       && (p_data[i_size - 1] & DATA_INDICATOR))
303                                         ?  true : false;
304
305     Dump( false, p_data, i_size );
306
307     if ( pi_size == NULL )
308         free( p_data );
309     else
310         *pi_size = i_size;
311
312     return VLC_SUCCESS;
313 }
314
315
316 /*
317  * Session layer
318  */
319
320 #define ST_SESSION_NUMBER           0x90
321 #define ST_OPEN_SESSION_REQUEST     0x91
322 #define ST_OPEN_SESSION_RESPONSE    0x92
323 #define ST_CREATE_SESSION           0x93
324 #define ST_CREATE_SESSION_RESPONSE  0x94
325 #define ST_CLOSE_SESSION_REQUEST    0x95
326 #define ST_CLOSE_SESSION_RESPONSE   0x96
327
328 #define SS_OK             0x00
329 #define SS_NOT_ALLOCATED  0xF0
330
331 #define RI_RESOURCE_MANAGER            0x00010041
332 #define RI_APPLICATION_INFORMATION     0x00020041
333 #define RI_CONDITIONAL_ACCESS_SUPPORT  0x00030041
334 #define RI_HOST_CONTROL                0x00200041
335 #define RI_DATE_TIME                   0x00240041
336 #define RI_MMI                         0x00400041
337
338 static int ResourceIdToInt( uint8_t *p_data )
339 {
340     return ((int)p_data[0] << 24) | ((int)p_data[1] << 16)
341             | ((int)p_data[2] << 8) | p_data[3];
342 }
343
344 /*****************************************************************************
345  * SPDUSend
346  *****************************************************************************/
347 static int SPDUSend( access_t * p_access, int i_session_id,
348                      uint8_t *p_data, int i_size )
349 {
350     access_sys_t *p_sys = p_access->p_sys;
351     uint8_t *p_spdu = malloc( i_size + 4 );
352     uint8_t *p = p_spdu;
353     uint8_t i_tag;
354     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
355
356     *p++ = ST_SESSION_NUMBER;
357     *p++ = 0x02;
358     *p++ = (i_session_id >> 8);
359     *p++ = i_session_id & 0xff;
360
361     memcpy( p, p_data, i_size );
362
363     i_size += 4;
364     p = p_spdu;
365
366     while ( i_size > 0 )
367     {
368         if ( i_size > MAX_TPDU_DATA )
369         {
370             if ( TPDUSend( p_access, i_slot, T_DATA_MORE, p,
371                            MAX_TPDU_DATA ) != VLC_SUCCESS )
372             {
373                 msg_Err( p_access, "couldn't send TPDU on session %d",
374                          i_session_id );
375                 free( p_spdu );
376                 return VLC_EGENERIC;
377             }
378             p += MAX_TPDU_DATA;
379             i_size -= MAX_TPDU_DATA;
380         }
381         else
382         {
383             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p, i_size )
384                     != VLC_SUCCESS )
385             {
386                 msg_Err( p_access, "couldn't send TPDU on session %d",
387                          i_session_id );
388                 free( p_spdu );
389                 return VLC_EGENERIC;
390             }
391             i_size = 0;
392         }
393
394         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS
395                || i_tag != T_SB )
396         {
397             msg_Err( p_access, "couldn't recv TPDU on session %d",
398                      i_session_id );
399             free( p_spdu );
400             return VLC_EGENERIC;
401         }
402     }
403
404     free( p_spdu );
405     return VLC_SUCCESS;
406 }
407
408 /*****************************************************************************
409  * SessionOpen
410  *****************************************************************************/
411 static void SessionOpen( access_t * p_access, uint8_t i_slot,
412                          uint8_t *p_spdu, int i_size )
413 {
414     access_sys_t *p_sys = p_access->p_sys;
415     int i_session_id;
416     int i_resource_id = ResourceIdToInt( &p_spdu[2] );
417     uint8_t p_response[16];
418     int i_status = SS_NOT_ALLOCATED;
419     uint8_t i_tag;
420
421     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
422     {
423         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
424             break;
425     }
426     if ( i_session_id == MAX_SESSIONS )
427     {
428         msg_Err( p_access, "too many sessions !" );
429         return;
430     }
431     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
432     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
433     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
434     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
435
436     if ( i_resource_id == RI_RESOURCE_MANAGER
437           || i_resource_id == RI_APPLICATION_INFORMATION
438           || i_resource_id == RI_CONDITIONAL_ACCESS_SUPPORT
439           || i_resource_id == RI_DATE_TIME
440           || i_resource_id == RI_MMI )
441     {
442         i_status = SS_OK;
443     }
444
445     p_response[0] = ST_OPEN_SESSION_RESPONSE;
446     p_response[1] = 0x7;
447     p_response[2] = i_status;
448     p_response[3] = p_spdu[2];
449     p_response[4] = p_spdu[3];
450     p_response[5] = p_spdu[4];
451     p_response[6] = p_spdu[5];
452     p_response[7] = i_session_id >> 8;
453     p_response[8] = i_session_id & 0xff;
454
455     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 9 ) !=
456             VLC_SUCCESS )
457     {
458         msg_Err( p_access,
459                  "SessionOpen: couldn't send TPDU on slot %d", i_slot );
460         return;
461     }
462     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
463     {
464         msg_Err( p_access,
465                  "SessionOpen: couldn't recv TPDU on slot %d", i_slot );
466         return;
467     }
468
469     switch ( i_resource_id )
470     {
471     case RI_RESOURCE_MANAGER:
472         ResourceManagerOpen( p_access, i_session_id ); break;
473     case RI_APPLICATION_INFORMATION:
474         ApplicationInformationOpen( p_access, i_session_id ); break;
475     case RI_CONDITIONAL_ACCESS_SUPPORT:
476         ConditionalAccessOpen( p_access, i_session_id ); break;
477     case RI_DATE_TIME:
478         DateTimeOpen( p_access, i_session_id ); break;
479     case RI_MMI:
480         MMIOpen( p_access, i_session_id ); break;
481
482     case RI_HOST_CONTROL:
483     default:
484         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
485         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
486     }
487 }
488
489 #if 0
490 /* unused code for the moment - commented out to keep gcc happy */
491 /*****************************************************************************
492  * SessionCreate
493  *****************************************************************************/
494 static void SessionCreate( access_t * p_access, int i_slot, int i_resource_id )
495 {
496     access_sys_t *p_sys = p_access->p_sys;
497     uint8_t p_response[16];
498     uint8_t i_tag;
499     int i_session_id;
500
501     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
502     {
503         if ( !p_sys->p_sessions[i_session_id - 1].i_resource_id )
504             break;
505     }
506     if ( i_session_id == MAX_SESSIONS )
507     {
508         msg_Err( p_access, "too many sessions !" );
509         return;
510     }
511     p_sys->p_sessions[i_session_id - 1].i_slot = i_slot;
512     p_sys->p_sessions[i_session_id - 1].i_resource_id = i_resource_id;
513     p_sys->p_sessions[i_session_id - 1].pf_close = NULL;
514     p_sys->p_sessions[i_session_id - 1].pf_manage = NULL;
515     p_sys->p_sessions[i_session_id - 1].p_sys = NULL;
516
517     p_response[0] = ST_CREATE_SESSION;
518     p_response[1] = 0x6;
519     p_response[2] = i_resource_id >> 24;
520     p_response[3] = (i_resource_id >> 16) & 0xff;
521     p_response[4] = (i_resource_id >> 8) & 0xff;
522     p_response[5] = i_resource_id & 0xff;
523     p_response[6] = i_session_id >> 8;
524     p_response[7] = i_session_id & 0xff;
525
526     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
527             VLC_SUCCESS )
528     {
529         msg_Err( p_access,
530                  "SessionCreate: couldn't send TPDU on slot %d", i_slot );
531         return;
532     }
533     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
534     {
535         msg_Err( p_access,
536                  "SessionCreate: couldn't recv TPDU on slot %d", i_slot );
537         return;
538     }
539 }
540 #endif
541
542 /*****************************************************************************
543  * SessionCreateResponse
544  *****************************************************************************/
545 static void SessionCreateResponse( access_t * p_access, uint8_t i_slot,
546                                    uint8_t *p_spdu, int i_size )
547 {
548     access_sys_t *p_sys = p_access->p_sys;
549     int i_status = p_spdu[2];
550     int i_resource_id = ResourceIdToInt( &p_spdu[3] );
551     int i_session_id = ((int)p_spdu[7] << 8) | p_spdu[8];
552
553     if ( i_status != SS_OK )
554     {
555         msg_Err( p_access, "SessionCreateResponse: failed to open session %d"
556                  " resource=0x%x status=0x%x", i_session_id, i_resource_id,
557                  i_status );
558         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
559         return;
560     }
561
562     switch ( i_resource_id )
563     {
564     case RI_RESOURCE_MANAGER:
565         ResourceManagerOpen( p_access, i_session_id ); break;
566     case RI_APPLICATION_INFORMATION:
567         ApplicationInformationOpen( p_access, i_session_id ); break;
568     case RI_CONDITIONAL_ACCESS_SUPPORT:
569         ConditionalAccessOpen( p_access, i_session_id ); break;
570     case RI_DATE_TIME:
571         DateTimeOpen( p_access, i_session_id ); break;
572     case RI_MMI:
573         MMIOpen( p_access, i_session_id ); break;
574
575     case RI_HOST_CONTROL:
576     default:
577         msg_Err( p_access, "unknown resource id (0x%x)", i_resource_id );
578         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
579     }
580 }
581
582 /*****************************************************************************
583  * SessionSendClose
584  *****************************************************************************/
585 static void SessionSendClose( access_t * p_access, int i_session_id )
586 {
587     access_sys_t *p_sys = p_access->p_sys;
588     uint8_t p_response[16];
589     uint8_t i_tag;
590     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
591
592     p_response[0] = ST_CLOSE_SESSION_REQUEST;
593     p_response[1] = 0x2;
594     p_response[2] = i_session_id >> 8;
595     p_response[3] = i_session_id & 0xff;
596
597     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 4 ) !=
598             VLC_SUCCESS )
599     {
600         msg_Err( p_access,
601                  "SessionSendClose: couldn't send TPDU on slot %d", i_slot );
602         return;
603     }
604     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
605     {
606         msg_Err( p_access,
607                  "SessionSendClose: couldn't recv TPDU on slot %d", i_slot );
608         return;
609     }
610 }
611
612 /*****************************************************************************
613  * SessionClose
614  *****************************************************************************/
615 static void SessionClose( access_t * p_access, int i_session_id )
616 {
617     access_sys_t *p_sys = p_access->p_sys;
618     uint8_t p_response[16];
619     uint8_t i_tag;
620     uint8_t i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
621
622     if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
623         p_sys->p_sessions[i_session_id - 1].pf_close( p_access, i_session_id );
624     p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
625
626     p_response[0] = ST_CLOSE_SESSION_RESPONSE;
627     p_response[1] = 0x3;
628     p_response[2] = SS_OK;
629     p_response[3] = i_session_id >> 8;
630     p_response[4] = i_session_id & 0xff;
631
632     if ( TPDUSend( p_access, i_slot, T_DATA_LAST, p_response, 5 ) !=
633             VLC_SUCCESS )
634     {
635         msg_Err( p_access,
636                  "SessionClose: couldn't send TPDU on slot %d", i_slot );
637         return;
638     }
639     if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) != VLC_SUCCESS )
640     {
641         msg_Err( p_access,
642                  "SessionClose: couldn't recv TPDU on slot %d", i_slot );
643         return;
644     }
645 }
646
647 /*****************************************************************************
648  * SPDUHandle
649  *****************************************************************************/
650 static void SPDUHandle( access_t * p_access, uint8_t i_slot,
651                         uint8_t *p_spdu, int i_size )
652 {
653     access_sys_t *p_sys = p_access->p_sys;
654     int i_session_id;
655
656     switch ( p_spdu[0] )
657     {
658     case ST_SESSION_NUMBER:
659         if ( i_size <= 4 )
660             return;
661         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
662         p_sys->p_sessions[i_session_id - 1].pf_handle( p_access, i_session_id,
663                                                        p_spdu + 4, i_size - 4 );
664         break;
665
666     case ST_OPEN_SESSION_REQUEST:
667         if ( i_size != 6 || p_spdu[1] != 0x4 )
668             return;
669         SessionOpen( p_access, i_slot, p_spdu, i_size );
670         break;
671
672     case ST_CREATE_SESSION_RESPONSE:
673         if ( i_size != 9 || p_spdu[1] != 0x7 )
674             return;
675         SessionCreateResponse( p_access, i_slot, p_spdu, i_size );
676         break;
677
678     case ST_CLOSE_SESSION_REQUEST:
679         if ( i_size != 4 || p_spdu[1] != 0x2 )
680             return;
681         i_session_id = ((int)p_spdu[2] << 8) | p_spdu[3];
682         SessionClose( p_access, i_session_id );
683         break;
684
685     case ST_CLOSE_SESSION_RESPONSE:
686         if ( i_size != 5 || p_spdu[1] != 0x3 )
687             return;
688         i_session_id = ((int)p_spdu[3] << 8) | p_spdu[4];
689         if ( p_spdu[2] )
690         {
691             msg_Err( p_access, "closing a session which is not allocated (%d)",
692                      i_session_id );
693         }
694         else
695         {
696             if ( p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
697                 p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
698                                                               i_session_id );
699             p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
700         }
701         break;
702
703     default:
704         msg_Err( p_access, "unexpected tag in SPDUHandle (%x)", p_spdu[0] );
705         break;
706     }
707 }
708
709
710 /*
711  * Application layer
712  */
713
714 #define AOT_NONE                    0x000000
715 #define AOT_PROFILE_ENQ             0x9F8010
716 #define AOT_PROFILE                 0x9F8011
717 #define AOT_PROFILE_CHANGE          0x9F8012
718 #define AOT_APPLICATION_INFO_ENQ    0x9F8020
719 #define AOT_APPLICATION_INFO        0x9F8021
720 #define AOT_ENTER_MENU              0x9F8022
721 #define AOT_CA_INFO_ENQ             0x9F8030
722 #define AOT_CA_INFO                 0x9F8031
723 #define AOT_CA_PMT                  0x9F8032
724 #define AOT_CA_PMT_REPLY            0x9F8033
725 #define AOT_TUNE                    0x9F8400
726 #define AOT_REPLACE                 0x9F8401
727 #define AOT_CLEAR_REPLACE           0x9F8402
728 #define AOT_ASK_RELEASE             0x9F8403
729 #define AOT_DATE_TIME_ENQ           0x9F8440
730 #define AOT_DATE_TIME               0x9F8441
731 #define AOT_CLOSE_MMI               0x9F8800
732 #define AOT_DISPLAY_CONTROL         0x9F8801
733 #define AOT_DISPLAY_REPLY           0x9F8802
734 #define AOT_TEXT_LAST               0x9F8803
735 #define AOT_TEXT_MORE               0x9F8804
736 #define AOT_KEYPAD_CONTROL          0x9F8805
737 #define AOT_KEYPRESS                0x9F8806
738 #define AOT_ENQ                     0x9F8807
739 #define AOT_ANSW                    0x9F8808
740 #define AOT_MENU_LAST               0x9F8809
741 #define AOT_MENU_MORE               0x9F880A
742 #define AOT_MENU_ANSW               0x9F880B
743 #define AOT_LIST_LAST               0x9F880C
744 #define AOT_LIST_MORE               0x9F880D
745 #define AOT_SUBTITLE_SEGMENT_LAST   0x9F880E
746 #define AOT_SUBTITLE_SEGMENT_MORE   0x9F880F
747 #define AOT_DISPLAY_MESSAGE         0x9F8810
748 #define AOT_SCENE_END_MARK          0x9F8811
749 #define AOT_SCENE_DONE              0x9F8812
750 #define AOT_SCENE_CONTROL           0x9F8813
751 #define AOT_SUBTITLE_DOWNLOAD_LAST  0x9F8814
752 #define AOT_SUBTITLE_DOWNLOAD_MORE  0x9F8815
753 #define AOT_FLUSH_DOWNLOAD          0x9F8816
754 #define AOT_DOWNLOAD_REPLY          0x9F8817
755 #define AOT_COMMS_CMD               0x9F8C00
756 #define AOT_CONNECTION_DESCRIPTOR   0x9F8C01
757 #define AOT_COMMS_REPLY             0x9F8C02
758 #define AOT_COMMS_SEND_LAST         0x9F8C03
759 #define AOT_COMMS_SEND_MORE         0x9F8C04
760 #define AOT_COMMS_RCV_LAST          0x9F8C05
761 #define AOT_COMMS_RCV_MORE          0x9F8C06
762
763 /*****************************************************************************
764  * APDUGetTag
765  *****************************************************************************/
766 static int APDUGetTag( const uint8_t *p_apdu, int i_size )
767 {
768     if ( i_size >= 3 )
769     {
770         int i, t = 0;
771         for ( i = 0; i < 3; i++ )
772             t = (t << 8) | *p_apdu++;
773         return t;
774     }
775
776     return AOT_NONE;
777 }
778
779 /*****************************************************************************
780  * APDUGetLength
781  *****************************************************************************/
782 static uint8_t *APDUGetLength( uint8_t *p_apdu, int *pi_size )
783 {
784     return GetLength( &p_apdu[3], pi_size );
785 }
786
787 /*****************************************************************************
788  * APDUSend
789  *****************************************************************************/
790 static int APDUSend( access_t * p_access, int i_session_id, int i_tag,
791                      uint8_t *p_data, int i_size )
792 {
793     access_sys_t *p_sys = p_access->p_sys;
794     uint8_t *p_apdu = malloc( i_size + 12 );
795     uint8_t *p = p_apdu;
796     ca_msg_t ca_msg;
797     int i_ret;
798
799     *p++ = (i_tag >> 16);
800     *p++ = (i_tag >> 8) & 0xff;
801     *p++ = i_tag & 0xff;
802     p = SetLength( p, i_size );
803     if ( i_size )
804         memcpy( p, p_data, i_size );
805     if ( p_sys->i_ca_type == CA_CI_LINK )
806     {
807         i_ret = SPDUSend( p_access, i_session_id, p_apdu, i_size + p - p_apdu );
808     }
809     else
810     {
811         if ( i_size + p - p_apdu > 256 )
812         {
813             msg_Err( p_access, "CAM: apdu overflow" );
814             i_ret = VLC_EGENERIC;
815         }
816         else
817         {
818             char *psz_hex;
819             ca_msg.length = i_size + p - p_apdu;
820             if ( i_size == 0 ) ca_msg.length=3;
821             psz_hex = (char*)malloc( ca_msg.length*3 + 1);
822             memcpy( ca_msg.msg, p_apdu, i_size + p - p_apdu );
823             i_ret = ioctl(p_sys->i_ca_handle, CA_SEND_MSG, &ca_msg );
824             if ( i_ret < 0 )
825             {
826                 msg_Err( p_access, "Error sending to CAM: %m" );
827                 i_ret = VLC_EGENERIC;
828             }
829         }
830     }
831     free( p_apdu );
832     return i_ret;
833 }
834
835 /*
836  * Resource Manager
837  */
838
839 /*****************************************************************************
840  * ResourceManagerHandle
841  *****************************************************************************/
842 static void ResourceManagerHandle( access_t * p_access, int i_session_id,
843                                    uint8_t *p_apdu, int i_size )
844 {
845     int i_tag = APDUGetTag( p_apdu, i_size );
846
847     switch ( i_tag )
848     {
849     case AOT_PROFILE_ENQ:
850     {
851         int resources[] = { htonl(RI_RESOURCE_MANAGER),
852                             htonl(RI_APPLICATION_INFORMATION),
853                             htonl(RI_CONDITIONAL_ACCESS_SUPPORT),
854                             htonl(RI_DATE_TIME),
855                             htonl(RI_MMI)
856                           };
857         APDUSend( p_access, i_session_id, AOT_PROFILE, (uint8_t*)resources,
858                   sizeof(resources) );
859         break;
860     }
861     case AOT_PROFILE:
862         APDUSend( p_access, i_session_id, AOT_PROFILE_CHANGE, NULL, 0 );
863         break;
864
865     default:
866         msg_Err( p_access, "unexpected tag in ResourceManagerHandle (0x%x)",
867                  i_tag );
868     }
869 }
870
871 /*****************************************************************************
872  * ResourceManagerOpen
873  *****************************************************************************/
874 static void ResourceManagerOpen( access_t * p_access, int i_session_id )
875 {
876     access_sys_t *p_sys = p_access->p_sys;
877
878     msg_Dbg( p_access, "opening ResourceManager session (%d)", i_session_id );
879
880     p_sys->p_sessions[i_session_id - 1].pf_handle = ResourceManagerHandle;
881
882     APDUSend( p_access, i_session_id, AOT_PROFILE_ENQ, NULL, 0 );
883 }
884
885 /*
886  * Application Information
887  */
888
889 /*****************************************************************************
890  * ApplicationInformationEnterMenu
891  *****************************************************************************/
892 static void ApplicationInformationEnterMenu( access_t * p_access,
893                                              int i_session_id )
894 {
895     access_sys_t *p_sys = p_access->p_sys;
896     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
897
898     msg_Dbg( p_access, "entering MMI menus on session %d", i_session_id );
899     APDUSend( p_access, i_session_id, AOT_ENTER_MENU, NULL, 0 );
900     p_sys->pb_slot_mmi_expected[i_slot] = true;
901 }
902
903 /*****************************************************************************
904  * ApplicationInformationHandle
905  *****************************************************************************/
906 static void ApplicationInformationHandle( access_t * p_access, int i_session_id,
907                                           uint8_t *p_apdu, int i_size )
908 {
909     int i_tag = APDUGetTag( p_apdu, i_size );
910
911     switch ( i_tag )
912     {
913     case AOT_APPLICATION_INFO:
914     {
915         int i_type, i_manufacturer, i_code;
916         int l = 0;
917         uint8_t *d = APDUGetLength( p_apdu, &l );
918
919         if ( l < 4 ) break;
920         p_apdu[l + 4] = '\0';
921
922         i_type = *d++;
923         i_manufacturer = ((int)d[0] << 8) | d[1];
924         d += 2;
925         i_code = ((int)d[0] << 8) | d[1];
926         d += 2;
927         d = GetLength( d, &l );
928         d[l] = '\0';
929         msg_Info( p_access, "CAM: %s, %02X, %04X, %04X",
930                   d, i_type, i_manufacturer, i_code );
931         break;
932     }
933     default:
934         msg_Err( p_access,
935                  "unexpected tag in ApplicationInformationHandle (0x%x)",
936                  i_tag );
937     }
938 }
939
940 /*****************************************************************************
941  * ApplicationInformationOpen
942  *****************************************************************************/
943 static void ApplicationInformationOpen( access_t * p_access, int i_session_id )
944 {
945     access_sys_t *p_sys = p_access->p_sys;
946
947     msg_Dbg( p_access, "opening ApplicationInformation session (%d)", i_session_id );
948
949     p_sys->p_sessions[i_session_id - 1].pf_handle = ApplicationInformationHandle;
950
951     APDUSend( p_access, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
952 }
953
954 /*
955  * Conditional Access
956  */
957
958 #define MAX_CASYSTEM_IDS 16
959
960 typedef struct
961 {
962     uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
963 } system_ids_t;
964
965 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
966 {
967     int i = 0;
968     if( !p_ids ) return true;
969
970     while ( p_ids->pi_system_ids[i] )
971     {
972         if ( p_ids->pi_system_ids[i] == i_id )
973             return true;
974         i++;
975     }
976
977     return false;
978 }
979
980 /*****************************************************************************
981  * CAPMTNeedsDescrambling
982  *****************************************************************************/
983 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
984 {
985     dvbpsi_descriptor_t *p_dr;
986     dvbpsi_pmt_es_t *p_es;
987
988     for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
989     {
990         if( p_dr->i_tag == 0x9 )
991         {
992             return true;
993         }
994     }
995  
996     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
997     {
998         for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
999              p_dr = p_dr->p_next )
1000         {
1001             if( p_dr->i_tag == 0x9 )
1002             {
1003                 return true;
1004             }
1005         }
1006     }
1007
1008     return false;
1009 }
1010
1011 /*****************************************************************************
1012  * CAPMTBuild
1013  *****************************************************************************/
1014 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1015 {
1016     int i_cad_size = 0;
1017
1018     while ( p_dr != NULL )
1019     {
1020         if( p_dr->i_tag == 0x9 )
1021         {
1022             uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1023                                     | p_dr->p_data[1];
1024             if ( CheckSystemID( p_ids, i_sysid ) )
1025                 i_cad_size += p_dr->i_length + 2;
1026         }
1027         p_dr = p_dr->p_next;
1028     }
1029
1030     return i_cad_size;
1031 }
1032
1033 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1034                              uint16_t i_program_number, uint8_t i_version,
1035                              int i_size, dvbpsi_descriptor_t *p_dr,
1036                              uint8_t i_cmd )
1037 {
1038     uint8_t *p_data;
1039
1040     if ( i_size )
1041         p_data = malloc( 7 + i_size );
1042     else
1043         p_data = malloc( 6 );
1044
1045     p_data[0] = i_list_mgt;
1046     p_data[1] = i_program_number >> 8;
1047     p_data[2] = i_program_number & 0xff;
1048     p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1049
1050     if ( i_size )
1051     {
1052         int i;
1053
1054         p_data[4] = (i_size + 1) >> 8;
1055         p_data[5] = (i_size + 1) & 0xff;
1056         p_data[6] = i_cmd;
1057         i = 7;
1058
1059         while ( p_dr != NULL )
1060         {
1061             if( p_dr->i_tag == 0x9 )
1062             {
1063                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1064                                     | p_dr->p_data[1];
1065                 if ( CheckSystemID( p_ids, i_sysid ) )
1066                 {
1067                     p_data[i] = 0x9;
1068                     p_data[i + 1] = p_dr->i_length;
1069                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1070 //                    p_data[i+4] &= 0x1f;
1071                     i += p_dr->i_length + 2;
1072                 }
1073             }
1074             p_dr = p_dr->p_next;
1075         }
1076     }
1077     else
1078     {
1079         p_data[4] = 0;
1080         p_data[5] = 0;
1081     }
1082
1083     return p_data;
1084 }
1085
1086 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1087                          int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1088                          int i_size, dvbpsi_descriptor_t *p_dr,
1089                          uint8_t i_cmd )
1090 {
1091     uint8_t *p_data;
1092     int i;
1093  
1094     if ( i_size )
1095         p_data = realloc( p_capmt, i_capmt_size + 6 + i_size );
1096     else
1097         p_data = realloc( p_capmt, i_capmt_size + 5 );
1098
1099     i = i_capmt_size;
1100
1101     p_data[i] = i_type;
1102     p_data[i + 1] = i_pid >> 8;
1103     p_data[i + 2] = i_pid & 0xff;
1104
1105     if ( i_size )
1106     {
1107         p_data[i + 3] = (i_size + 1) >> 8;
1108         p_data[i + 4] = (i_size + 1) & 0xff;
1109         p_data[i + 5] = i_cmd;
1110         i += 6;
1111
1112         while ( p_dr != NULL )
1113         {
1114             if( p_dr->i_tag == 0x9 )
1115             {
1116                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1117                                     | p_dr->p_data[1];
1118                 if ( CheckSystemID( p_ids, i_sysid ) )
1119                 {
1120                     p_data[i] = 0x9;
1121                     p_data[i + 1] = p_dr->i_length;
1122                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1123                     i += p_dr->i_length + 2;
1124                 }
1125             }
1126             p_dr = p_dr->p_next;
1127         }
1128     }
1129     else
1130     {
1131         p_data[i + 3] = 0;
1132         p_data[i + 4] = 0;
1133     }
1134
1135     return p_data;
1136 }
1137
1138 static uint8_t *CAPMTBuild( access_t * p_access, int i_session_id,
1139                             dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1140                             uint8_t i_cmd, int *pi_capmt_size )
1141 {
1142     access_sys_t *p_sys = p_access->p_sys;
1143     system_ids_t *p_ids =
1144         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1145     dvbpsi_pmt_es_t *p_es;
1146     int i_cad_size, i_cad_program_size;
1147     uint8_t *p_capmt;
1148
1149     i_cad_size = i_cad_program_size =
1150             GetCADSize( p_ids, p_pmt->p_first_descriptor );
1151     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1152     {
1153         i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1154     }
1155
1156     if ( !i_cad_size )
1157     {
1158         msg_Warn( p_access,
1159                   "no compatible scrambling system for SID %d on session %d",
1160                   p_pmt->i_program_number, i_session_id );
1161         *pi_capmt_size = 0;
1162         return NULL;
1163     }
1164
1165     p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1166                            p_pmt->i_version, i_cad_program_size,
1167                            p_pmt->p_first_descriptor, i_cmd );
1168
1169     if ( i_cad_program_size )
1170         *pi_capmt_size = 7 + i_cad_program_size;
1171     else
1172         *pi_capmt_size = 6;
1173
1174     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1175     {
1176         i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1177
1178         if ( i_cad_size || i_cad_program_size )
1179         {
1180             p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1181                                p_es->i_pid, i_cad_size,
1182                                p_es->p_first_descriptor, i_cmd );
1183             if ( i_cad_size )
1184                 *pi_capmt_size += 6 + i_cad_size;
1185             else
1186                 *pi_capmt_size += 5;
1187         }
1188     }
1189
1190     return p_capmt;
1191 }
1192
1193 /*****************************************************************************
1194  * CAPMTFirst
1195  *****************************************************************************/
1196 static void CAPMTFirst( access_t * p_access, int i_session_id,
1197                         dvbpsi_pmt_t *p_pmt )
1198 {
1199     uint8_t *p_capmt;
1200     int i_capmt_size;
1201
1202     msg_Dbg( p_access, "adding first CAPMT for SID %d on session %d",
1203              p_pmt->i_program_number, i_session_id );
1204
1205     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1206                           0x3 /* only */, 0x1 /* ok_descrambling */,
1207                           &i_capmt_size );
1208
1209     if ( i_capmt_size )
1210         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1211 }
1212
1213 /*****************************************************************************
1214  * CAPMTAdd
1215  *****************************************************************************/
1216 static void CAPMTAdd( access_t * p_access, int i_session_id,
1217                       dvbpsi_pmt_t *p_pmt )
1218 {
1219     uint8_t *p_capmt;
1220     int i_capmt_size;
1221
1222     if( p_access->p_sys->i_selected_programs >= CAM_PROG_MAX )
1223     {
1224         msg_Warn( p_access, "Not adding CAPMT for SID %d, too many programs",
1225                   p_pmt->i_program_number );
1226         return;
1227     }
1228     p_access->p_sys->i_selected_programs++;
1229     if( p_access->p_sys->i_selected_programs == 1 )
1230     {
1231         CAPMTFirst( p_access, i_session_id, p_pmt );
1232         return;
1233     }
1234  
1235  
1236     msg_Dbg( p_access, "adding CAPMT for SID %d on session %d",
1237              p_pmt->i_program_number, i_session_id );
1238
1239     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1240                           0x4 /* add */, 0x1 /* ok_descrambling */,
1241                           &i_capmt_size );
1242
1243     if ( i_capmt_size )
1244         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1245 }
1246
1247 /*****************************************************************************
1248  * CAPMTUpdate
1249  *****************************************************************************/
1250 static void CAPMTUpdate( access_t * p_access, int i_session_id,
1251                          dvbpsi_pmt_t *p_pmt )
1252 {
1253     uint8_t *p_capmt;
1254     int i_capmt_size;
1255
1256     msg_Dbg( p_access, "updating CAPMT for SID %d on session %d",
1257              p_pmt->i_program_number, i_session_id );
1258
1259     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1260                           0x5 /* update */, 0x1 /* ok_descrambling */,
1261                           &i_capmt_size );
1262
1263     if ( i_capmt_size )
1264         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1265 }
1266
1267 /*****************************************************************************
1268  * CAPMTDelete
1269  *****************************************************************************/
1270 static void CAPMTDelete( access_t * p_access, int i_session_id,
1271                          dvbpsi_pmt_t *p_pmt )
1272 {
1273     uint8_t *p_capmt;
1274     int i_capmt_size;
1275
1276     p_access->p_sys->i_selected_programs--;
1277     msg_Dbg( p_access, "deleting CAPMT for SID %d on session %d",
1278              p_pmt->i_program_number, i_session_id );
1279
1280     p_capmt = CAPMTBuild( p_access, i_session_id, p_pmt,
1281                           0x5 /* update */, 0x4 /* not selected */,
1282                           &i_capmt_size );
1283
1284     if ( i_capmt_size )
1285         APDUSend( p_access, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1286 }
1287
1288 /*****************************************************************************
1289  * ConditionalAccessHandle
1290  *****************************************************************************/
1291 static void ConditionalAccessHandle( access_t * p_access, int i_session_id,
1292                                      uint8_t *p_apdu, int i_size )
1293 {
1294     access_sys_t *p_sys = p_access->p_sys;
1295     system_ids_t *p_ids =
1296         (system_ids_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1297     int i_tag = APDUGetTag( p_apdu, i_size );
1298
1299     switch ( i_tag )
1300     {
1301     case AOT_CA_INFO:
1302     {
1303         int i;
1304         int l = 0;
1305         uint8_t *d = APDUGetLength( p_apdu, &l );
1306         msg_Dbg( p_access, "CA system IDs supported by the application :" );
1307
1308         for ( i = 0; i < l / 2; i++ )
1309         {
1310             p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1311             d += 2;
1312             msg_Dbg( p_access, "- 0x%x", p_ids->pi_system_ids[i] );
1313         }
1314         p_ids->pi_system_ids[i] = 0;
1315
1316         for ( i = 0; i < MAX_PROGRAMS; i++ )
1317         {
1318             if ( p_sys->pp_selected_programs[i] != NULL )
1319             {
1320                 CAPMTAdd( p_access, i_session_id,
1321                           p_sys->pp_selected_programs[i] );
1322             }
1323         }
1324         break;
1325     }
1326
1327     default:
1328         msg_Err( p_access,
1329                  "unexpected tag in ConditionalAccessHandle (0x%x)",
1330                  i_tag );
1331     }
1332 }
1333
1334 /*****************************************************************************
1335  * ConditionalAccessClose
1336  *****************************************************************************/
1337 static void ConditionalAccessClose( access_t * p_access, int i_session_id )
1338 {
1339     access_sys_t *p_sys = p_access->p_sys;
1340
1341     msg_Dbg( p_access, "closing ConditionalAccess session (%d)", i_session_id );
1342
1343     free( p_sys->p_sessions[i_session_id - 1].p_sys );
1344 }
1345
1346 /*****************************************************************************
1347  * ConditionalAccessOpen
1348  *****************************************************************************/
1349 static void ConditionalAccessOpen( access_t * p_access, int i_session_id )
1350 {
1351     access_sys_t *p_sys = p_access->p_sys;
1352
1353     msg_Dbg( p_access, "opening ConditionalAccess session (%d)", i_session_id );
1354
1355     p_sys->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1356     p_sys->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1357     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(system_ids_t));
1358     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0,
1359             sizeof(system_ids_t) );
1360
1361     APDUSend( p_access, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1362 }
1363
1364 /*
1365  * Date Time
1366  */
1367
1368 typedef struct
1369 {
1370     int i_interval;
1371     mtime_t i_last;
1372 } date_time_t;
1373
1374 /*****************************************************************************
1375  * DateTimeSend
1376  *****************************************************************************/
1377 static void DateTimeSend( access_t * p_access, int i_session_id )
1378 {
1379     access_sys_t *p_sys = p_access->p_sys;
1380     date_time_t *p_date =
1381         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1382
1383     time_t t = time(NULL);
1384     struct tm tm_gmt;
1385     struct tm tm_loc;
1386
1387     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1388     {
1389         int Y = tm_gmt.tm_year;
1390         int M = tm_gmt.tm_mon + 1;
1391         int D = tm_gmt.tm_mday;
1392         int L = (M == 1 || M == 2) ? 1 : 0;
1393         int MJD = 14956 + D + (int)((Y - L) * 365.25)
1394                     + (int)((M + 1 + L * 12) * 30.6001);
1395         uint8_t p_response[7];
1396
1397 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1398
1399         p_response[0] = htons(MJD) >> 8;
1400         p_response[1] = htons(MJD) & 0xff;
1401         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1402         p_response[3] = DEC2BCD(tm_gmt.tm_min);
1403         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1404         p_response[5] = htons(tm_loc.tm_gmtoff / 60) >> 8;
1405         p_response[6] = htons(tm_loc.tm_gmtoff / 60) & 0xff;
1406
1407         APDUSend( p_access, i_session_id, AOT_DATE_TIME, p_response, 7 );
1408
1409         p_date->i_last = mdate();
1410     }
1411 }
1412
1413 /*****************************************************************************
1414  * DateTimeHandle
1415  *****************************************************************************/
1416 static void DateTimeHandle( access_t * p_access, int i_session_id,
1417                             uint8_t *p_apdu, int i_size )
1418 {
1419     access_sys_t *p_sys = p_access->p_sys;
1420     date_time_t *p_date =
1421         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1422
1423     int i_tag = APDUGetTag( p_apdu, i_size );
1424
1425     switch ( i_tag )
1426     {
1427     case AOT_DATE_TIME_ENQ:
1428     {
1429         int l;
1430         const uint8_t *d = APDUGetLength( p_apdu, &l );
1431
1432         if ( l > 0 )
1433         {
1434             p_date->i_interval = *d;
1435             msg_Dbg( p_access, "DateTimeHandle : interval set to %d",
1436                      p_date->i_interval );
1437         }
1438         else
1439             p_date->i_interval = 0;
1440
1441         DateTimeSend( p_access, i_session_id );
1442         break;
1443     }
1444     default:
1445         msg_Err( p_access, "unexpected tag in DateTimeHandle (0x%x)", i_tag );
1446     }
1447 }
1448
1449 /*****************************************************************************
1450  * DateTimeManage
1451  *****************************************************************************/
1452 static void DateTimeManage( access_t * p_access, int i_session_id )
1453 {
1454     access_sys_t *p_sys = p_access->p_sys;
1455     date_time_t *p_date =
1456         (date_time_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1457
1458     if ( p_date->i_interval
1459           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1460     {
1461         DateTimeSend( p_access, i_session_id );
1462     }
1463 }
1464
1465 /*****************************************************************************
1466  * DateTimeClose
1467  *****************************************************************************/
1468 static void DateTimeClose( access_t * p_access, int i_session_id )
1469 {
1470     access_sys_t *p_sys = p_access->p_sys;
1471
1472     msg_Dbg( p_access, "closing DateTime session (%d)", i_session_id );
1473
1474     free( p_sys->p_sessions[i_session_id - 1].p_sys );
1475 }
1476
1477 /*****************************************************************************
1478  * DateTimeOpen
1479  *****************************************************************************/
1480 static void DateTimeOpen( access_t * p_access, int i_session_id )
1481 {
1482     access_sys_t *p_sys = p_access->p_sys;
1483
1484     msg_Dbg( p_access, "opening DateTime session (%d)", i_session_id );
1485
1486     p_sys->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1487     p_sys->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1488     p_sys->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1489     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(date_time_t));
1490     memset( p_sys->p_sessions[i_session_id - 1].p_sys, 0, sizeof(date_time_t) );
1491
1492     DateTimeSend( p_access, i_session_id );
1493 }
1494
1495 /*
1496  * MMI
1497  */
1498
1499 /* Display Control Commands */
1500
1501 #define DCC_SET_MMI_MODE                          0x01
1502 #define DCC_DISPLAY_CHARACTER_TABLE_LIST          0x02
1503 #define DCC_INPUT_CHARACTER_TABLE_LIST            0x03
1504 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS      0x04
1505 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS  0x05
1506
1507 /* MMI Modes */
1508
1509 #define MM_HIGH_LEVEL                      0x01
1510 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS      0x02
1511 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS  0x03
1512
1513 /* Display Reply IDs */
1514
1515 #define DRI_MMI_MODE_ACK                              0x01
1516 #define DRI_LIST_DISPLAY_CHARACTER_TABLES             0x02
1517 #define DRI_LIST_INPUT_CHARACTER_TABLES               0x03
1518 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS      0x04
1519 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS  0x05
1520 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD               0xF0
1521 #define DRI_UNKNOWN_MMI_MODE                          0xF1
1522 #define DRI_UNKNOWN_CHARACTER_TABLE                   0xF2
1523
1524 /* Enquiry Flags */
1525
1526 #define EF_BLIND  0x01
1527
1528 /* Answer IDs */
1529
1530 #define AI_CANCEL  0x00
1531 #define AI_ANSWER  0x01
1532
1533 typedef struct
1534 {
1535     en50221_mmi_object_t last_object;
1536 } mmi_t;
1537
1538 /*****************************************************************************
1539  * MMISendObject
1540  *****************************************************************************/
1541 static void MMISendObject( access_t *p_access, int i_session_id,
1542                            en50221_mmi_object_t *p_object )
1543 {
1544     access_sys_t *p_sys = p_access->p_sys;
1545     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1546     uint8_t *p_data;
1547     int i_size, i_tag;
1548
1549     switch ( p_object->i_object_type )
1550     {
1551     case EN50221_MMI_ANSW:
1552         i_tag = AOT_ANSW;
1553         i_size = 1 + strlen( p_object->u.answ.psz_answ );
1554         p_data = malloc( i_size );
1555         p_data[0] = (p_object->u.answ.b_ok == true) ? 0x1 : 0x0;
1556         strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1557         break;
1558
1559     case EN50221_MMI_MENU_ANSW:
1560         i_tag = AOT_MENU_ANSW;
1561         i_size = 1;
1562         p_data = malloc( i_size );
1563         p_data[0] = p_object->u.menu_answ.i_choice;
1564         break;
1565
1566     default:
1567         msg_Err( p_access, "unknown MMI object %d", p_object->i_object_type );
1568         return;
1569     }
1570
1571     APDUSend( p_access, i_session_id, i_tag, p_data, i_size );
1572     free( p_data );
1573
1574     p_sys->pb_slot_mmi_expected[i_slot] = true;
1575 }
1576
1577 /*****************************************************************************
1578  * MMISendClose
1579  *****************************************************************************/
1580 static void MMISendClose( access_t *p_access, int i_session_id )
1581 {
1582     access_sys_t *p_sys = p_access->p_sys;
1583     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1584
1585     APDUSend( p_access, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1586
1587     p_sys->pb_slot_mmi_expected[i_slot] = true;
1588 }
1589
1590 /*****************************************************************************
1591  * MMIDisplayReply
1592  *****************************************************************************/
1593 static void MMIDisplayReply( access_t *p_access, int i_session_id )
1594 {
1595     uint8_t p_response[2];
1596
1597     p_response[0] = DRI_MMI_MODE_ACK;
1598     p_response[1] = MM_HIGH_LEVEL;
1599
1600     APDUSend( p_access, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1601
1602     msg_Dbg( p_access, "sending DisplayReply on session (%d)", i_session_id );
1603 }
1604
1605 /*****************************************************************************
1606  * MMIGetText
1607  *****************************************************************************/
1608 static char *MMIGetText( access_t *p_access, uint8_t **pp_apdu, int *pi_size )
1609 {
1610     int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1611     int l;
1612     uint8_t *d;
1613
1614     if ( i_tag != AOT_TEXT_LAST )
1615     {
1616         msg_Err( p_access, "unexpected text tag: %06x", i_tag );
1617         *pi_size = 0;
1618         return strdup( "" );
1619     }
1620
1621     d = APDUGetLength( *pp_apdu, &l );
1622
1623     *pp_apdu += l + 4;
1624     *pi_size -= l + 4;
1625
1626     return dvbsi_to_utf8((char*)d,l);
1627 }
1628
1629 /*****************************************************************************
1630  * MMIHandleEnq
1631  *****************************************************************************/
1632 static void MMIHandleEnq( access_t *p_access, int i_session_id,
1633                           uint8_t *p_apdu, int i_size )
1634 {
1635     access_sys_t *p_sys = p_access->p_sys;
1636     mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1637     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1638     int l;
1639     uint8_t *d = APDUGetLength( p_apdu, &l );
1640
1641     en50221_MMIFree( &p_mmi->last_object );
1642     p_mmi->last_object.i_object_type = EN50221_MMI_ENQ;
1643     p_mmi->last_object.u.enq.b_blind = (*d & 0x1) ? true : false;
1644     d += 2; /* skip answer_text_length because it is not mandatory */
1645     l -= 2;
1646     p_mmi->last_object.u.enq.psz_text = malloc( l + 1 );
1647     strncpy( p_mmi->last_object.u.enq.psz_text, (char *)d, l );
1648     p_mmi->last_object.u.enq.psz_text[l] = '\0';
1649
1650     msg_Dbg( p_access, "MMI enq: %s%s", p_mmi->last_object.u.enq.psz_text,
1651              p_mmi->last_object.u.enq.b_blind == true ? " (blind)" : "" );
1652     p_sys->pb_slot_mmi_expected[i_slot] = false;
1653     p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1654 }
1655
1656 /*****************************************************************************
1657  * MMIHandleMenu
1658  *****************************************************************************/
1659 static void MMIHandleMenu( access_t *p_access, int i_session_id, int i_tag,
1660                            uint8_t *p_apdu, int i_size )
1661 {
1662     access_sys_t *p_sys = p_access->p_sys;
1663     mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1664     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1665     int l;
1666     uint8_t *d = APDUGetLength( p_apdu, &l );
1667
1668     en50221_MMIFree( &p_mmi->last_object );
1669     p_mmi->last_object.i_object_type = (i_tag == AOT_MENU_LAST) ?
1670                                        EN50221_MMI_MENU : EN50221_MMI_LIST;
1671     p_mmi->last_object.u.menu.i_choices = 0;
1672     p_mmi->last_object.u.menu.ppsz_choices = NULL;
1673
1674     if ( l > 0 )
1675     {
1676         l--; d++; /* choice_nb */
1677
1678 #define GET_FIELD( x )                                                      \
1679         if ( l > 0 )                                                        \
1680         {                                                                   \
1681             p_mmi->last_object.u.menu.psz_##x                               \
1682                             = MMIGetText( p_access, &d, &l );               \
1683             msg_Dbg( p_access, "MMI " STRINGIFY( x ) ": %s",                \
1684                      p_mmi->last_object.u.menu.psz_##x );                   \
1685         }
1686
1687         GET_FIELD( title );
1688         GET_FIELD( subtitle );
1689         GET_FIELD( bottom );
1690 #undef GET_FIELD
1691
1692         while ( l > 0 )
1693         {
1694             char *psz_text = MMIGetText( p_access, &d, &l );
1695             TAB_APPEND( p_mmi->last_object.u.menu.i_choices,
1696                         p_mmi->last_object.u.menu.ppsz_choices,
1697                         psz_text );
1698             msg_Dbg( p_access, "MMI choice: %s", psz_text );
1699         }
1700     }
1701     p_sys->pb_slot_mmi_expected[i_slot] = false;
1702     p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1703 }
1704
1705 /*****************************************************************************
1706  * MMIHandle
1707  *****************************************************************************/
1708 static void MMIHandle( access_t *p_access, int i_session_id,
1709                        uint8_t *p_apdu, int i_size )
1710 {
1711     int i_tag = APDUGetTag( p_apdu, i_size );
1712
1713     switch ( i_tag )
1714     {
1715     case AOT_DISPLAY_CONTROL:
1716     {
1717         int l;
1718         uint8_t *d = APDUGetLength( p_apdu, &l );
1719
1720         if ( l > 0 )
1721         {
1722             switch ( *d )
1723             {
1724             case DCC_SET_MMI_MODE:
1725                 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1726                     MMIDisplayReply( p_access, i_session_id );
1727                 else
1728                     msg_Err( p_access, "unsupported MMI mode %02x", d[1] );
1729                 break;
1730
1731             default:
1732                 msg_Err( p_access, "unsupported display control command %02x",
1733                          *d );
1734                 break;
1735             }
1736         }
1737         break;
1738     }
1739
1740     case AOT_ENQ:
1741         MMIHandleEnq( p_access, i_session_id, p_apdu, i_size );
1742         break;
1743
1744     case AOT_LIST_LAST:
1745     case AOT_MENU_LAST:
1746         MMIHandleMenu( p_access, i_session_id, i_tag, p_apdu, i_size );
1747         break;
1748
1749     case AOT_CLOSE_MMI:
1750         SessionSendClose( p_access, i_session_id );
1751         break;
1752
1753     default:
1754         msg_Err( p_access, "unexpected tag in MMIHandle (0x%x)", i_tag );
1755     }
1756 }
1757
1758 /*****************************************************************************
1759  * MMIClose
1760  *****************************************************************************/
1761 static void MMIClose( access_t *p_access, int i_session_id )
1762 {
1763     access_sys_t *p_sys = p_access->p_sys;
1764     int i_slot = p_sys->p_sessions[i_session_id - 1].i_slot;
1765     mmi_t *p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1766
1767     en50221_MMIFree( &p_mmi->last_object );
1768     free( p_sys->p_sessions[i_session_id - 1].p_sys );
1769
1770     msg_Dbg( p_access, "closing MMI session (%d)", i_session_id );
1771     p_sys->pb_slot_mmi_expected[i_slot] = false;
1772     p_sys->pb_slot_mmi_undisplayed[i_slot] = true;
1773 }
1774
1775 /*****************************************************************************
1776  * MMIOpen
1777  *****************************************************************************/
1778 static void MMIOpen( access_t *p_access, int i_session_id )
1779 {
1780     access_sys_t *p_sys = p_access->p_sys;
1781     mmi_t *p_mmi;
1782
1783     msg_Dbg( p_access, "opening MMI session (%d)", i_session_id );
1784
1785     p_sys->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1786     p_sys->p_sessions[i_session_id - 1].pf_close = MMIClose;
1787     p_sys->p_sessions[i_session_id - 1].p_sys = malloc(sizeof(mmi_t));
1788     p_mmi = (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
1789     p_mmi->last_object.i_object_type = EN50221_MMI_NONE;
1790 }
1791
1792
1793 /*
1794  * Hardware handling
1795  */
1796
1797 /*****************************************************************************
1798  * InitSlot: Open the transport layer
1799  *****************************************************************************/
1800 #define MAX_TC_RETRIES 20
1801
1802 static int InitSlot( access_t * p_access, int i_slot )
1803 {
1804     access_sys_t *p_sys = p_access->p_sys;
1805     int i;
1806
1807     if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1808             != VLC_SUCCESS )
1809     {
1810         msg_Err( p_access, "en50221_Init: couldn't send TPDU on slot %d",
1811                  i_slot );
1812         return VLC_EGENERIC;
1813     }
1814
1815     /* This is out of the spec */
1816     for ( i = 0; i < MAX_TC_RETRIES; i++ )
1817     {
1818         uint8_t i_tag;
1819         if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1820               && i_tag == T_CTC_REPLY )
1821         {
1822             p_sys->pb_active_slot[i_slot] = true;
1823             break;
1824         }
1825
1826         if ( TPDUSend( p_access, i_slot, T_CREATE_TC, NULL, 0 )
1827                 != VLC_SUCCESS )
1828         {
1829             msg_Err( p_access,
1830                      "en50221_Init: couldn't send TPDU on slot %d",
1831                      i_slot );
1832             continue;
1833         }
1834     }
1835
1836     if ( p_sys->pb_active_slot[i_slot] )
1837     {
1838         p_sys->i_ca_timeout = 100000;
1839         return VLC_SUCCESS;
1840     }
1841
1842     return VLC_EGENERIC;
1843 }
1844
1845
1846 /*
1847  * External entry points
1848  */
1849
1850 /*****************************************************************************
1851  * en50221_Init : Initialize the CAM for en50221
1852  *****************************************************************************/
1853 int en50221_Init( access_t * p_access )
1854 {
1855     access_sys_t *p_sys = p_access->p_sys;
1856
1857     if( p_sys->i_ca_type & CA_CI_LINK )
1858     {
1859         int i_slot;
1860         for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1861         {
1862             if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
1863             {
1864                 msg_Err( p_access, "en50221_Init: couldn't reset slot %d",
1865                          i_slot );
1866             }
1867         }
1868
1869         p_sys->i_ca_timeout = 100000;
1870         /* Wait a bit otherwise it doesn't initialize properly... */
1871         msleep( 1000000 );
1872
1873         return VLC_SUCCESS;
1874     }
1875     else
1876     {
1877         struct ca_slot_info info;
1878         info.num = 0;
1879
1880         /* We don't reset the CAM in that case because it's done by the
1881          * ASIC. */
1882         if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &info ) < 0 )
1883         {
1884             msg_Err( p_access, "en50221_Init: couldn't get slot info" );
1885             close( p_sys->i_ca_handle );
1886             p_sys->i_ca_handle = 0;
1887             return VLC_EGENERIC;
1888         }
1889         if( info.flags == 0 )
1890         {
1891             msg_Err( p_access, "en50221_Init: no CAM inserted" );
1892             close( p_sys->i_ca_handle );
1893             p_sys->i_ca_handle = 0;
1894             return VLC_EGENERIC;
1895         }
1896
1897         /* Allocate a dummy sessions */
1898         p_sys->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
1899
1900         /* Get application info to find out which cam we are using and make
1901            sure everything is ready to play */
1902         ca_msg_t ca_msg;
1903         ca_msg.length=3;
1904         ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1905         ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1906         ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1907         memset( &ca_msg.msg[3], 0, 253 );
1908         APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1909         if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1910         {
1911             msg_Err( p_access, "en50221_Init: failed getting message" );
1912             return VLC_EGENERIC;
1913         }
1914
1915 #if HLCI_WAIT_CAM_READY
1916         while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1917         {
1918             if( !vlc_object_alive (p_access) ) return VLC_EGENERIC;
1919             msleep(1);
1920             msg_Dbg( p_access, "CAM: please wait" );
1921             APDUSend( p_access, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1922             ca_msg.length=3;
1923             ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
1924             ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
1925             ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
1926             memset( &ca_msg.msg[3], 0, 253 );
1927             if ( ioctl( p_sys->i_ca_handle, CA_GET_MSG, &ca_msg ) < 0 )
1928             {
1929                 msg_Err( p_access, "en50221_Init: failed getting message" );
1930                 return VLC_EGENERIC;
1931             }
1932             msg_Dbg( p_access, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
1933         }
1934 #else
1935         if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
1936         {
1937             msg_Err( p_access, "CAM returns garbage as application info!" );
1938             return VLC_EGENERIC;
1939         }
1940 #endif
1941         msg_Dbg( p_access, "found CAM %s using id 0x%x", &ca_msg.msg[12],
1942                  (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
1943         return VLC_SUCCESS;
1944     }
1945 }
1946
1947 /*****************************************************************************
1948  * en50221_Poll : Poll the CAM for TPDUs
1949  *****************************************************************************/
1950 int en50221_Poll( access_t * p_access )
1951 {
1952     access_sys_t *p_sys = p_access->p_sys;
1953     int i_slot;
1954     int i_session_id;
1955
1956     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1957     {
1958         uint8_t i_tag;
1959         ca_slot_info_t sinfo;
1960
1961         sinfo.num = i_slot;
1962         if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1963         {
1964             msg_Err( p_access, "en50221_Poll: couldn't get info on slot %d",
1965                      i_slot );
1966             continue;
1967         }
1968
1969         if ( !(sinfo.flags & CA_CI_MODULE_READY) )
1970         {
1971             if ( p_sys->pb_active_slot[i_slot] )
1972             {
1973                 msg_Dbg( p_access, "en50221_Poll: slot %d has been removed",
1974                          i_slot );
1975                 p_sys->pb_active_slot[i_slot] = false;
1976                 p_sys->pb_slot_mmi_expected[i_slot] = false;
1977                 p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
1978
1979                 /* Close all sessions for this slot. */
1980                 for ( i_session_id = 1; i_session_id <= MAX_SESSIONS;
1981                       i_session_id++ )
1982                 {
1983                     if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
1984                           && p_sys->p_sessions[i_session_id - 1].i_slot
1985                                == i_slot )
1986                     {
1987                         if ( p_sys->p_sessions[i_session_id - 1].pf_close
1988                               != NULL )
1989                         {
1990                             p_sys->p_sessions[i_session_id - 1].pf_close(
1991                                                 p_access, i_session_id );
1992                         }
1993                         p_sys->p_sessions[i_session_id - 1].i_resource_id = 0;
1994                     }
1995                 }
1996             }
1997
1998             continue;
1999         }
2000         else if ( !p_sys->pb_active_slot[i_slot] )
2001         {
2002             InitSlot( p_access, i_slot );
2003
2004             if ( !p_sys->pb_active_slot[i_slot] )
2005             {
2006                 msg_Dbg( p_access, "en50221_Poll: resetting slot %d", i_slot );
2007
2008                 if ( ioctl( p_sys->i_ca_handle, CA_RESET, 1 << i_slot) != 0 )
2009                 {
2010                     msg_Err( p_access, "en50221_Poll: couldn't reset slot %d",
2011                              i_slot );
2012                 }
2013                 continue;
2014             }
2015
2016             msg_Dbg( p_access, "en50221_Poll: slot %d is active",
2017                      i_slot );
2018         }
2019
2020         if ( !p_sys->pb_tc_has_data[i_slot] )
2021         {
2022             if ( TPDUSend( p_access, i_slot, T_DATA_LAST, NULL, 0 ) !=
2023                     VLC_SUCCESS )
2024             {
2025                 msg_Err( p_access,
2026                          "en50221_Poll: couldn't send TPDU on slot %d",
2027                          i_slot );
2028                 continue;
2029             }
2030             if ( TPDURecv( p_access, i_slot, &i_tag, NULL, NULL ) !=
2031                     VLC_SUCCESS )
2032             {
2033                 msg_Err( p_access,
2034                          "en50221_Poll: couldn't recv TPDU on slot %d",
2035                          i_slot );
2036                 continue;
2037             }
2038         }
2039
2040         while ( p_sys->pb_tc_has_data[i_slot] )
2041         {
2042             uint8_t p_tpdu[MAX_TPDU_SIZE];
2043             int i_size, i_session_size;
2044             uint8_t *p_session;
2045
2046             if ( TPDUSend( p_access, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2047             {
2048                 msg_Err( p_access,
2049                          "en50221_Poll: couldn't send TPDU on slot %d",
2050                          i_slot );
2051                 continue;
2052             }
2053             if ( TPDURecv( p_access, i_slot, &i_tag, p_tpdu, &i_size ) !=
2054                     VLC_SUCCESS )
2055             {
2056                 msg_Err( p_access,
2057                          "en50221_Poll: couldn't recv TPDU on slot %d",
2058                          i_slot );
2059                 continue;
2060             }
2061
2062             p_session = GetLength( &p_tpdu[3], &i_session_size );
2063             if ( i_session_size <= 1 )
2064                 continue;
2065
2066             p_session++;
2067             i_session_size--;
2068
2069             if ( i_tag != T_DATA_LAST )
2070             {
2071                 msg_Err( p_access,
2072                          "en50221_Poll: fragmented TPDU not supported" );
2073                 break;
2074             }
2075
2076             SPDUHandle( p_access, i_slot, p_session, i_session_size );
2077         }
2078     }
2079
2080     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2081     {
2082         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2083               && p_sys->p_sessions[i_session_id - 1].pf_manage )
2084         {
2085             p_sys->p_sessions[i_session_id - 1].pf_manage( p_access,
2086                                                            i_session_id );
2087         }
2088     }
2089
2090     return VLC_SUCCESS;
2091 }
2092
2093
2094 /*****************************************************************************
2095  * en50221_SetCAPMT :
2096  *****************************************************************************/
2097 int en50221_SetCAPMT( access_t * p_access, dvbpsi_pmt_t *p_pmt )
2098 {
2099     access_sys_t *p_sys = p_access->p_sys;
2100     int i, i_session_id;
2101     bool b_update = false;
2102     bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2103
2104     for ( i = 0; i < MAX_PROGRAMS; i++ )
2105     {
2106         if ( p_sys->pp_selected_programs[i] != NULL
2107               && p_sys->pp_selected_programs[i]->i_program_number
2108                   == p_pmt->i_program_number )
2109         {
2110             b_update = true;
2111
2112             if ( !b_needs_descrambling )
2113             {
2114                 dvbpsi_DeletePMT( p_pmt );
2115                 p_pmt = p_sys->pp_selected_programs[i];
2116                 p_sys->pp_selected_programs[i] = NULL;
2117             }
2118             else if( p_pmt != p_sys->pp_selected_programs[i] )
2119             {
2120                 dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2121                 p_sys->pp_selected_programs[i] = p_pmt;
2122             }
2123
2124             break;
2125         }
2126     }
2127
2128     if ( !b_update && b_needs_descrambling )
2129     {
2130         for ( i = 0; i < MAX_PROGRAMS; i++ )
2131         {
2132             if ( p_sys->pp_selected_programs[i] == NULL )
2133             {
2134                 p_sys->pp_selected_programs[i] = p_pmt;
2135                 break;
2136             }
2137         }
2138     }
2139
2140     if ( b_update || b_needs_descrambling )
2141     {
2142         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2143         {
2144             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2145                     == RI_CONDITIONAL_ACCESS_SUPPORT )
2146             {
2147                 if ( b_update && b_needs_descrambling )
2148                     CAPMTUpdate( p_access, i_session_id, p_pmt );
2149                 else if ( b_update )
2150                     CAPMTDelete( p_access, i_session_id, p_pmt );
2151                 else
2152                     CAPMTAdd( p_access, i_session_id, p_pmt );
2153             }
2154         }
2155     }
2156
2157     if ( !b_needs_descrambling )
2158     {
2159         dvbpsi_DeletePMT( p_pmt );
2160     }
2161
2162     return VLC_SUCCESS;
2163 }
2164
2165 /*****************************************************************************
2166  * en50221_OpenMMI :
2167  *****************************************************************************/
2168 int en50221_OpenMMI( access_t * p_access, int i_slot )
2169 {
2170     access_sys_t *p_sys = p_access->p_sys;
2171
2172     if( p_sys->i_ca_type & CA_CI_LINK )
2173     {
2174         int i_session_id;
2175         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2176         {
2177             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2178                   && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2179             {
2180                 msg_Dbg( p_access,
2181                          "MMI menu is already opened on slot %d (session=%d)",
2182                          i_slot, i_session_id );
2183                 return VLC_SUCCESS;
2184             }
2185         }
2186
2187         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2188         {
2189             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2190                     == RI_APPLICATION_INFORMATION
2191                   && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2192             {
2193                 ApplicationInformationEnterMenu( p_access, i_session_id );
2194                 return VLC_SUCCESS;
2195             }
2196         }
2197
2198         msg_Err( p_access, "no application information on slot %d", i_slot );
2199         return VLC_EGENERIC;
2200     }
2201     else
2202     {
2203         msg_Err( p_access, "MMI menu not supported" );
2204         return VLC_EGENERIC;
2205     }
2206 }
2207
2208 /*****************************************************************************
2209  * en50221_CloseMMI :
2210  *****************************************************************************/
2211 int en50221_CloseMMI( access_t * p_access, int i_slot )
2212 {
2213     access_sys_t *p_sys = p_access->p_sys;
2214
2215     if( p_sys->i_ca_type & CA_CI_LINK )
2216     {
2217         int i_session_id;
2218         for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2219         {
2220             if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2221                   && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2222             {
2223                 MMISendClose( p_access, i_session_id );
2224                 return VLC_SUCCESS;
2225             }
2226         }
2227
2228         msg_Warn( p_access, "closing a non-existing MMI session on slot %d",
2229                   i_slot );
2230         return VLC_EGENERIC;
2231     }
2232     else
2233     {
2234         msg_Err( p_access, "MMI menu not supported" );
2235         return VLC_EGENERIC;
2236     }
2237 }
2238
2239 /*****************************************************************************
2240  * en50221_GetMMIObject :
2241  *****************************************************************************/
2242 en50221_mmi_object_t *en50221_GetMMIObject( access_t * p_access,
2243                                                 int i_slot )
2244 {
2245     access_sys_t *p_sys = p_access->p_sys;
2246     int i_session_id;
2247
2248     if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
2249         return NULL; /* should not happen */
2250
2251     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2252     {
2253         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2254               && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2255         {
2256             mmi_t *p_mmi =
2257                 (mmi_t *)p_sys->p_sessions[i_session_id - 1].p_sys;
2258             if ( p_mmi == NULL )
2259                 return NULL; /* should not happen */
2260             return &p_mmi->last_object;
2261         }
2262     }
2263
2264     return NULL;
2265 }
2266
2267
2268 /*****************************************************************************
2269  * en50221_SendMMIObject :
2270  *****************************************************************************/
2271 void en50221_SendMMIObject( access_t * p_access, int i_slot,
2272                                 en50221_mmi_object_t *p_object )
2273 {
2274     access_sys_t *p_sys = p_access->p_sys;
2275     int i_session_id;
2276
2277     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2278     {
2279         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id == RI_MMI
2280               && p_sys->p_sessions[i_session_id - 1].i_slot == i_slot )
2281         {
2282             MMISendObject( p_access, i_session_id, p_object );
2283             return;
2284         }
2285     }
2286
2287     msg_Err( p_access, "SendMMIObject when no MMI session is opened !" );
2288 }
2289
2290 /*****************************************************************************
2291  * en50221_End :
2292  *****************************************************************************/
2293 void en50221_End( access_t * p_access )
2294 {
2295     access_sys_t *p_sys = p_access->p_sys;
2296     int i_session_id, i;
2297
2298     for ( i = 0; i < MAX_PROGRAMS; i++ )
2299     {
2300         if ( p_sys->pp_selected_programs[i] != NULL )
2301         {
2302             dvbpsi_DeletePMT( p_sys->pp_selected_programs[i] );
2303         }
2304     }
2305
2306     for ( i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2307     {
2308         if ( p_sys->p_sessions[i_session_id - 1].i_resource_id
2309               && p_sys->p_sessions[i_session_id - 1].pf_close != NULL )
2310         {
2311             p_sys->p_sessions[i_session_id - 1].pf_close( p_access,
2312                                                           i_session_id );
2313         }
2314     }
2315
2316     /* Leave the CAM configured, so that it can be reused in another
2317      * program. */
2318 }
2319
2320 static inline void *FixUTF8( char *p )
2321 {
2322     EnsureUTF8( p );
2323     return p;
2324 }
2325
2326 char *dvbsi_to_utf8( char *psz_instring, size_t i_length )
2327 {
2328     const char *psz_encoding, *psz_stringstart;
2329     char *psz_outstring, *psz_tmp;
2330     char psz_encbuf[12];
2331     size_t i_in, i_out;
2332     vlc_iconv_t iconv_handle;
2333     if( i_length < 1 ) return NULL;
2334     if( psz_instring[0] < 0 || psz_instring[0] >= 0x20 )
2335     {
2336         psz_stringstart = psz_instring;
2337         psz_encoding = "ISO_8859-1"; /* should be ISO6937 according to spec, but this seems to be the one used */
2338     } else switch( psz_instring[0] )
2339     {
2340     case 0x01:
2341         psz_stringstart = &psz_instring[1];
2342         psz_encoding = "ISO_8859-5";
2343         break;
2344     case 0x02:
2345         psz_stringstart = &psz_instring[1];
2346         psz_encoding = "ISO_8859-6";
2347         break;
2348     case 0x03:
2349         psz_stringstart = &psz_instring[1];
2350         psz_encoding = "ISO_8859-7";
2351         break;
2352     case 0x04:
2353         psz_stringstart = &psz_instring[1];
2354         psz_encoding = "ISO_8859-8";
2355         break;
2356     case 0x05:
2357         psz_stringstart = &psz_instring[1];
2358         psz_encoding = "ISO_8859-9";
2359         break;
2360     case 0x06:
2361         psz_stringstart = &psz_instring[1];
2362         psz_encoding = "ISO_8859-10";
2363         break;
2364     case 0x07:
2365         psz_stringstart = &psz_instring[1];
2366         psz_encoding = "ISO_8859-11";
2367         break;
2368     case 0x08:
2369         psz_stringstart = &psz_instring[1]; /*possibly reserved?*/
2370         psz_encoding = "ISO_8859-12";
2371         break;
2372     case 0x09:
2373         psz_stringstart = &psz_instring[1];
2374         psz_encoding = "ISO_8859-13";
2375         break;
2376     case 0x0a:
2377         psz_stringstart = &psz_instring[1];
2378         psz_encoding = "ISO_8859-14";
2379         break;
2380     case 0x0b:
2381         psz_stringstart = &psz_instring[1];
2382         psz_encoding = "ISO_8859-15";
2383         break;
2384     case 0x10:
2385         if( i_length < 3 || psz_instring[1] != '\0' || psz_instring[2] > 0x0f
2386             || psz_instring[2] == 0 )
2387             return FixUTF8(strndup(psz_instring,i_length));
2388         sprintf( psz_encbuf, "ISO_8859-%d", psz_instring[2] );
2389         psz_stringstart = &psz_instring[3];
2390         psz_encoding = psz_encbuf;
2391         break;
2392     case 0x11:
2393         psz_stringstart = &psz_instring[1];
2394         psz_encoding = "UTF-16";
2395         break;
2396     case 0x12:
2397         psz_stringstart = &psz_instring[1];
2398         psz_encoding = "KSC5601-1987";
2399         break;
2400     case 0x13:
2401         psz_stringstart = &psz_instring[1];
2402         psz_encoding = "GB2312";/*GB-2312-1980 */
2403         break;
2404     case 0x14:
2405         psz_stringstart = &psz_instring[1];
2406         psz_encoding = "BIG-5";
2407         break;
2408     case 0x15:
2409         return FixUTF8(strndup(&psz_instring[1],i_length-1));
2410         break;
2411     default:
2412         /* invalid */
2413         return FixUTF8(strndup(psz_instring,i_length));
2414     }
2415     iconv_handle = vlc_iconv_open( "UTF-8", psz_encoding );
2416     i_in = i_length - (psz_stringstart - psz_instring );
2417     i_out = i_in * 6;
2418     psz_outstring = psz_tmp = (char*)malloc( i_out * sizeof(char) + 1 );
2419     vlc_iconv( iconv_handle, &psz_stringstart, &i_in, &psz_tmp, &i_out );
2420     vlc_iconv_close( iconv_handle );
2421     *psz_tmp = '\0';
2422     return psz_outstring;
2423 }