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