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