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