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