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