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