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