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