edcc56d379302b4d96ec5ddbb2ced24ac2bcf60b
[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 the VideoLAN team
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  * Based on code from libdvbci Copyright (C) 2000 Klaus Schmidinger
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_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 /*****************************************************************************
943  * ApplicationInformationEnterMenu
944  *****************************************************************************/
945 static void ApplicationInformationEnterMenu( cam_t * p_cam, int i_session_id )
946 {
947     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
948
949     msg_Dbg( p_cam->obj, "entering MMI menus on session %d", i_session_id );
950     APDUSend( p_cam, i_session_id, AOT_ENTER_MENU, NULL, 0 );
951     p_cam->pb_slot_mmi_expected[i_slot] = true;
952 }
953
954 /*****************************************************************************
955  * ApplicationInformationHandle
956  *****************************************************************************/
957 static void ApplicationInformationHandle( cam_t * p_cam, int i_session_id,
958                                           uint8_t *p_apdu, int i_size )
959 {
960     VLC_UNUSED(i_session_id);
961     int i_tag = APDUGetTag( p_apdu, i_size );
962
963     switch ( i_tag )
964     {
965     case AOT_APPLICATION_INFO:
966     {
967         int i_type, i_manufacturer, i_code;
968         int l = 0;
969         uint8_t *d = APDUGetLength( p_apdu, &l );
970
971         if ( l < 4 ) break;
972         p_apdu[l + 4] = '\0';
973
974         i_type = *d++;
975         i_manufacturer = ((int)d[0] << 8) | d[1];
976         d += 2;
977         i_code = ((int)d[0] << 8) | d[1];
978         d += 2;
979         d = GetLength( d, &l );
980         d[l] = '\0';
981         msg_Info( p_cam->obj, "CAM: %s, %02X, %04X, %04X",
982                   d, i_type, i_manufacturer, i_code );
983         break;
984     }
985     default:
986         msg_Err( p_cam->obj,
987                  "unexpected tag in ApplicationInformationHandle (0x%x)",
988                  i_tag );
989     }
990 }
991
992 /*****************************************************************************
993  * ApplicationInformationOpen
994  *****************************************************************************/
995 static void ApplicationInformationOpen( cam_t * p_cam, unsigned i_session_id )
996 {
997     msg_Dbg( p_cam->obj, "opening ApplicationInformation session (%u)",
998              i_session_id );
999     p_cam->p_sessions[i_session_id - 1].pf_handle =
1000         ApplicationInformationHandle;
1001     APDUSend( p_cam, i_session_id, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
1002 }
1003
1004 /*
1005  * Conditional Access
1006  */
1007
1008 #define MAX_CASYSTEM_IDS 64
1009
1010 typedef struct
1011 {
1012     uint16_t pi_system_ids[MAX_CASYSTEM_IDS + 1];
1013 } system_ids_t;
1014
1015 static bool CheckSystemID( system_ids_t *p_ids, uint16_t i_id )
1016 {
1017     int i = 0;
1018     if( !p_ids ) return true;      /* dummy session for high-level CI intf */
1019
1020     while ( p_ids->pi_system_ids[i] )
1021     {
1022         if ( p_ids->pi_system_ids[i] == i_id )
1023             return true;
1024         i++;
1025     }
1026
1027     return false;
1028 }
1029
1030 /*****************************************************************************
1031  * CAPMTNeedsDescrambling
1032  *****************************************************************************/
1033 static bool CAPMTNeedsDescrambling( dvbpsi_pmt_t *p_pmt )
1034 {
1035     dvbpsi_descriptor_t *p_dr;
1036     dvbpsi_pmt_es_t *p_es;
1037
1038     for( p_dr = p_pmt->p_first_descriptor; p_dr != NULL; p_dr = p_dr->p_next )
1039     {
1040         if( p_dr->i_tag == 0x9 )
1041         {
1042             return true;
1043         }
1044     }
1045  
1046     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1047     {
1048         for( p_dr = p_es->p_first_descriptor; p_dr != NULL;
1049              p_dr = p_dr->p_next )
1050         {
1051             if( p_dr->i_tag == 0x9 )
1052             {
1053                 return true;
1054             }
1055         }
1056     }
1057
1058     return false;
1059 }
1060
1061 /*****************************************************************************
1062  * CAPMTBuild
1063  *****************************************************************************/
1064 static int GetCADSize( system_ids_t *p_ids, dvbpsi_descriptor_t *p_dr )
1065 {
1066     int i_cad_size = 0;
1067
1068     while ( p_dr != NULL )
1069     {
1070         if( p_dr->i_tag == 0x9 )
1071         {
1072             uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1073                                     | p_dr->p_data[1];
1074             if ( CheckSystemID( p_ids, i_sysid ) )
1075                 i_cad_size += p_dr->i_length + 2;
1076         }
1077         p_dr = p_dr->p_next;
1078     }
1079
1080     return i_cad_size;
1081 }
1082
1083 static uint8_t *CAPMTHeader( system_ids_t *p_ids, uint8_t i_list_mgt,
1084                              uint16_t i_program_number, uint8_t i_version,
1085                              int i_size, dvbpsi_descriptor_t *p_dr,
1086                              uint8_t i_cmd )
1087 {
1088     uint8_t *p_data;
1089
1090     if ( i_size )
1091         p_data = xmalloc( 7 + i_size );
1092     else
1093         p_data = xmalloc( 6 );
1094
1095     p_data[0] = i_list_mgt;
1096     p_data[1] = i_program_number >> 8;
1097     p_data[2] = i_program_number & 0xff;
1098     p_data[3] = ((i_version & 0x1f) << 1) | 0x1;
1099
1100     if ( i_size )
1101     {
1102         int i;
1103
1104         p_data[4] = (i_size + 1) >> 8;
1105         p_data[5] = (i_size + 1) & 0xff;
1106         p_data[6] = i_cmd;
1107         i = 7;
1108
1109         while ( p_dr != NULL )
1110         {
1111             if( p_dr->i_tag == 0x9 )
1112             {
1113                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1114                                     | p_dr->p_data[1];
1115                 if ( CheckSystemID( p_ids, i_sysid ) )
1116                 {
1117                     p_data[i] = 0x9;
1118                     p_data[i + 1] = p_dr->i_length;
1119                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1120 //                    p_data[i+4] &= 0x1f;
1121                     i += p_dr->i_length + 2;
1122                 }
1123             }
1124             p_dr = p_dr->p_next;
1125         }
1126     }
1127     else
1128     {
1129         p_data[4] = 0;
1130         p_data[5] = 0;
1131     }
1132
1133     return p_data;
1134 }
1135
1136 static uint8_t *CAPMTES( system_ids_t *p_ids, uint8_t *p_capmt,
1137                          int i_capmt_size, uint8_t i_type, uint16_t i_pid,
1138                          int i_size, dvbpsi_descriptor_t *p_dr,
1139                          uint8_t i_cmd )
1140 {
1141     uint8_t *p_data;
1142     int i;
1143  
1144     if ( i_size )
1145         p_data = xrealloc( p_capmt, i_capmt_size + 6 + i_size );
1146     else
1147         p_data = xrealloc( p_capmt, i_capmt_size + 5 );
1148
1149     i = i_capmt_size;
1150
1151     p_data[i] = i_type;
1152     p_data[i + 1] = i_pid >> 8;
1153     p_data[i + 2] = i_pid & 0xff;
1154
1155     if ( i_size )
1156     {
1157         p_data[i + 3] = (i_size + 1) >> 8;
1158         p_data[i + 4] = (i_size + 1) & 0xff;
1159         p_data[i + 5] = i_cmd;
1160         i += 6;
1161
1162         while ( p_dr != NULL )
1163         {
1164             if( p_dr->i_tag == 0x9 )
1165             {
1166                 uint16_t i_sysid = ((uint16_t)p_dr->p_data[0] << 8)
1167                                     | p_dr->p_data[1];
1168                 if ( CheckSystemID( p_ids, i_sysid ) )
1169                 {
1170                     p_data[i] = 0x9;
1171                     p_data[i + 1] = p_dr->i_length;
1172                     memcpy( &p_data[i + 2], p_dr->p_data, p_dr->i_length );
1173                     i += p_dr->i_length + 2;
1174                 }
1175             }
1176             p_dr = p_dr->p_next;
1177         }
1178     }
1179     else
1180     {
1181         p_data[i + 3] = 0;
1182         p_data[i + 4] = 0;
1183     }
1184
1185     return p_data;
1186 }
1187
1188 static uint8_t *CAPMTBuild( cam_t * p_cam, int i_session_id,
1189                             dvbpsi_pmt_t *p_pmt, uint8_t i_list_mgt,
1190                             uint8_t i_cmd, int *pi_capmt_size )
1191 {
1192     system_ids_t *p_ids =
1193         (system_ids_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1194     dvbpsi_pmt_es_t *p_es;
1195     int i_cad_size, i_cad_program_size;
1196     uint8_t *p_capmt;
1197
1198     i_cad_size = i_cad_program_size =
1199             GetCADSize( p_ids, p_pmt->p_first_descriptor );
1200     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1201     {
1202         i_cad_size += GetCADSize( p_ids, p_es->p_first_descriptor );
1203     }
1204
1205     if ( !i_cad_size )
1206     {
1207         msg_Warn( p_cam->obj,
1208                   "no compatible scrambling system for SID %d on session %d",
1209                   p_pmt->i_program_number, i_session_id );
1210         *pi_capmt_size = 0;
1211         return NULL;
1212     }
1213
1214     p_capmt = CAPMTHeader( p_ids, i_list_mgt, p_pmt->i_program_number,
1215                            p_pmt->i_version, i_cad_program_size,
1216                            p_pmt->p_first_descriptor, i_cmd );
1217
1218     if ( i_cad_program_size )
1219         *pi_capmt_size = 7 + i_cad_program_size;
1220     else
1221         *pi_capmt_size = 6;
1222
1223     for( p_es = p_pmt->p_first_es; p_es != NULL; p_es = p_es->p_next )
1224     {
1225         i_cad_size = GetCADSize( p_ids, p_es->p_first_descriptor );
1226
1227         if ( i_cad_size || i_cad_program_size )
1228         {
1229             p_capmt = CAPMTES( p_ids, p_capmt, *pi_capmt_size, p_es->i_type,
1230                                p_es->i_pid, i_cad_size,
1231                                p_es->p_first_descriptor, i_cmd );
1232             if ( i_cad_size )
1233                 *pi_capmt_size += 6 + i_cad_size;
1234             else
1235                 *pi_capmt_size += 5;
1236         }
1237     }
1238
1239     return p_capmt;
1240 }
1241
1242 /*****************************************************************************
1243  * CAPMTFirst
1244  *****************************************************************************/
1245 static void CAPMTFirst( cam_t * p_cam, int i_session_id,
1246                         dvbpsi_pmt_t *p_pmt )
1247 {
1248     uint8_t *p_capmt;
1249     int i_capmt_size;
1250
1251     msg_Dbg( p_cam->obj, "adding first CAPMT for SID %d on session %d",
1252              p_pmt->i_program_number, i_session_id );
1253
1254     p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1255                           0x3 /* only */, 0x1 /* ok_descrambling */,
1256                           &i_capmt_size );
1257
1258     if( i_capmt_size )
1259     {
1260         APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1261         free( p_capmt );
1262     }
1263 }
1264
1265 /*****************************************************************************
1266  * CAPMTAdd
1267  *****************************************************************************/
1268 static void CAPMTAdd( cam_t * p_cam, int i_session_id,
1269                       dvbpsi_pmt_t *p_pmt )
1270 {
1271     uint8_t *p_capmt;
1272     int i_capmt_size;
1273
1274     if( p_cam->i_selected_programs >= CAM_PROG_MAX )
1275     {
1276         msg_Warn( p_cam->obj, "Not adding CAPMT for SID %d, too many programs",
1277                   p_pmt->i_program_number );
1278         return;
1279     }
1280     p_cam->i_selected_programs++;
1281     if( p_cam->i_selected_programs == 1 )
1282     {
1283         CAPMTFirst( p_cam, i_session_id, p_pmt );
1284         return;
1285     }
1286  
1287 #ifdef CAPMT_WAIT
1288     msleep( CAPMT_WAIT * 1000 );
1289 #endif
1290  
1291     msg_Dbg( p_cam->obj, "adding CAPMT for SID %d on session %d",
1292              p_pmt->i_program_number, i_session_id );
1293
1294     p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1295                           0x4 /* add */, 0x1 /* ok_descrambling */,
1296                           &i_capmt_size );
1297
1298     if( i_capmt_size )
1299     {
1300         APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1301         free( p_capmt );
1302     }
1303 }
1304
1305 /*****************************************************************************
1306  * CAPMTUpdate
1307  *****************************************************************************/
1308 static void CAPMTUpdate( cam_t * p_cam, int i_session_id,
1309                          dvbpsi_pmt_t *p_pmt )
1310 {
1311     uint8_t *p_capmt;
1312     int i_capmt_size;
1313
1314     msg_Dbg( p_cam->obj, "updating CAPMT for SID %d on session %d",
1315              p_pmt->i_program_number, i_session_id );
1316
1317     p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1318                           0x5 /* update */, 0x1 /* ok_descrambling */,
1319                           &i_capmt_size );
1320
1321     if( i_capmt_size )
1322     {
1323         APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1324         free( p_capmt );
1325     }
1326 }
1327
1328 /*****************************************************************************
1329  * CAPMTDelete
1330  *****************************************************************************/
1331 static void CAPMTDelete( cam_t * p_cam, int i_session_id,
1332                          dvbpsi_pmt_t *p_pmt )
1333 {
1334     uint8_t *p_capmt;
1335     int i_capmt_size;
1336
1337     p_cam->i_selected_programs--;
1338     msg_Dbg( p_cam->obj, "deleting CAPMT for SID %d on session %d",
1339              p_pmt->i_program_number, i_session_id );
1340
1341     p_capmt = CAPMTBuild( p_cam, i_session_id, p_pmt,
1342                           0x5 /* update */, 0x4 /* not selected */,
1343                           &i_capmt_size );
1344
1345     if( i_capmt_size )
1346     {
1347         APDUSend( p_cam, i_session_id, AOT_CA_PMT, p_capmt, i_capmt_size );
1348         free( p_capmt );
1349     }
1350 }
1351
1352 /*****************************************************************************
1353  * ConditionalAccessHandle
1354  *****************************************************************************/
1355 static void ConditionalAccessHandle( cam_t * p_cam, int i_session_id,
1356                                      uint8_t *p_apdu, int i_size )
1357 {
1358     system_ids_t *p_ids =
1359         (system_ids_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1360     int i_tag = APDUGetTag( p_apdu, i_size );
1361
1362     switch ( i_tag )
1363     {
1364     case AOT_CA_INFO:
1365     {
1366         int i;
1367         int l = 0;
1368         uint8_t *d = APDUGetLength( p_apdu, &l );
1369         msg_Dbg( p_cam->obj, "CA system IDs supported by the application :" );
1370
1371         for ( i = 0; i < l / 2; i++ )
1372         {
1373             p_ids->pi_system_ids[i] = ((uint16_t)d[0] << 8) | d[1];
1374             d += 2;
1375             msg_Dbg( p_cam->obj, "- 0x%x", p_ids->pi_system_ids[i] );
1376         }
1377         p_ids->pi_system_ids[i] = 0;
1378
1379         for ( i = 0; i < MAX_PROGRAMS; i++ )
1380         {
1381             if ( p_cam->pp_selected_programs[i] != NULL )
1382             {
1383                 CAPMTAdd( p_cam, i_session_id,
1384                           p_cam->pp_selected_programs[i] );
1385             }
1386         }
1387         break;
1388     }
1389
1390     default:
1391         msg_Err( p_cam->obj,
1392                  "unexpected tag in ConditionalAccessHandle (0x%x)",
1393                  i_tag );
1394     }
1395 }
1396
1397 /*****************************************************************************
1398  * ConditionalAccessClose
1399  *****************************************************************************/
1400 static void ConditionalAccessClose( cam_t * p_cam, int i_session_id )
1401 {
1402     msg_Dbg( p_cam->obj, "closing ConditionalAccess session (%d)",
1403              i_session_id );
1404     free( p_cam->p_sessions[i_session_id - 1].p_sys );
1405 }
1406
1407 /*****************************************************************************
1408  * ConditionalAccessOpen
1409  *****************************************************************************/
1410 static void ConditionalAccessOpen( cam_t * p_cam, unsigned i_session_id )
1411 {
1412     msg_Dbg( p_cam->obj, "opening ConditionalAccess session (%u)",
1413              i_session_id );
1414     p_cam->p_sessions[i_session_id - 1].pf_handle = ConditionalAccessHandle;
1415     p_cam->p_sessions[i_session_id - 1].pf_close = ConditionalAccessClose;
1416     p_cam->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(system_ids_t) );
1417
1418     APDUSend( p_cam, i_session_id, AOT_CA_INFO_ENQ, NULL, 0 );
1419 }
1420
1421 /*
1422  * Date Time
1423  */
1424
1425 typedef struct
1426 {
1427     int i_interval;
1428     mtime_t i_last;
1429 } date_time_t;
1430
1431 /*****************************************************************************
1432  * DateTimeSend
1433  *****************************************************************************/
1434 static void DateTimeSend( cam_t * p_cam, int i_session_id )
1435 {
1436     date_time_t *p_date =
1437         (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1438
1439     time_t t = time(NULL);
1440     struct tm tm_gmt;
1441     struct tm tm_loc;
1442
1443     if ( gmtime_r(&t, &tm_gmt) && localtime_r(&t, &tm_loc) )
1444     {
1445         int Y = tm_gmt.tm_year;
1446         int M = tm_gmt.tm_mon + 1;
1447         int D = tm_gmt.tm_mday;
1448         int L = (M == 1 || M == 2) ? 1 : 0;
1449         int MJD = 14956 + D + (int)((Y - L) * 365.25)
1450                     + (int)((M + 1 + L * 12) * 30.6001);
1451         uint8_t p_response[7];
1452
1453 #define DEC2BCD(d) (((d / 10) << 4) + (d % 10))
1454
1455         SetWBE( &p_response[0], MJD );
1456         p_response[2] = DEC2BCD(tm_gmt.tm_hour);
1457         p_response[3] = DEC2BCD(tm_gmt.tm_min);
1458         p_response[4] = DEC2BCD(tm_gmt.tm_sec);
1459         SetWBE( &p_response[5], tm_loc.tm_gmtoff / 60 );
1460
1461         APDUSend( p_cam, i_session_id, AOT_DATE_TIME, p_response, 7 );
1462
1463         p_date->i_last = mdate();
1464     }
1465 }
1466
1467 /*****************************************************************************
1468  * DateTimeHandle
1469  *****************************************************************************/
1470 static void DateTimeHandle( cam_t *p_cam, int i_session_id,
1471                             uint8_t *p_apdu, int i_size )
1472 {
1473     date_time_t *p_date =
1474         (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1475
1476     int i_tag = APDUGetTag( p_apdu, i_size );
1477
1478     switch ( i_tag )
1479     {
1480     case AOT_DATE_TIME_ENQ:
1481     {
1482         int l;
1483         const uint8_t *d = APDUGetLength( p_apdu, &l );
1484
1485         if ( l > 0 )
1486         {
1487             p_date->i_interval = *d;
1488             msg_Dbg( p_cam->obj, "DateTimeHandle : interval set to %d",
1489                      p_date->i_interval );
1490         }
1491         else
1492             p_date->i_interval = 0;
1493
1494         DateTimeSend( p_cam, i_session_id );
1495         break;
1496     }
1497     default:
1498         msg_Err( p_cam->obj, "unexpected tag in DateTimeHandle (0x%x)",
1499                  i_tag );
1500     }
1501 }
1502
1503 /*****************************************************************************
1504  * DateTimeManage
1505  *****************************************************************************/
1506 static void DateTimeManage( cam_t * p_cam, int i_session_id )
1507 {
1508     date_time_t *p_date =
1509         (date_time_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1510
1511     if ( p_date->i_interval
1512           && mdate() > p_date->i_last + (mtime_t)p_date->i_interval * 1000000 )
1513     {
1514         DateTimeSend( p_cam, i_session_id );
1515     }
1516 }
1517
1518 /*****************************************************************************
1519  * DateTimeClose
1520  *****************************************************************************/
1521 static void DateTimeClose( cam_t * p_cam, int i_session_id )
1522 {
1523     msg_Dbg( p_cam->obj, "closing DateTime session (%d)", i_session_id );
1524
1525     free( p_cam->p_sessions[i_session_id - 1].p_sys );
1526 }
1527
1528 /*****************************************************************************
1529  * DateTimeOpen
1530  *****************************************************************************/
1531 static void DateTimeOpen( cam_t * p_cam, unsigned i_session_id )
1532 {
1533     msg_Dbg( p_cam->obj, "opening DateTime session (%u)", i_session_id );
1534
1535     p_cam->p_sessions[i_session_id - 1].pf_handle = DateTimeHandle;
1536     p_cam->p_sessions[i_session_id - 1].pf_manage = DateTimeManage;
1537     p_cam->p_sessions[i_session_id - 1].pf_close = DateTimeClose;
1538     p_cam->p_sessions[i_session_id - 1].p_sys = calloc( 1, sizeof(date_time_t) );
1539
1540     DateTimeSend( p_cam, i_session_id );
1541 }
1542
1543 /*
1544  * MMI
1545  */
1546
1547 /* Display Control Commands */
1548
1549 #define DCC_SET_MMI_MODE                          0x01
1550 #define DCC_DISPLAY_CHARACTER_TABLE_LIST          0x02
1551 #define DCC_INPUT_CHARACTER_TABLE_LIST            0x03
1552 #define DCC_OVERLAY_GRAPHICS_CHARACTERISTICS      0x04
1553 #define DCC_FULL_SCREEN_GRAPHICS_CHARACTERISTICS  0x05
1554
1555 /* MMI Modes */
1556
1557 #define MM_HIGH_LEVEL                      0x01
1558 #define MM_LOW_LEVEL_OVERLAY_GRAPHICS      0x02
1559 #define MM_LOW_LEVEL_FULL_SCREEN_GRAPHICS  0x03
1560
1561 /* Display Reply IDs */
1562
1563 #define DRI_MMI_MODE_ACK                              0x01
1564 #define DRI_LIST_DISPLAY_CHARACTER_TABLES             0x02
1565 #define DRI_LIST_INPUT_CHARACTER_TABLES               0x03
1566 #define DRI_LIST_GRAPHIC_OVERLAY_CHARACTERISTICS      0x04
1567 #define DRI_LIST_FULL_SCREEN_GRAPHIC_CHARACTERISTICS  0x05
1568 #define DRI_UNKNOWN_DISPLAY_CONTROL_CMD               0xF0
1569 #define DRI_UNKNOWN_MMI_MODE                          0xF1
1570 #define DRI_UNKNOWN_CHARACTER_TABLE                   0xF2
1571
1572 /* Enquiry Flags */
1573
1574 #define EF_BLIND  0x01
1575
1576 /* Answer IDs */
1577
1578 #define AI_CANCEL  0x00
1579 #define AI_ANSWER  0x01
1580
1581 static void MMIFree( mmi_t *p_object )
1582 {
1583     switch ( p_object->i_object_type )
1584     {
1585     case EN50221_MMI_ENQ:
1586         FREENULL( p_object->u.enq.psz_text );
1587         break;
1588
1589     case EN50221_MMI_ANSW:
1590         if ( p_object->u.answ.b_ok )
1591         {
1592             FREENULL( p_object->u.answ.psz_answ );
1593         }
1594         break;
1595
1596     case EN50221_MMI_MENU:
1597     case EN50221_MMI_LIST:
1598         FREENULL( p_object->u.menu.psz_title );
1599         FREENULL( p_object->u.menu.psz_subtitle );
1600         FREENULL( p_object->u.menu.psz_bottom );
1601         for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
1602         {
1603             free( p_object->u.menu.ppsz_choices[i] );
1604         }
1605         FREENULL( p_object->u.menu.ppsz_choices );
1606         break;
1607
1608     default:
1609         break;
1610     }
1611 }
1612
1613
1614 /*****************************************************************************
1615  * MMISendObject
1616  *****************************************************************************/
1617 static void MMISendObject( cam_t *p_cam, int i_session_id,
1618                            mmi_t *p_object )
1619 {
1620     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1621     uint8_t *p_data;
1622     int i_size, i_tag;
1623
1624     switch ( p_object->i_object_type )
1625     {
1626     case EN50221_MMI_ANSW:
1627         i_tag = AOT_ANSW;
1628         i_size = 1 + strlen( p_object->u.answ.psz_answ );
1629         p_data = xmalloc( i_size );
1630         p_data[0] = p_object->u.answ.b_ok ? 0x1 : 0x0;
1631         strncpy( (char *)&p_data[1], p_object->u.answ.psz_answ, i_size - 1 );
1632         break;
1633
1634     case EN50221_MMI_MENU_ANSW:
1635         i_tag = AOT_MENU_ANSW;
1636         i_size = 1;
1637         p_data = xmalloc( i_size );
1638         p_data[0] = p_object->u.menu_answ.i_choice;
1639         break;
1640
1641     default:
1642         msg_Err( p_cam->obj, "unknown MMI object %d", p_object->i_object_type );
1643         return;
1644     }
1645
1646     APDUSend( p_cam, i_session_id, i_tag, p_data, i_size );
1647     free( p_data );
1648
1649     p_cam->pb_slot_mmi_expected[i_slot] = true;
1650 }
1651
1652 /*****************************************************************************
1653  * MMISendClose
1654  *****************************************************************************/
1655 static void MMISendClose( cam_t *p_cam, int i_session_id )
1656 {
1657     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1658
1659     APDUSend( p_cam, i_session_id, AOT_CLOSE_MMI, NULL, 0 );
1660
1661     p_cam->pb_slot_mmi_expected[i_slot] = true;
1662 }
1663
1664 /*****************************************************************************
1665  * MMIDisplayReply
1666  *****************************************************************************/
1667 static void MMIDisplayReply( cam_t *p_cam, int i_session_id )
1668 {
1669     uint8_t p_response[2];
1670
1671     p_response[0] = DRI_MMI_MODE_ACK;
1672     p_response[1] = MM_HIGH_LEVEL;
1673
1674     APDUSend( p_cam, i_session_id, AOT_DISPLAY_REPLY, p_response, 2 );
1675
1676     msg_Dbg( p_cam->obj, "sending DisplayReply on session (%d)", i_session_id );
1677 }
1678
1679 /*****************************************************************************
1680  * MMIGetText
1681  *****************************************************************************/
1682 static char *MMIGetText( cam_t *p_cam, uint8_t **pp_apdu, int *pi_size )
1683 {
1684     int i_tag = APDUGetTag( *pp_apdu, *pi_size );
1685     int l;
1686     uint8_t *d;
1687
1688     if ( i_tag != AOT_TEXT_LAST )
1689     {
1690         msg_Err( p_cam->obj, "unexpected text tag: %06x", i_tag );
1691         *pi_size = 0;
1692         return strdup( "" );
1693     }
1694
1695     d = APDUGetLength( *pp_apdu, &l );
1696
1697     *pp_apdu += l + 4;
1698     *pi_size -= l + 4;
1699
1700     return vlc_from_EIT(d,l);
1701 }
1702
1703 /*****************************************************************************
1704  * MMIHandleEnq
1705  *****************************************************************************/
1706 static void MMIHandleEnq( cam_t *p_cam, int i_session_id,
1707                           uint8_t *p_apdu, int i_size )
1708 {
1709     VLC_UNUSED( i_size );
1710
1711     mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1712     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1713     int l;
1714     uint8_t *d = APDUGetLength( p_apdu, &l );
1715
1716     MMIFree( p_mmi );
1717     p_mmi->i_object_type = EN50221_MMI_ENQ;
1718     p_mmi->u.enq.b_blind = (*d & 0x1) ? true : false;
1719     d += 2; /* skip answer_text_length because it is not mandatory */
1720     l -= 2;
1721     p_mmi->u.enq.psz_text = xmalloc( l + 1 );
1722     strncpy( p_mmi->u.enq.psz_text, (char *)d, l );
1723     p_mmi->u.enq.psz_text[l] = '\0';
1724
1725     msg_Dbg( p_cam->obj, "MMI enq: %s%s", p_mmi->u.enq.psz_text,
1726              p_mmi->u.enq.b_blind ? " (blind)" : "" );
1727     p_cam->pb_slot_mmi_expected[i_slot] = false;
1728     p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1729 }
1730
1731 /*****************************************************************************
1732  * MMIHandleMenu
1733  *****************************************************************************/
1734 static void MMIHandleMenu( cam_t *p_cam, int i_session_id, int i_tag,
1735                            uint8_t *p_apdu, int i_size )
1736 {
1737     VLC_UNUSED(i_size);
1738
1739     mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1740     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1741     int l;
1742     uint8_t *d = APDUGetLength( p_apdu, &l );
1743
1744     MMIFree( p_mmi );
1745     p_mmi->i_object_type = (i_tag == AOT_MENU_LAST) ?
1746                                        EN50221_MMI_MENU : EN50221_MMI_LIST;
1747     p_mmi->u.menu.i_choices = 0;
1748     p_mmi->u.menu.ppsz_choices = NULL;
1749
1750     if ( l > 0 )
1751     {
1752         l--; d++; /* choice_nb */
1753
1754 #define GET_FIELD( x )                                                      \
1755         if ( l > 0 )                                                        \
1756         {                                                                   \
1757             p_mmi->u.menu.psz_##x = MMIGetText( p_cam, &d, &l );            \
1758             msg_Dbg( p_cam->obj, "MMI " STRINGIFY( x ) ": %s",              \
1759                      p_mmi->u.menu.psz_##x );                               \
1760         }
1761
1762         GET_FIELD( title );
1763         GET_FIELD( subtitle );
1764         GET_FIELD( bottom );
1765 #undef GET_FIELD
1766
1767         while ( l > 0 )
1768         {
1769             char *psz_text = MMIGetText( p_cam, &d, &l );
1770             TAB_APPEND( p_mmi->u.menu.i_choices,
1771                         p_mmi->u.menu.ppsz_choices,
1772                         psz_text );
1773             msg_Dbg( p_cam->obj, "MMI choice: %s", psz_text );
1774         }
1775     }
1776     p_cam->pb_slot_mmi_expected[i_slot] = false;
1777     p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1778 }
1779
1780 /*****************************************************************************
1781  * MMIHandle
1782  *****************************************************************************/
1783 static void MMIHandle( cam_t *p_cam, int i_session_id,
1784                        uint8_t *p_apdu, int i_size )
1785 {
1786     int i_tag = APDUGetTag( p_apdu, i_size );
1787
1788     switch ( i_tag )
1789     {
1790     case AOT_DISPLAY_CONTROL:
1791     {
1792         int l;
1793         uint8_t *d = APDUGetLength( p_apdu, &l );
1794
1795         if ( l > 0 )
1796         {
1797             switch ( *d )
1798             {
1799             case DCC_SET_MMI_MODE:
1800                 if ( l == 2 && d[1] == MM_HIGH_LEVEL )
1801                     MMIDisplayReply( p_cam, i_session_id );
1802                 else
1803                     msg_Err( p_cam->obj, "unsupported MMI mode %02x", d[1] );
1804                 break;
1805
1806             default:
1807                 msg_Err( p_cam->obj, "unsupported display control command %02x",
1808                          *d );
1809                 break;
1810             }
1811         }
1812         break;
1813     }
1814
1815     case AOT_ENQ:
1816         MMIHandleEnq( p_cam, i_session_id, p_apdu, i_size );
1817         break;
1818
1819     case AOT_LIST_LAST:
1820     case AOT_MENU_LAST:
1821         MMIHandleMenu( p_cam, i_session_id, i_tag, p_apdu, i_size );
1822         break;
1823
1824     case AOT_CLOSE_MMI:
1825         SessionSendClose( p_cam, i_session_id );
1826         break;
1827
1828     default:
1829         msg_Err( p_cam->obj, "unexpected tag in MMIHandle (0x%x)", i_tag );
1830     }
1831 }
1832
1833 /*****************************************************************************
1834  * MMIClose
1835  *****************************************************************************/
1836 static void MMIClose( cam_t *p_cam, int i_session_id )
1837 {
1838     int i_slot = p_cam->p_sessions[i_session_id - 1].i_slot;
1839     mmi_t *p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1840
1841     MMIFree( p_mmi );
1842     free( p_cam->p_sessions[i_session_id - 1].p_sys );
1843
1844     msg_Dbg( p_cam->obj, "closing MMI session (%d)", i_session_id );
1845     p_cam->pb_slot_mmi_expected[i_slot] = false;
1846     p_cam->pb_slot_mmi_undisplayed[i_slot] = true;
1847 }
1848
1849 /*****************************************************************************
1850  * MMIOpen
1851  *****************************************************************************/
1852 static void MMIOpen( cam_t *p_cam, unsigned i_session_id )
1853 {
1854     mmi_t *p_mmi;
1855
1856     msg_Dbg( p_cam->obj, "opening MMI session (%u)", i_session_id );
1857
1858     p_cam->p_sessions[i_session_id - 1].pf_handle = MMIHandle;
1859     p_cam->p_sessions[i_session_id - 1].pf_close = MMIClose;
1860     p_cam->p_sessions[i_session_id - 1].p_sys = xmalloc(sizeof(mmi_t));
1861     p_mmi = (mmi_t *)p_cam->p_sessions[i_session_id - 1].p_sys;
1862     p_mmi->i_object_type = EN50221_MMI_NONE;
1863 }
1864
1865
1866 /*
1867  * Hardware handling
1868  */
1869
1870 /*****************************************************************************
1871  * InitSlot: Open the transport layer
1872  *****************************************************************************/
1873 #define MAX_TC_RETRIES 20
1874
1875 static int InitSlot( cam_t * p_cam, int i_slot )
1876 {
1877     if ( TPDUSend( p_cam, i_slot, T_CREATE_TC, NULL, 0 ) != VLC_SUCCESS )
1878     {
1879         msg_Err( p_cam->obj, "en50221_Init: couldn't send TPDU on slot %d",
1880                  i_slot );
1881         return VLC_EGENERIC;
1882     }
1883
1884     /* This is out of the spec */
1885     for ( int i = 0; i < MAX_TC_RETRIES; i++ )
1886     {
1887         uint8_t i_tag;
1888         if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) == VLC_SUCCESS
1889               && i_tag == T_CTC_REPLY )
1890         {
1891             p_cam->pb_active_slot[i_slot] = true;
1892             break;
1893         }
1894
1895         if ( TPDUSend( p_cam, i_slot, T_CREATE_TC, NULL, 0 )
1896                 != VLC_SUCCESS )
1897         {
1898             msg_Err( p_cam->obj,
1899                      "en50221_Init: couldn't send TPDU on slot %d",
1900                      i_slot );
1901             continue;
1902         }
1903     }
1904
1905     if ( p_cam->pb_active_slot[i_slot] )
1906     {
1907         p_cam->i_timeout = CLOCK_FREQ / 10;
1908         return VLC_SUCCESS;
1909     }
1910
1911     return VLC_EGENERIC;
1912 }
1913
1914
1915 /*
1916  * External entry points
1917  */
1918
1919 /*****************************************************************************
1920  * en50221_Init : Initialize the CAM for en50221
1921  *****************************************************************************/
1922 cam_t *en50221_Init( vlc_object_t *obj, int fd )
1923 {
1924     ca_caps_t caps;
1925
1926     memset( &caps, 0, sizeof( caps ));
1927     if( ioctl( fd, CA_GET_CAP, &caps ) < 0 )
1928     {
1929         msg_Err( obj, "CAMInit: ioctl() error getting CAM capabilities" );
1930         return NULL;
1931     }
1932
1933     /* Output CA capabilities */
1934     msg_Dbg( obj, "CA interface with %d slot(s)", caps.slot_num );
1935     if( caps.slot_type & CA_CI )
1936         msg_Dbg( obj, " CI high level interface type" );
1937     if( caps.slot_type & CA_CI_LINK )
1938         msg_Dbg( obj, " CI link layer level interface type" );
1939     if( caps.slot_type & CA_CI_PHYS )
1940         msg_Dbg( obj, " CI physical layer level interface type (not supported) " );
1941     if( caps.slot_type & CA_DESCR )
1942         msg_Dbg( obj, " built-in descrambler detected" );
1943     if( caps.slot_type & CA_SC )
1944         msg_Dbg( obj, " simple smart card interface" );
1945
1946     msg_Dbg( obj, "%d available descrambler(s) (keys)", caps.descr_num );
1947     if( caps.descr_type & CA_ECD )
1948         msg_Dbg( obj, " ECD scrambling system supported" );
1949     if( caps.descr_type & CA_NDS )
1950         msg_Dbg( obj, " NDS scrambling system supported" );
1951     if( caps.descr_type & CA_DSS )
1952         msg_Dbg( obj, " DSS scrambling system supported" );
1953
1954     if( caps.slot_num == 0 )
1955     {
1956         msg_Err( obj, "CAM module without slots" );
1957         return NULL;
1958     }
1959
1960     cam_t *p_cam = calloc( 1, sizeof( *p_cam ) );
1961     if( unlikely(p_cam == NULL) )
1962         goto error;
1963
1964     p_cam->obj = obj;
1965     p_cam->fd = fd;
1966
1967     if( caps.slot_type & CA_CI_LINK )
1968     {
1969         p_cam->i_ca_type = CA_CI_LINK;
1970
1971         for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
1972         {
1973             if ( ioctl( p_cam->fd, CA_RESET, 1 << i_slot) != 0 )
1974             {
1975                 msg_Err( p_cam->obj, "en50221_Init: couldn't reset slot %d",
1976                          i_slot );
1977             }
1978         }
1979
1980         p_cam->i_timeout = CLOCK_FREQ / 10;
1981         /* Wait a bit otherwise it doesn't initialize properly... */
1982         msleep( CLOCK_FREQ / 10 );
1983         p_cam->i_next_event = 0;
1984     }
1985     else
1986     if( caps.slot_type & CA_CI )
1987     {
1988         p_cam->i_ca_type = CA_CI;
1989
1990         struct ca_slot_info info;
1991         info.num = 0;
1992         /* We don't reset the CAM in that case because it's done by the
1993          * ASIC. */
1994         if ( ioctl( fd, CA_GET_SLOT_INFO, &info ) < 0 )
1995         {
1996             msg_Err( obj, "cannot get slot info: %m" );
1997             goto error;
1998         }
1999         if( info.flags == 0 )
2000         {
2001             msg_Err( obj, "no CAM inserted" );
2002             goto error;
2003         }
2004
2005         /* Allocate a dummy sessions */
2006         p_cam->p_sessions[ 0 ].i_resource_id = RI_CONDITIONAL_ACCESS_SUPPORT;
2007
2008         /* Get application info to find out which cam we are using and make
2009            sure everything is ready to play */
2010         ca_msg_t ca_msg;
2011         ca_msg.length=3;
2012         ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2013         ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2014         ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2015         memset( &ca_msg.msg[3], 0, 253 );
2016         APDUSend( p_cam, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2017         if ( ioctl( fd, CA_GET_MSG, &ca_msg ) < 0 )
2018         {
2019             msg_Err( obj, "en50221_Init: failed getting message" );
2020             goto error;
2021         }
2022
2023 #if HLCI_WAIT_CAM_READY
2024         while( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2025         {
2026             if( !vlc_object_alive (obj) )
2027                 goto error;
2028             msleep(1);
2029             msg_Dbg( obj, "CAM: please wait" );
2030             APDUSend( p_cam, 1, AOT_APPLICATION_INFO_ENQ, NULL, 0 );
2031             ca_msg.length=3;
2032             ca_msg.msg[0] = ( AOT_APPLICATION_INFO & 0xFF0000 ) >> 16;
2033             ca_msg.msg[1] = ( AOT_APPLICATION_INFO & 0x00FF00 ) >> 8;
2034             ca_msg.msg[2] = ( AOT_APPLICATION_INFO & 0x0000FF ) >> 0;
2035             memset( &ca_msg.msg[3], 0, 253 );
2036             if ( ioctl( fd, CA_GET_MSG, &ca_msg ) < 0 )
2037             {
2038                 msg_Err( obj, "en50221_Init: failed getting message" );
2039                 goto error;
2040             }
2041             msg_Dbg( p_cam->obj, "en50221_Init: Got length: %d, tag: 0x%x", ca_msg.length, APDUGetTag( ca_msg.msg, ca_msg.length ) );
2042         }
2043 #else
2044         if( ca_msg.msg[8] == 0xff && ca_msg.msg[9] == 0xff )
2045         {
2046             msg_Err( obj, "CAM returns garbage as application info!" );
2047             goto error;
2048         }
2049 #endif
2050         msg_Dbg( obj, "found CAM %s using id 0x%x", &ca_msg.msg[12],
2051                  (ca_msg.msg[8]<<8)|ca_msg.msg[9] );
2052     }
2053     else
2054     {
2055         msg_Err( obj, "CAM interface incompatible" );
2056         goto error;
2057     }
2058     return p_cam;
2059
2060 error:
2061     free( p_cam );
2062     return NULL;
2063 }
2064
2065
2066 /*****************************************************************************
2067  * en50221_Poll : Poll the CAM for TPDUs
2068  *****************************************************************************/
2069 void en50221_Poll( cam_t * p_cam )
2070 {
2071     switch( p_cam->i_ca_type )
2072     {
2073     case CA_CI_LINK:
2074         if( mdate() > p_cam->i_next_event )
2075             break;
2076     case CA_CI:
2077         return;
2078     default:
2079         assert( 0 );
2080     }
2081
2082     for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2083     {
2084         uint8_t i_tag;
2085         ca_slot_info_t sinfo;
2086
2087         sinfo.num = i_slot;
2088         if ( ioctl( p_cam->fd, CA_GET_SLOT_INFO, &sinfo ) != 0 )
2089         {
2090             msg_Err( p_cam->obj, "en50221_Poll: couldn't get info on slot %d",
2091                      i_slot );
2092             continue;
2093         }
2094
2095         if ( !(sinfo.flags & CA_CI_MODULE_READY) )
2096         {
2097             if ( p_cam->pb_active_slot[i_slot] )
2098             {
2099                 msg_Dbg( p_cam->obj, "en50221_Poll: slot %d has been removed",
2100                          i_slot );
2101                 p_cam->pb_active_slot[i_slot] = false;
2102                 p_cam->pb_slot_mmi_expected[i_slot] = false;
2103                 p_cam->pb_slot_mmi_undisplayed[i_slot] = false;
2104
2105                 /* Close all sessions for this slot. */
2106                 for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2107                 {
2108                     if ( p_cam->p_sessions[i - 1].i_resource_id
2109                           && p_cam->p_sessions[i - 1].i_slot == i_slot )
2110                     {
2111                         if ( p_cam->p_sessions[i - 1].pf_close != NULL )
2112                         {
2113                             p_cam->p_sessions[i - 1].pf_close( p_cam, i );
2114                         }
2115                         p_cam->p_sessions[i - 1].i_resource_id = 0;
2116                     }
2117                 }
2118             }
2119
2120             continue;
2121         }
2122         else if ( !p_cam->pb_active_slot[i_slot] )
2123         {
2124             InitSlot( p_cam, i_slot );
2125
2126             if ( !p_cam->pb_active_slot[i_slot] )
2127             {
2128                 msg_Dbg( p_cam->obj, "en50221_Poll: resetting slot %d", i_slot );
2129
2130                 if ( ioctl( p_cam->fd, CA_RESET, 1 << i_slot) != 0 )
2131                 {
2132                     msg_Err( p_cam->obj, "en50221_Poll: couldn't reset slot %d",
2133                              i_slot );
2134                 }
2135                 continue;
2136             }
2137
2138             msg_Dbg( p_cam->obj, "en50221_Poll: slot %d is active",
2139                      i_slot );
2140         }
2141
2142         if ( !p_cam->pb_tc_has_data[i_slot] )
2143         {
2144             if ( TPDUSend( p_cam, i_slot, T_DATA_LAST, NULL, 0 ) !=
2145                     VLC_SUCCESS )
2146             {
2147                 msg_Err( p_cam->obj,
2148                          "en50221_Poll: couldn't send TPDU on slot %d",
2149                          i_slot );
2150                 continue;
2151             }
2152             if ( TPDURecv( p_cam, i_slot, &i_tag, NULL, NULL ) !=
2153                     VLC_SUCCESS )
2154             {
2155                 msg_Err( p_cam->obj,
2156                          "en50221_Poll: couldn't recv TPDU on slot %d",
2157                          i_slot );
2158                 continue;
2159             }
2160         }
2161
2162         while ( p_cam->pb_tc_has_data[i_slot] )
2163         {
2164             uint8_t p_tpdu[MAX_TPDU_SIZE];
2165             int i_size, i_session_size;
2166             uint8_t *p_session;
2167
2168             if ( TPDUSend( p_cam, i_slot, T_RCV, NULL, 0 ) != VLC_SUCCESS )
2169             {
2170                 msg_Err( p_cam->obj,
2171                          "en50221_Poll: couldn't send TPDU on slot %d",
2172                          i_slot );
2173                 continue;
2174             }
2175             if ( TPDURecv( p_cam, i_slot, &i_tag, p_tpdu, &i_size ) !=
2176                     VLC_SUCCESS )
2177             {
2178                 msg_Err( p_cam->obj,
2179                          "en50221_Poll: couldn't recv TPDU on slot %d",
2180                          i_slot );
2181                 continue;
2182             }
2183
2184             p_session = GetLength( &p_tpdu[3], &i_session_size );
2185             if ( i_session_size <= 1 )
2186                 continue;
2187
2188             p_session++;
2189             i_session_size--;
2190
2191             if ( i_tag != T_DATA_LAST )
2192             {
2193                 msg_Err( p_cam->obj,
2194                          "en50221_Poll: fragmented TPDU not supported" );
2195                 break;
2196             }
2197
2198             SPDUHandle( p_cam, i_slot, p_session, i_session_size );
2199         }
2200     }
2201
2202     for ( int i_session_id = 1; i_session_id <= MAX_SESSIONS; i_session_id++ )
2203     {
2204         if ( p_cam->p_sessions[i_session_id - 1].i_resource_id
2205               && p_cam->p_sessions[i_session_id - 1].pf_manage )
2206         {
2207             p_cam->p_sessions[i_session_id - 1].pf_manage( p_cam,
2208                                                            i_session_id );
2209         }
2210     }
2211
2212     p_cam->i_next_event = mdate() + p_cam->i_timeout;
2213 }
2214
2215
2216 /*****************************************************************************
2217  * en50221_SetCAPMT :
2218  *****************************************************************************/
2219 int en50221_SetCAPMT( cam_t * p_cam, dvbpsi_pmt_t *p_pmt )
2220 {
2221     bool b_update = false;
2222     bool b_needs_descrambling = CAPMTNeedsDescrambling( p_pmt );
2223
2224     for ( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2225     {
2226         if ( p_cam->pp_selected_programs[i] != NULL
2227               && p_cam->pp_selected_programs[i]->i_program_number
2228                   == p_pmt->i_program_number )
2229         {
2230             b_update = true;
2231
2232             if ( !b_needs_descrambling )
2233             {
2234                 dvbpsi_DeletePMT( p_pmt );
2235                 p_pmt = p_cam->pp_selected_programs[i];
2236                 p_cam->pp_selected_programs[i] = NULL;
2237             }
2238             else if( p_pmt != p_cam->pp_selected_programs[i] )
2239             {
2240                 dvbpsi_DeletePMT( p_cam->pp_selected_programs[i] );
2241                 p_cam->pp_selected_programs[i] = p_pmt;
2242             }
2243
2244             break;
2245         }
2246     }
2247
2248     if ( !b_update && b_needs_descrambling )
2249     {
2250         for ( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2251         {
2252             if ( p_cam->pp_selected_programs[i] == NULL )
2253             {
2254                 p_cam->pp_selected_programs[i] = p_pmt;
2255                 break;
2256             }
2257         }
2258     }
2259
2260     if ( b_update || b_needs_descrambling )
2261     {
2262         for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2263         {
2264             if ( p_cam->p_sessions[i - 1].i_resource_id
2265                     == RI_CONDITIONAL_ACCESS_SUPPORT )
2266             {
2267                 if ( b_update && b_needs_descrambling )
2268                     CAPMTUpdate( p_cam, i, p_pmt );
2269                 else if ( b_update )
2270                     CAPMTDelete( p_cam, i, p_pmt );
2271                 else
2272                     CAPMTAdd( p_cam, i, p_pmt );
2273             }
2274         }
2275     }
2276
2277     if ( !b_needs_descrambling )
2278     {
2279         dvbpsi_DeletePMT( p_pmt );
2280     }
2281
2282     return VLC_SUCCESS;
2283 }
2284
2285 /*****************************************************************************
2286  * en50221_OpenMMI :
2287  *****************************************************************************/
2288 static int en50221_OpenMMI( cam_t * p_cam, unsigned i_slot )
2289 {
2290     if( p_cam->i_ca_type & CA_CI_LINK )
2291     {
2292         for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2293         {
2294             if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2295                   && p_cam->p_sessions[i - 1].i_slot == i_slot )
2296             {
2297                 msg_Dbg( p_cam->obj,
2298                          "MMI menu is already opened on slot %d (session=%u)",
2299                          i_slot, i );
2300                 return VLC_SUCCESS;
2301             }
2302         }
2303
2304         for ( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2305         {
2306             if ( p_cam->p_sessions[i - 1].i_resource_id
2307                     == RI_APPLICATION_INFORMATION
2308                   && p_cam->p_sessions[i - 1].i_slot == i_slot )
2309             {
2310                 ApplicationInformationEnterMenu( p_cam, i );
2311                 return VLC_SUCCESS;
2312             }
2313         }
2314
2315         msg_Err( p_cam->obj, "no application information on slot %d", i_slot );
2316         return VLC_EGENERIC;
2317     }
2318     else
2319     {
2320         msg_Err( p_cam->obj, "MMI menu not supported" );
2321         return VLC_EGENERIC;
2322     }
2323 }
2324
2325 /*****************************************************************************
2326  * en50221_CloseMMI :
2327  *****************************************************************************/
2328 static int en50221_CloseMMI( cam_t * p_cam, unsigned i_slot )
2329 {
2330     if( p_cam->i_ca_type & CA_CI_LINK )
2331     {
2332         for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2333         {
2334             if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2335                   && p_cam->p_sessions[i - 1].i_slot == i_slot )
2336             {
2337                 MMISendClose( p_cam, i );
2338                 return VLC_SUCCESS;
2339             }
2340         }
2341
2342         msg_Warn( p_cam->obj, "closing a non-existing MMI session on slot %d",
2343                   i_slot );
2344         return VLC_EGENERIC;
2345     }
2346     else
2347     {
2348         msg_Err( p_cam->obj, "MMI menu not supported" );
2349         return VLC_EGENERIC;
2350     }
2351 }
2352
2353 /*****************************************************************************
2354  * en50221_GetMMIObject :
2355  *****************************************************************************/
2356 static mmi_t *en50221_GetMMIObject( cam_t * p_cam, unsigned i_slot )
2357 {
2358     if( p_cam->pb_slot_mmi_expected[i_slot] )
2359         return NULL; /* should not happen */
2360
2361     for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2362     {
2363         if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2364               && p_cam->p_sessions[i - 1].i_slot == i_slot )
2365         {
2366             mmi_t *p_mmi =
2367                 (mmi_t *)p_cam->p_sessions[i - 1].p_sys;
2368             if ( p_mmi == NULL )
2369                 return NULL; /* should not happen */
2370             return p_mmi;
2371         }
2372     }
2373
2374     return NULL;
2375 }
2376
2377
2378 /*****************************************************************************
2379  * en50221_SendMMIObject :
2380  *****************************************************************************/
2381 static void en50221_SendMMIObject( cam_t * p_cam, unsigned i_slot,
2382                                    mmi_t *p_object )
2383 {
2384     for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2385     {
2386         if ( p_cam->p_sessions[i - 1].i_resource_id == RI_MMI
2387               && p_cam->p_sessions[i - 1].i_slot == i_slot )
2388         {
2389             MMISendObject( p_cam, i, p_object );
2390             return;
2391         }
2392     }
2393
2394     msg_Err( p_cam->obj, "SendMMIObject when no MMI session is opened !" );
2395 }
2396
2397 #ifdef ENABLE_HTTPD
2398 char *en50221_Status( cam_t *p_cam, char *psz_request )
2399 {
2400     if( psz_request != NULL && *psz_request )
2401     {
2402         /* Check if we have an undisplayed MMI message : in that case we ignore
2403          * the user input to avoid confusing the CAM. */
2404         for ( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2405         {
2406             if ( p_cam->pb_slot_mmi_undisplayed[i_slot] )
2407             {
2408                 psz_request = NULL;
2409                 msg_Dbg( p_cam->obj,
2410                          "ignoring user request because of a new MMI object" );
2411                 break;
2412             }
2413         }
2414     }
2415
2416     if( psz_request != NULL && *psz_request )
2417     {
2418         /* We have a mission to accomplish. */
2419         mmi_t mmi_object;
2420         char psz_value[255];
2421         int i_slot;
2422         bool b_ok = false;
2423
2424         if ( HTTPExtractValue( psz_request, "slot", psz_value,
2425                                    sizeof(psz_value) ) == NULL )
2426         {
2427             return strdup( "invalid request parameter\n" );
2428         }
2429         i_slot = atoi(psz_value);
2430
2431         if ( HTTPExtractValue( psz_request, "open", psz_value,
2432                                    sizeof(psz_value) ) != NULL )
2433         {
2434             en50221_OpenMMI( p_cam, i_slot );
2435             return NULL;
2436         }
2437
2438         if ( HTTPExtractValue( psz_request, "close", psz_value,
2439                                    sizeof(psz_value) ) != NULL )
2440         {
2441             en50221_CloseMMI( p_cam, i_slot );
2442             return NULL;
2443         }
2444
2445         if ( HTTPExtractValue( psz_request, "cancel", psz_value,
2446                                    sizeof(psz_value) ) == NULL )
2447         {
2448             b_ok = true;
2449         }
2450
2451         if ( HTTPExtractValue( psz_request, "type", psz_value,
2452                                    sizeof(psz_value) ) == NULL )
2453         {
2454             return strdup( "invalid request parameter\n" );
2455         }
2456
2457         if ( !strcmp( psz_value, "enq" ) )
2458         {
2459             mmi_object.i_object_type = EN50221_MMI_ANSW;
2460             mmi_object.u.answ.b_ok = b_ok;
2461             if ( !b_ok )
2462             {
2463                 mmi_object.u.answ.psz_answ = strdup("");
2464             }
2465             else
2466             {
2467                 if ( HTTPExtractValue( psz_request, "answ", psz_value,
2468                                            sizeof(psz_value) ) == NULL )
2469                 {
2470                     return strdup( "invalid request parameter\n" );
2471                 }
2472
2473                 mmi_object.u.answ.psz_answ = strdup(psz_value);
2474             }
2475         }
2476         else
2477         {
2478             mmi_object.i_object_type = EN50221_MMI_MENU_ANSW;
2479             if ( !b_ok )
2480             {
2481                 mmi_object.u.menu_answ.i_choice = 0;
2482             }
2483             else
2484             {
2485                 if ( HTTPExtractValue( psz_request, "choice", psz_value,
2486                                            sizeof(psz_value) ) == NULL )
2487                     mmi_object.u.menu_answ.i_choice = 0;
2488                 else
2489                     mmi_object.u.menu_answ.i_choice = atoi(psz_value);
2490             }
2491         }
2492
2493         en50221_SendMMIObject( p_cam, i_slot, &mmi_object );
2494         return NULL;
2495     }
2496
2497     /* Check that we have all necessary MMI information. */
2498     for( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2499     {
2500         if ( p_cam->pb_slot_mmi_expected[i_slot] )
2501             return NULL;
2502     }
2503
2504     char *buf;
2505     size_t len;
2506     FILE *p = open_memstream( &buf, &len );
2507     if( unlikely(p == NULL) )
2508         return NULL;
2509
2510     ca_caps_t caps;
2511
2512     if( ioctl( p_cam->fd, CA_GET_CAP, &caps ) < 0 )
2513     {
2514         fprintf( p, "ioctl CA_GET_CAP failed (%m)\n" );
2515         goto out;
2516     }
2517
2518     /* Output CA capabilities */
2519     fprintf( p, "CA interface with %d %s, type:\n<table>", caps.slot_num,
2520              caps.slot_num == 1 ? "slot" : "slots" );
2521 #define CHECK_CAPS( x, s )                                                  \
2522     if ( caps.slot_type & (CA_##x) )                                        \
2523         fprintf( p, "<tr><td>%s</td></tr>\n", s )
2524
2525     CHECK_CAPS( CI, "CI high level interface" );
2526     CHECK_CAPS( CI_LINK, "CI link layer level interface" );
2527     CHECK_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
2528     CHECK_CAPS( DESCR, "built-in descrambler" );
2529     CHECK_CAPS( SC, "simple smartcard interface" );
2530 #undef CHECK_CAPS
2531
2532     fprintf( p, "</table>%d available %s\n<table>", caps.descr_num,
2533            caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
2534 #define CHECK_DESC( x )                                                     \
2535     if ( caps.descr_type & (CA_##x) )                                       \
2536         fprintf( p, "<tr><td>%s</td></tr>", STRINGIFY(x) )
2537
2538     CHECK_DESC( ECD );
2539     CHECK_DESC( NDS );
2540     CHECK_DESC( DSS );
2541 #undef CHECK_DESC
2542
2543     fputs( "</table>", p );
2544
2545     for( unsigned i_slot = 0; i_slot < p_cam->i_nb_slots; i_slot++ )
2546     {
2547         ca_slot_info_t sinfo;
2548
2549         p_cam->pb_slot_mmi_undisplayed[i_slot] = false;
2550         fprintf( p, "<p>CA slot #%d: ", i_slot );
2551
2552         sinfo.num = i_slot;
2553         if ( ioctl( p_cam->fd, CA_GET_SLOT_INFO, &sinfo ) < 0 )
2554         {
2555             fprintf( p, "ioctl CA_GET_SLOT_INFO failed (%m)<br>\n" );
2556             continue;
2557         }
2558
2559 #define CHECK_TYPE( x, s )                                                  \
2560         if ( sinfo.type & (CA_##x) )                                        \
2561             fputs( s, p )
2562
2563         CHECK_TYPE( CI, "high level, " );
2564         CHECK_TYPE( CI_LINK, "link layer level, " );
2565         CHECK_TYPE( CI_PHYS, "physical layer level, " );
2566 #undef CHECK_TYPE
2567
2568         if ( sinfo.flags & CA_CI_MODULE_READY )
2569         {
2570             mmi_t *p_object = en50221_GetMMIObject( p_cam, i_slot );
2571
2572             fputs( "module present and ready<p>\n", p );
2573             fputs( "<form action=index.html method=get>\n", p );
2574             fprintf( p, "<input type=hidden name=slot value=\"%d\">\n",
2575                      i_slot );
2576
2577             if ( p_object == NULL )
2578             {
2579                 fputs( "<input type=submit name=open"
2580                        " value=\"Open session\">\n", p );
2581             }
2582             else
2583             {
2584                 switch ( p_object->i_object_type )
2585                 {
2586                 case EN50221_MMI_ENQ:
2587                     fputs( "<input type=hidden name=type value=enq>\n", p );
2588                     fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2589                              p_object->u.enq.psz_text );
2590                     fprintf( p, "<tr><td><input type=%s name=answ>"
2591                              "</td></tr>\n",
2592                              p_object->u.enq.b_blind ? "password" : "text" );
2593                     break;
2594
2595                 case EN50221_MMI_MENU:
2596                     fputs( "<input type=hidden name=type value=menu>\n", p );
2597                     fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2598                              p_object->u.menu.psz_title );
2599                     fprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
2600                              p_object->u.menu.psz_subtitle );
2601                     for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
2602                         fprintf( p, "<input type=radio name=choice"
2603                                  " value=\"%d\">%s<br>\n", i + 1,
2604                                  p_object->u.menu.ppsz_choices[i] );
2605                     fprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
2606                              p_object->u.menu.psz_bottom );
2607                     break;
2608
2609                 case EN50221_MMI_LIST:
2610                     fputs( "<input type=hidden name=type value=menu>\n", p );
2611                     fputs( "<input type=hidden name=choice value=0>\n", p );
2612                     fprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
2613                              p_object->u.menu.psz_title );
2614                     fprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
2615                              p_object->u.menu.psz_subtitle );
2616                     for ( int i = 0; i < p_object->u.menu.i_choices; i++ )
2617                         fprintf( p, "%s<br>\n",
2618                                  p_object->u.menu.ppsz_choices[i] );
2619                     fprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
2620                              p_object->u.menu.psz_bottom );
2621                     break;
2622
2623                 default:
2624                     fputs( "<table><tr><th>Unknown MMI object type</th></tr>\n", p );
2625                 }
2626
2627                 fputs( "</table><p><input type=submit name=ok value=\"OK\">\n", p );
2628                 fputs( "<input type=submit name=cancel value=\"Cancel\">\n", p );
2629                 fputs( "<input type=submit name=close value=\"Close Session\">\n", p );
2630             }
2631             fputs( "</form>\n", p );
2632         }
2633         else if ( sinfo.flags & CA_CI_MODULE_PRESENT )
2634             fputs( "module present, not ready<br>\n", p );
2635         else
2636             fputs( "module not present<br>\n", p );
2637     }
2638 out:
2639     fclose( p );
2640     return buf;
2641 }
2642 #endif
2643
2644
2645 /*****************************************************************************
2646  * en50221_End :
2647  *****************************************************************************/
2648 void en50221_End( cam_t * p_cam )
2649 {
2650     for( unsigned i = 0; i < MAX_PROGRAMS; i++ )
2651     {
2652         if( p_cam->pp_selected_programs[i] != NULL )
2653         {
2654             dvbpsi_DeletePMT( p_cam->pp_selected_programs[i] );
2655         }
2656     }
2657
2658     for( unsigned i = 1; i <= MAX_SESSIONS; i++ )
2659     {
2660         if( p_cam->p_sessions[i - 1].i_resource_id
2661               && p_cam->p_sessions[i - 1].pf_close != NULL )
2662         {
2663             p_cam->p_sessions[i - 1].pf_close( p_cam, i );
2664         }
2665     }
2666
2667     close( p_cam->fd );
2668     free( p_cam );
2669 }