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