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