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