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