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