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