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