b84033eedbad9fe95e7707137cd44e75e0da5bd7
[vlc.git] / modules / access / dvb / linux_dvb.c
1 /*****************************************************************************
2  * linux_dvb.c : functions to control a DVB card under Linux with v4l2
3  *****************************************************************************
4  * Copyright (C) 1998-2010 the VideoLAN team
5  *
6  * Authors: Damien Lucas <nitrox@via.ecp.fr>
7  *          Johan Bilien <jobi@via.ecp.fr>
8  *          Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
9  *          Christopher Ross <chris@tebibyte.org>
10  *          Christophe Massiot <massiot@via.ecp.fr>
11  *          David Kaplan <david@of1.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_access.h>
34 #include <vlc_fs.h>
35
36 #include <errno.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <poll.h>
42
43 /* DVB Card Drivers */
44 #include <linux/dvb/version.h>
45 #include <linux/dvb/dmx.h>
46 #include <linux/dvb/frontend.h>
47 #include <linux/dvb/ca.h>
48
49 /* Include dvbpsi headers */
50 # include <dvbpsi/dvbpsi.h>
51 # include <dvbpsi/descriptor.h>
52 # include <dvbpsi/pat.h>
53 # include <dvbpsi/pmt.h>
54 # include <dvbpsi/dr.h>
55 # include <dvbpsi/psi.h>
56 # include <dvbpsi/demux.h>
57 # include <dvbpsi/sdt.h>
58
59 #ifdef ENABLE_HTTPD
60 #   include <vlc_httpd.h>
61 #endif
62
63 #include "dvb.h"
64
65 #define DMX      "/dev/dvb/adapter%d/demux%d"
66 #define FRONTEND "/dev/dvb/adapter%d/frontend%d"
67 #define DVR      "/dev/dvb/adapter%d/dvr%d"
68 #define CA       "/dev/dvb/adapter%d/ca%d"
69
70 /*
71  * Frontends
72  */
73 struct frontend_t
74 {
75     fe_status_t i_last_status;
76     struct dvb_frontend_info info;
77 };
78
79 #define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */
80
81 /* Local prototypes */
82 static int FrontendInfo( access_t * );
83 static int FrontendSetQPSK( access_t * );
84 static int FrontendSetQAM( access_t * );
85 static int FrontendSetOFDM( access_t * );
86 static int FrontendSetATSC( access_t * );
87
88 /*****************************************************************************
89  * FrontendOpen : Determine frontend device information and capabilities
90  *****************************************************************************/
91 int FrontendOpen( access_t *p_access )
92 {
93     access_sys_t *p_sys = p_access->p_sys;
94     frontend_t * p_frontend;
95     unsigned int i_adapter, i_device;
96     bool b_probe;
97     char frontend[128];
98
99     i_adapter = var_GetInteger( p_access, "dvb-adapter" );
100     i_device = var_GetInteger( p_access, "dvb-device" );
101     b_probe = var_GetBool( p_access, "dvb-probe" );
102
103     if( snprintf( frontend, sizeof(frontend), FRONTEND, i_adapter, i_device ) >= (int)sizeof(frontend) )
104     {
105         msg_Err( p_access, "snprintf() truncated string for FRONTEND" );
106         frontend[sizeof(frontend) - 1] = '\0';
107     }
108
109     p_sys->p_frontend = p_frontend = malloc( sizeof(frontend_t) );
110     if( !p_frontend )
111         return VLC_ENOMEM;
112
113     msg_Dbg( p_access, "Opening device %s", frontend );
114     if( (p_sys->i_frontend_handle = vlc_open(frontend, O_RDWR | O_NONBLOCK)) < 0 )
115     {
116         msg_Err( p_access, "FrontEndOpen: opening device failed (%m)" );
117         free( p_frontend );
118         return VLC_EGENERIC;
119     }
120
121     if( b_probe )
122     {
123         const char * psz_expected = NULL;
124         const char * psz_real;
125
126         if( FrontendInfo( p_access ) < 0 )
127         {
128             close( p_sys->i_frontend_handle );
129             free( p_frontend );
130             return VLC_EGENERIC;
131         }
132
133         switch( p_frontend->info.type )
134         {
135         case FE_OFDM:
136             psz_real = "DVB-T";
137             break;
138         case FE_QAM:
139             psz_real = "DVB-C";
140             break;
141         case FE_QPSK:
142             psz_real = "DVB-S";
143             break;
144         case FE_ATSC:
145             psz_real = "ATSC";
146             break;
147         default:
148             psz_real = "unknown";
149         }
150
151         /* Sanity checks */
152         if( (!strncmp( p_access->psz_access, "qpsk", 4 ) ||
153              !strncmp( p_access->psz_access, "dvb-s", 5 ) ||
154              !strncmp( p_access->psz_access, "satellite", 9 ) ) &&
155              (p_frontend->info.type != FE_QPSK) )
156         {
157             psz_expected = "DVB-S";
158         }
159         if( (!strncmp( p_access->psz_access, "cable", 5 ) ||
160              !strncmp( p_access->psz_access, "dvb-c", 5 ) ) &&
161              (p_frontend->info.type != FE_QAM) )
162         {
163             psz_expected = "DVB-C";
164         }
165         if( (!strncmp( p_access->psz_access, "terrestrial", 11 ) ||
166              !strncmp( p_access->psz_access, "dvb-t", 5 ) ) &&
167              (p_frontend->info.type != FE_OFDM) )
168         {
169             psz_expected = "DVB-T";
170         }
171
172         if( (!strncmp( p_access->psz_access, "usdigital", 9 ) ||
173              !strncmp( p_access->psz_access, "atsc", 4 ) ) &&
174              (p_frontend->info.type != FE_ATSC) )
175         {
176             psz_expected = "ATSC";
177         }
178
179         if( psz_expected != NULL )
180         {
181             msg_Err( p_access, "requested type %s not supported by %s tuner",
182                      psz_expected, psz_real );
183             close( p_sys->i_frontend_handle );
184             free( p_frontend );
185             return VLC_EGENERIC;
186         }
187     }
188     else /* no frontend probing is done so use default border values. */
189     {
190         msg_Dbg( p_access, "using default values for frontend info" );
191
192         msg_Dbg( p_access, "method of access is %s", p_access->psz_access );
193         p_frontend->info.type = FE_QPSK;
194         if( !strncmp( p_access->psz_access, "qpsk", 4 ) ||
195             !strncmp( p_access->psz_access, "dvb-s", 5 ) )
196             p_frontend->info.type = FE_QPSK;
197         else if( !strncmp( p_access->psz_access, "cable", 5 ) ||
198                  !strncmp( p_access->psz_access, "dvb-c", 5 ) )
199             p_frontend->info.type = FE_QAM;
200         else if( !strncmp( p_access->psz_access, "terrestrial", 11 ) ||
201                  !strncmp( p_access->psz_access, "dvb-t", 5 ) )
202             p_frontend->info.type = FE_OFDM;
203         else if( !strncmp( p_access->psz_access, "usdigital", 9 ) ||
204                  !strncmp( p_access->psz_access, "atsc", 4 ) )
205             p_frontend->info.type = FE_ATSC;
206     }
207
208     return VLC_SUCCESS;
209 }
210
211 /*****************************************************************************
212  * FrontendClose : Close the frontend
213  *****************************************************************************/
214 void FrontendClose( access_t *p_access )
215 {
216     access_sys_t *p_sys = p_access->p_sys;
217
218     if( p_sys->p_frontend )
219     {
220         close( p_sys->i_frontend_handle );
221         free( p_sys->p_frontend );
222
223         p_sys->p_frontend = NULL;
224     }
225 }
226
227 /*****************************************************************************
228  * FrontendSet : Tune !
229  *****************************************************************************/
230 int FrontendSet( access_t *p_access )
231 {
232     access_sys_t *p_sys = p_access->p_sys;
233
234     switch( p_sys->p_frontend->info.type )
235     {
236     /* DVB-S */
237     case FE_QPSK:
238         if( FrontendSetQPSK( p_access ) )
239         {
240             msg_Err( p_access, "DVB-S tuning error" );
241             return VLC_EGENERIC;
242         }
243         break;
244
245     /* DVB-C */
246     case FE_QAM:
247         if( FrontendSetQAM( p_access ) )
248         {
249             msg_Err( p_access, "DVB-C tuning error" );
250             return VLC_EGENERIC;
251         }
252         break;
253
254     /* DVB-T */
255     case FE_OFDM:
256         if( FrontendSetOFDM( p_access ) )
257         {
258             msg_Err( p_access, "DVB-T tuning error" );
259             return VLC_EGENERIC;
260         }
261         break;
262
263     /* ATSC */
264     case FE_ATSC:
265         if( FrontendSetATSC( p_access ) )
266         {
267             msg_Err( p_access, "ATSC tuning error" );
268             return VLC_EGENERIC;
269         }
270         break;
271
272     default:
273         msg_Err( p_access, "tuner type %s not supported",
274                  p_sys->p_frontend->info.name );
275         return VLC_EGENERIC;
276     }
277     p_sys->p_frontend->i_last_status = 0;
278     p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
279     return VLC_SUCCESS;
280 }
281
282 /*****************************************************************************
283  * FrontendPoll : Poll for frontend events
284  *****************************************************************************/
285 void FrontendPoll( access_t *p_access )
286 {
287     access_sys_t *p_sys = p_access->p_sys;
288     frontend_t * p_frontend = p_sys->p_frontend;
289     struct dvb_frontend_event event;
290     fe_status_t i_status, i_diff;
291
292     for( ;; )
293     {
294         if( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 )
295         {
296             if( errno != EWOULDBLOCK )
297                 msg_Err( p_access, "frontend event error: %m" );
298             return;
299         }
300
301         i_status = event.status;
302         i_diff = i_status ^ p_frontend->i_last_status;
303         p_frontend->i_last_status = i_status;
304
305         {
306 #define IF_UP( x )                                                          \
307         }                                                                   \
308         if ( i_diff & (x) )                                                 \
309         {                                                                   \
310             if ( i_status & (x) )
311
312             IF_UP( FE_HAS_SIGNAL )
313                 msg_Dbg( p_access, "frontend has acquired signal" );
314             else
315                 msg_Dbg( p_access, "frontend has lost signal" );
316
317             IF_UP( FE_HAS_CARRIER )
318                 msg_Dbg( p_access, "frontend has acquired carrier" );
319             else
320                 msg_Dbg( p_access, "frontend has lost carrier" );
321
322             IF_UP( FE_HAS_VITERBI )
323                 msg_Dbg( p_access, "frontend has acquired stable FEC" );
324             else
325                 msg_Dbg( p_access, "frontend has lost FEC" );
326
327             IF_UP( FE_HAS_SYNC )
328                 msg_Dbg( p_access, "frontend has acquired sync" );
329             else
330                 msg_Dbg( p_access, "frontend has lost sync" );
331
332             IF_UP( FE_HAS_LOCK )
333             {
334                 frontend_statistic_t stat;
335
336                 msg_Dbg( p_access, "frontend has acquired lock" );
337                 p_sys->i_frontend_timeout = 0;
338
339                 /* Read some statistics */
340                 if( !FrontendGetStatistic( p_access, &stat ) )
341                 {
342                     if( stat.i_ber >= 0 )
343                         msg_Dbg( p_access, "- Bit error rate: %d", stat.i_ber );
344                     if( stat.i_signal_strenth >= 0 )
345                         msg_Dbg( p_access, "- Signal strength: %d", stat.i_signal_strenth );
346                     if( stat.i_snr >= 0 )
347                         msg_Dbg( p_access, "- SNR: %d", stat.i_snr );
348                 }
349             }
350             else
351             {
352                 msg_Dbg( p_access, "frontend has lost lock" );
353                 p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
354             }
355
356             IF_UP( FE_REINIT )
357             {
358                 /* The frontend was reinited. */
359                 msg_Warn( p_access, "reiniting frontend");
360                 FrontendSet( p_access );
361             }
362         }
363 #undef IF_UP
364     }
365 }
366
367 int FrontendGetStatistic( access_t *p_access, frontend_statistic_t *p_stat )
368 {
369     access_sys_t *p_sys = p_access->p_sys;
370     frontend_t * p_frontend = p_sys->p_frontend;
371
372     if( (p_frontend->i_last_status & FE_HAS_LOCK) == 0 )
373         return VLC_EGENERIC;
374
375     memset( p_stat, 0, sizeof(*p_stat) );
376     if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &p_stat->i_ber ) < 0 )
377         p_stat->i_ber = -1;
378     if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &p_stat->i_signal_strenth ) < 0 )
379         p_stat->i_signal_strenth = -1;
380     if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &p_stat->i_snr ) < 0 )
381         p_stat->i_snr = -1;
382
383     return VLC_SUCCESS;
384 }
385
386 void FrontendGetStatus( access_t *p_access, frontend_status_t *p_status )
387 {
388     access_sys_t *p_sys = p_access->p_sys;
389     frontend_t * p_frontend = p_sys->p_frontend;
390
391     p_status->b_has_signal = (p_frontend->i_last_status & FE_HAS_SIGNAL) != 0;
392     p_status->b_has_carrier = (p_frontend->i_last_status & FE_HAS_CARRIER) != 0;
393     p_status->b_has_lock = (p_frontend->i_last_status & FE_HAS_LOCK) != 0;
394 }
395
396 static int ScanParametersDvbS( access_t *p_access, scan_parameter_t *p_scan )
397 {
398     const frontend_t *p_frontend = p_access->p_sys->p_frontend;
399
400     memset( p_scan, 0, sizeof(*p_scan) );
401     p_scan->type = SCAN_DVB_S;
402
403     p_scan->frequency.i_min = p_frontend->info.frequency_min;
404     p_scan->frequency.i_max = p_frontend->info.frequency_max;
405
406     return VLC_SUCCESS;
407 }
408
409 static int ScanParametersDvbC( access_t *p_access, scan_parameter_t *p_scan )
410 {
411     const frontend_t *p_frontend = p_access->p_sys->p_frontend;
412
413
414     memset( p_scan, 0, sizeof(*p_scan) );
415     p_scan->type = SCAN_DVB_C;
416     p_scan->b_exhaustive = false;
417
418     /* */
419     p_scan->frequency.i_min = p_frontend->info.frequency_min;
420     p_scan->frequency.i_max = p_frontend->info.frequency_max;
421     p_scan->frequency.i_step = p_frontend->info.frequency_stepsize
422         ? p_frontend->info.frequency_stepsize : 166667;
423     p_scan->frequency.i_count = (p_scan->frequency.i_max-p_scan->frequency.i_min)/p_scan->frequency.i_step;
424
425     /* */
426     p_scan->bandwidth.i_min  = 6;
427     p_scan->bandwidth.i_max  = 8;
428     p_scan->bandwidth.i_step = 1;
429     p_scan->bandwidth.i_count = 3;
430     return VLC_SUCCESS;
431 }
432
433 static int ScanParametersDvbT( access_t *p_access, scan_parameter_t *p_scan )
434 {
435     const frontend_t *p_frontend = p_access->p_sys->p_frontend;
436
437
438     memset( p_scan, 0, sizeof(*p_scan) );
439     p_scan->type = SCAN_DVB_T;
440     p_scan->b_exhaustive = false;
441
442     /* */
443     p_scan->frequency.i_min = p_frontend->info.frequency_min;
444     p_scan->frequency.i_max = p_frontend->info.frequency_max;
445     p_scan->frequency.i_step = p_frontend->info.frequency_stepsize
446         ? p_frontend->info.frequency_stepsize : 166667;
447     p_scan->frequency.i_count = (p_scan->frequency.i_max-p_scan->frequency.i_min)/p_scan->frequency.i_step;
448
449     /* */
450     p_scan->bandwidth.i_min  = 6;
451     p_scan->bandwidth.i_max  = 8;
452     p_scan->bandwidth.i_step = 1;
453     p_scan->bandwidth.i_count = 3;
454     return VLC_SUCCESS;
455 }
456
457 int  FrontendGetScanParameter( access_t *p_access, scan_parameter_t *p_scan )
458 {
459     access_sys_t *p_sys = p_access->p_sys;
460     const frontend_t *p_frontend = p_sys->p_frontend;
461
462     if( p_frontend->info.type == FE_OFDM )              /* DVB-T */
463         return ScanParametersDvbT( p_access, p_scan );
464     else if( p_frontend->info.type == FE_QAM )          /* DVB-C */
465         return ScanParametersDvbC( p_access, p_scan );
466     else if( p_frontend->info.type == FE_QPSK )
467         return ScanParametersDvbS( p_access, p_scan );  /* DVB-S */
468
469     msg_Err( p_access, "frontend scanning not supported" );
470     return VLC_EGENERIC;
471 }
472
473 #ifdef ENABLE_HTTPD
474 /*****************************************************************************
475  * FrontendStatus : Read frontend status
476  *****************************************************************************/
477 void FrontendStatus( access_t *p_access )
478 {
479     access_sys_t *p_sys = p_access->p_sys;
480     frontend_t *p_frontend = p_sys->p_frontend;
481     char *p = p_sys->psz_frontend_info = malloc( 10000 );
482     fe_status_t i_status;
483
484     /* Determine type of frontend */
485     if( ioctl( p_sys->i_frontend_handle, FE_GET_INFO,  &p_frontend->info ) < 0 )
486     {
487         char buf[1000];
488         strerror_r( errno, buf, sizeof( buf ) );
489         p += sprintf( p, "ioctl FE_GET_INFO failed %s\n", buf );
490         goto out;
491     }
492
493     /* Print out frontend capabilities. */
494     p += sprintf( p, "<table border=1><tr><th>name</th><td>%s</td></tr>\n",
495                   p_frontend->info.name );
496     switch( p_frontend->info.type )
497     {
498         case FE_QPSK:
499             p += sprintf( p, "<tr><th>type</th><td>QPSK (DVB-S)</td></tr>\n" );
500             break;
501         case FE_QAM:
502             p += sprintf( p, "<tr><th>type</th><td>QAM (DVB-C)</td></tr>\n" );
503             break;
504         case FE_OFDM:
505             p += sprintf( p, "<tr><th>type</th><td>OFDM (DVB-T)</td></tr>\n" );
506             break;
507 #if 0 /* DVB_API_VERSION == 3 */
508         case FE_MEMORY:
509             p += sprintf( p, "<tr><th>type</th><td>MEMORY</td></tr>\n" );
510             break;
511         case FE_NET:
512             p += sprintf( p, "<tr><th>type</th><td>NETWORK</td></tr>\n" );
513             break;
514 #endif
515         default:
516             p += sprintf( p, "<tr><th>type</th><td>UNKNOWN (%d)</td></tr>\n",
517                           p_frontend->info.type );
518             goto out;
519     }
520 #define CHECK_INFO( x )                                                     \
521     p += sprintf( p,                                                        \
522                   "<tr><th>" STRINGIFY(x) "</th><td>%u</td></tr>\n",        \
523                   p_frontend->info.x );
524
525     CHECK_INFO( frequency_min );
526     CHECK_INFO( frequency_max );
527     CHECK_INFO( frequency_stepsize );
528     CHECK_INFO( frequency_tolerance );
529     CHECK_INFO( symbol_rate_min );
530     CHECK_INFO( symbol_rate_max );
531     CHECK_INFO( symbol_rate_tolerance );
532     CHECK_INFO( notifier_delay );
533 #undef CHECK_INFO
534
535     p += sprintf( p, "</table><p>Frontend capability list:\n<table border=1>" );
536
537 #define CHECK_CAPS( x )                                                     \
538     if ( p_frontend->info.caps & (FE_##x) )                                 \
539         p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
540
541     CHECK_CAPS( IS_STUPID );
542     CHECK_CAPS( CAN_INVERSION_AUTO );
543     CHECK_CAPS( CAN_FEC_1_2 );
544     CHECK_CAPS( CAN_FEC_2_3 );
545     CHECK_CAPS( CAN_FEC_3_4 );
546     CHECK_CAPS( CAN_FEC_4_5 );
547     CHECK_CAPS( CAN_FEC_5_6 );
548     CHECK_CAPS( CAN_FEC_6_7 );
549     CHECK_CAPS( CAN_FEC_7_8 );
550     CHECK_CAPS( CAN_FEC_8_9 );
551     CHECK_CAPS( CAN_FEC_AUTO );
552     CHECK_CAPS( CAN_QPSK );
553     CHECK_CAPS( CAN_QAM_16 );
554     CHECK_CAPS( CAN_QAM_32 );
555     CHECK_CAPS( CAN_QAM_64 );
556     CHECK_CAPS( CAN_QAM_128 );
557     CHECK_CAPS( CAN_QAM_256 );
558     CHECK_CAPS( CAN_QAM_AUTO );
559     CHECK_CAPS( CAN_TRANSMISSION_MODE_AUTO );
560     CHECK_CAPS( CAN_BANDWIDTH_AUTO );
561     CHECK_CAPS( CAN_GUARD_INTERVAL_AUTO );
562     CHECK_CAPS( CAN_HIERARCHY_AUTO );
563     CHECK_CAPS( CAN_MUTE_TS );
564     CHECK_CAPS( CAN_RECOVER );
565 #if 0 /* Disabled because of older distributions */
566     CHECK_CAPS( CAN_CLEAN_SETUP );
567 #endif
568 #undef CHECK_CAPS
569
570     p += sprintf( p, "</table><p>Current frontend status:\n<table border=1>" );
571
572     if( ioctl( p_sys->i_frontend_handle, FE_READ_STATUS, &i_status ) < 0 )
573     {
574         char buf[1000];
575         strerror_r( errno, buf, sizeof( buf ) );
576         p += sprintf( p, "</table>ioctl FE_READ_STATUS failed %s\n", buf );
577         goto out;
578     }
579
580 #define CHECK_STATUS( x )                                                   \
581     if ( i_status & (FE_##x) )                                              \
582         p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
583
584     CHECK_STATUS( HAS_SIGNAL );
585     CHECK_STATUS( HAS_CARRIER );
586     CHECK_STATUS( HAS_VITERBI );
587     CHECK_STATUS( HAS_SYNC );
588     CHECK_STATUS( HAS_LOCK );
589     CHECK_STATUS( REINIT );
590     if( i_status == 0 )
591         p += sprintf( p, "<tr><td>Tuning failed</td></tr>\n" );
592 #undef CHECK_STATUS
593
594     if ( i_status & FE_HAS_LOCK )
595     {
596         int32_t i_value;
597         p += sprintf( p, "</table><p>Signal status:\n<table border=1>" );
598         if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 )
599             p += sprintf( p, "<tr><th>Bit error rate</th><td>%d</td></tr>\n",
600                           i_value );
601         if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH,
602                    &i_value ) >= 0 )
603             p += sprintf( p, "<tr><th>Signal strength</th><td>%d</td></tr>\n",
604                           i_value );
605         if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 )
606             p += sprintf( p, "<tr><th>SNR</th><td>%d</td></tr>\n",
607                           i_value );
608     }
609     p += sprintf( p, "</table>" );
610
611 out:
612     vlc_mutex_lock( &p_sys->httpd_mutex );
613     p_sys->b_request_frontend_info = false;
614     vlc_cond_signal( &p_sys->httpd_cond );
615     vlc_mutex_unlock( &p_sys->httpd_mutex );
616 }
617 #endif
618
619 /*****************************************************************************
620  * FrontendInfo : Return information about given frontend
621  *****************************************************************************/
622 static int FrontendInfo( access_t *p_access )
623 {
624     access_sys_t *p_sys = p_access->p_sys;
625     frontend_t *p_frontend = p_sys->p_frontend;
626
627     /* Determine type of frontend */
628     if( ioctl( p_sys->i_frontend_handle, FE_GET_INFO, &p_frontend->info ) < 0 )
629     {
630         msg_Err( p_access, "frontend info request error: %m" );
631         return VLC_EGENERIC;
632     }
633
634     /* Print out frontend capabilities. */
635     msg_Dbg(p_access, "Frontend Info:" );
636     msg_Dbg(p_access, "  name = %s", p_frontend->info.name );
637     switch( p_frontend->info.type )
638     {
639         case FE_QPSK:
640             msg_Dbg( p_access, "  type = QPSK (DVB-S)" );
641             break;
642         case FE_QAM:
643             msg_Dbg( p_access, "  type = QAM (DVB-C)" );
644             break;
645         case FE_OFDM:
646             msg_Dbg( p_access, "  type = OFDM (DVB-T)" );
647             break;
648         case FE_ATSC:
649             msg_Dbg( p_access, "  type = ATSC (USA)" );
650             break;
651 #if 0 /* DVB_API_VERSION == 3 */
652         case FE_MEMORY:
653             msg_Dbg(p_access, "  type = MEMORY" );
654             break;
655         case FE_NET:
656             msg_Dbg(p_access, "  type = NETWORK" );
657             break;
658 #endif
659         default:
660             msg_Err( p_access, "  unknown frontend type (%d)",
661                      p_frontend->info.type );
662             return VLC_EGENERIC;
663     }
664     msg_Dbg(p_access, "  frequency_min = %u (kHz)",
665             p_frontend->info.frequency_min);
666     msg_Dbg(p_access, "  frequency_max = %u (kHz)",
667             p_frontend->info.frequency_max);
668     msg_Dbg(p_access, "  frequency_stepsize = %u",
669             p_frontend->info.frequency_stepsize);
670     msg_Dbg(p_access, "  frequency_tolerance = %u",
671             p_frontend->info.frequency_tolerance);
672     msg_Dbg(p_access, "  symbol_rate_min = %u (kHz)",
673             p_frontend->info.symbol_rate_min);
674     msg_Dbg(p_access, "  symbol_rate_max = %u (kHz)",
675             p_frontend->info.symbol_rate_max);
676     msg_Dbg(p_access, "  symbol_rate_tolerance (ppm) = %u",
677             p_frontend->info.symbol_rate_tolerance);
678     msg_Dbg(p_access, "  notifier_delay (ms) = %u",
679             p_frontend->info.notifier_delay );
680
681     msg_Dbg(p_access, "Frontend Info capability list:");
682     if( p_frontend->info.caps == FE_IS_STUPID)
683         msg_Dbg(p_access, "  no capabilities - frontend is stupid!");
684     if( p_frontend->info.caps & FE_CAN_INVERSION_AUTO)
685         msg_Dbg(p_access, "  inversion auto");
686     if( p_frontend->info.caps & FE_CAN_FEC_1_2)
687         msg_Dbg(p_access, "  forward error correction 1/2");
688     if( p_frontend->info.caps & FE_CAN_FEC_2_3)
689         msg_Dbg(p_access, "  forward error correction 2/3");
690     if( p_frontend->info.caps & FE_CAN_FEC_3_4)
691         msg_Dbg(p_access, "  forward error correction 3/4");
692     if( p_frontend->info.caps & FE_CAN_FEC_4_5)
693         msg_Dbg(p_access, "  forward error correction 4/5");
694     if( p_frontend->info.caps & FE_CAN_FEC_5_6)
695         msg_Dbg(p_access, "  forward error correction 5/6");
696     if( p_frontend->info.caps & FE_CAN_FEC_6_7)
697         msg_Dbg(p_access, "  forward error correction 6/7");
698     if( p_frontend->info.caps & FE_CAN_FEC_7_8)
699         msg_Dbg(p_access, "  forward error correction 7/8");
700     if( p_frontend->info.caps & FE_CAN_FEC_8_9)
701         msg_Dbg(p_access, "  forward error correction 8/9");
702     if( p_frontend->info.caps & FE_CAN_FEC_AUTO)
703         msg_Dbg(p_access, "  forward error correction auto");
704     if( p_frontend->info.caps & FE_CAN_QPSK)
705         msg_Dbg(p_access, "  QPSK modulation");
706     if( p_frontend->info.caps & FE_CAN_QAM_16)
707         msg_Dbg(p_access, "  QAM 16 modulation");
708     if( p_frontend->info.caps & FE_CAN_QAM_32)
709         msg_Dbg(p_access, "  QAM 32 modulation");
710     if( p_frontend->info.caps & FE_CAN_QAM_64)
711         msg_Dbg(p_access, "  QAM 64 modulation");
712     if( p_frontend->info.caps & FE_CAN_QAM_128)
713         msg_Dbg(p_access, "  QAM 128 modulation");
714     if( p_frontend->info.caps & FE_CAN_QAM_256)
715         msg_Dbg(p_access, "  QAM 256 modulation");
716     if( p_frontend->info.caps & FE_CAN_QAM_AUTO)
717         msg_Dbg(p_access, "  QAM auto modulation");
718     if( p_frontend->info.caps & FE_CAN_TRANSMISSION_MODE_AUTO)
719         msg_Dbg(p_access, "  transmission mode auto");
720     if( p_frontend->info.caps & FE_CAN_BANDWIDTH_AUTO)
721         msg_Dbg(p_access, "  bandwidth mode auto");
722     if( p_frontend->info.caps & FE_CAN_GUARD_INTERVAL_AUTO)
723         msg_Dbg(p_access, "  guard interval mode auto");
724     if( p_frontend->info.caps & FE_CAN_HIERARCHY_AUTO)
725         msg_Dbg(p_access, "  hierarchy mode auto");
726     if( p_frontend->info.caps & FE_CAN_8VSB)
727         msg_Dbg(p_access, "  8-level VSB modulation");
728     if( p_frontend->info.caps & FE_CAN_16VSB)
729         msg_Dbg(p_access, "  16-level VSB modulation");
730     if( p_frontend->info.caps & FE_HAS_EXTENDED_CAPS)
731         msg_Dbg(p_access, "  8-level VSB modulation");
732     /* 3 capabilities that don't exist yet HERE */
733 #ifdef FE_CAN_TURBO_FEC
734     if( p_frontend->info.caps & FE_CAN_TURBO_FEC)
735         msg_Dbg(p_access, "  Turbo FEC modulation");
736 #else
737 # warning Please update your Linux kernel headers!
738 #endif
739     if( p_frontend->info.caps & FE_CAN_2G_MODULATION)
740         msg_Dbg(p_access, "  2nd generation modulation (DVB-S2)");
741     /* FE_NEEDS_BENDING is deprecated */
742     if( p_frontend->info.caps & FE_CAN_RECOVER)
743         msg_Dbg(p_access, "  cable unplug recovery");
744     if( p_frontend->info.caps & FE_CAN_MUTE_TS)
745         msg_Dbg(p_access, "  spurious TS muting");
746    msg_Dbg(p_access, "End of capability list");
747
748     return VLC_SUCCESS;
749 }
750
751 /*****************************************************************************
752  * Decoding the DVB parameters (common)
753  *****************************************************************************/
754 static fe_spectral_inversion_t DecodeInversion( access_t *p_access )
755 {
756     int i_val;
757     fe_spectral_inversion_t fe_inversion = 0;
758
759     i_val = var_GetInteger( p_access, "dvb-inversion" );
760     msg_Dbg( p_access, "using inversion=%d", i_val );
761
762     switch( i_val )
763     {
764         case 0: fe_inversion = INVERSION_OFF; break;
765         case 1: fe_inversion = INVERSION_ON; break;
766         case 2: fe_inversion = INVERSION_AUTO; break;
767         default:
768             msg_Dbg( p_access, "dvb has inversion not set, using auto");
769             fe_inversion = INVERSION_AUTO;
770             break;
771     }
772     return fe_inversion;
773 }
774
775 static fe_code_rate_t DecodeFEC( access_t *p_access, const char *varname )
776 {
777     switch( var_GetInteger(p_access, varname) )
778     {
779         case 0:  return FEC_NONE;
780         case 1:  return FEC_1_2;
781         case 2:  return FEC_2_3;
782         case 3:  return FEC_3_4;
783         case 4:  return FEC_4_5;
784         case 5:  return FEC_5_6;
785         case 6:  return FEC_6_7;
786         case 7:  return FEC_7_8;
787         case 8:  return FEC_8_9;
788         case 9:  return FEC_AUTO;
789         default: return FEC_NONE;
790     }
791 }
792
793 static fe_modulation_t DecodeModulation( access_t *p_access,
794                                          fe_modulation_t def )
795 {
796     switch( var_GetInteger( p_access, "dvb-modulation" ) )
797     {
798         case -1:    return QPSK;
799         case 0:     return QAM_AUTO;
800         case 8:     return VSB_8;
801         case 16:    return QAM_16;
802         case 32:    return QAM_32;
803         case 64:    return QAM_64;
804         case 128:   return QAM_128;
805         case 256:   return QAM_256;
806         default:    return def;
807     }
808 }
809
810 /*****************************************************************************
811  * FrontendSetQPSK : controls the FE device
812  *****************************************************************************/
813 static fe_sec_voltage_t DecodeVoltage( access_t *p_access )
814 {
815     switch( var_GetInteger( p_access, "dvb-voltage" ) )
816     {
817         case 0:  return SEC_VOLTAGE_OFF;
818         case 13: return SEC_VOLTAGE_13;
819         case 18: return SEC_VOLTAGE_18;
820         default: return SEC_VOLTAGE_OFF;
821     }
822 }
823
824 static fe_sec_tone_mode_t DecodeTone( access_t *p_access )
825 {
826     switch( var_GetInteger( p_access, "dvb-tone" ) )
827     {
828         case 0:  return SEC_TONE_OFF;
829         case 1:  return SEC_TONE_ON;
830         default: return SEC_TONE_OFF;
831     }
832 }
833
834 struct diseqc_cmd_t
835 {
836     struct dvb_diseqc_master_cmd cmd;
837     uint32_t wait;
838 };
839
840 static int DoDiseqc( access_t *p_access )
841 {
842     access_sys_t *p_sys = p_access->p_sys;
843     int i_val;
844     bool b_val;
845     int i_frequency, i_lnb_slof;
846     fe_sec_voltage_t fe_voltage;
847     fe_sec_tone_mode_t fe_tone;
848
849     i_frequency = var_GetInteger( p_access, "dvb-frequency" );
850     i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" );
851
852     i_val = var_GetInteger( p_access, "dvb-tone" );
853     if( i_val == -1 /* auto */ )
854     {
855         if( i_frequency >= i_lnb_slof )
856             i_val = 1;
857         else
858             i_val = 0;
859         var_SetInteger( p_access, "dvb-tone", i_val );
860     }
861
862     fe_voltage = DecodeVoltage( p_access );
863     fe_tone = DecodeTone( p_access );
864
865     /* Switch off continuous tone. */
866     if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, SEC_TONE_OFF ) < 0 )
867     {
868         msg_Err( p_access, "switching tone %s error: %m", "off" );
869         return VLC_EGENERIC;
870     }
871
872     /* Configure LNB voltage. */
873     if( ioctl( p_sys->i_frontend_handle, FE_SET_VOLTAGE, fe_voltage ) < 0 )
874     {
875         msg_Err( p_access, "voltage error: %m" );
876         return VLC_EGENERIC;
877     }
878
879     b_val = var_GetBool( p_access, "dvb-high-voltage" );
880     if( ioctl( p_sys->i_frontend_handle,
881                FE_ENABLE_HIGH_LNB_VOLTAGE, b_val ) < 0 && b_val )
882     {
883         msg_Err( p_access, "high LNB voltage error: %m" );
884     }
885
886     /* Wait for at least 15 ms. */
887     msleep(15000);
888
889     i_val = var_GetInteger( p_access, "dvb-satno" );
890     if( i_val > 0 && i_val < 5 )
891     {
892         /* digital satellite equipment control,
893          * specification is available from http://www.eutelsat.com/
894          */
895
896         /* 1.x compatible equipment */
897         struct diseqc_cmd_t cmd =  { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };
898
899         /* param: high nibble: reset bits, low nibble set bits,
900          * bits are: option, position, polarization, band
901          */
902         cmd.cmd.msg[3] = 0xf0 /* reset bits */
903                           | (((i_val - 1) * 4) & 0xc)
904                           | (fe_voltage == SEC_VOLTAGE_13 ? 0 : 2)
905                           | (fe_tone == SEC_TONE_ON ? 1 : 0);
906
907         if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_MASTER_CMD,
908                    &cmd.cmd ) )
909         {
910             msg_Err( p_access, "master command sending error: %m" );
911             return VLC_EGENERIC;
912         }
913
914         msleep(15000 + cmd.wait * 1000);
915
916         /* A or B simple diseqc ("diseqc-compatible") */
917         if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_BURST,
918                   ((i_val - 1) % 2) ? SEC_MINI_B : SEC_MINI_A ) )
919         {
920             msg_Err( p_access, "burst sending error: %m" );
921             return VLC_EGENERIC;
922         }
923
924         msleep(15000);
925     }
926
927     if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, fe_tone ) )
928     {
929         msg_Err( p_access, "switching tone %s error: %m",
930                  (fe_tone == SEC_TONE_ON) ? "on" : "off" );
931         return VLC_EGENERIC;
932     }
933
934     msleep(50000);
935     return 0;
936 }
937
938 static int FrontendSetQPSK( access_t *p_access )
939 {
940     access_sys_t *p_sys = p_access->p_sys;
941     struct dvb_frontend_parameters fep;
942     int i_val;
943     int i_frequency, i_lnb_slof = 0, i_lnb_lof1, i_lnb_lof2 = 0;
944
945     /* Prepare the fep structure */
946     i_frequency = var_GetInteger( p_access, "dvb-frequency" );
947
948     i_val = var_GetInteger( p_access, "dvb-lnb-lof1" );
949     if( i_val == 0 )
950     {
951         /* Automatic mode. */
952         if ( i_frequency >= 950000 && i_frequency <= 2150000 )
953         {
954             msg_Dbg( p_access, "frequency %d is in IF-band", i_frequency );
955             i_lnb_lof1 = 0;
956         }
957         else if ( i_frequency >= 2500000 && i_frequency <= 2700000 )
958         {
959             msg_Dbg( p_access, "frequency %d is in S-band", i_frequency );
960             i_lnb_lof1 = 3650000;
961         }
962         else if ( i_frequency >= 3400000 && i_frequency <= 4200000 )
963         {
964             msg_Dbg( p_access, "frequency %d is in C-band (lower)",
965                      i_frequency );
966             i_lnb_lof1 = 5150000;
967         }
968         else if ( i_frequency >= 4500000 && i_frequency <= 4800000 )
969         {
970             msg_Dbg( p_access, "frequency %d is in C-band (higher)",
971                      i_frequency );
972             i_lnb_lof1 = 5950000;
973         }
974         else if ( i_frequency >= 10700000 && i_frequency <= 13250000 )
975         {
976             msg_Dbg( p_access, "frequency %d is in Ku-band",
977                      i_frequency );
978             i_lnb_lof1 = 9750000;
979             i_lnb_lof2 = 10600000;
980             i_lnb_slof = 11700000;
981         }
982         else
983         {
984             msg_Err( p_access, "frequency %d is out of any known band",
985                      i_frequency );
986             msg_Err( p_access, "specify dvb-lnb-lof1 manually for the local "
987                      "oscillator frequency" );
988             return VLC_EGENERIC;
989         }
990         var_SetInteger( p_access, "dvb-lnb-lof1", i_lnb_lof1 );
991         var_SetInteger( p_access, "dvb-lnb-lof2", i_lnb_lof2 );
992         var_SetInteger( p_access, "dvb-lnb-slof", i_lnb_slof );
993     }
994     else
995     {
996         i_lnb_lof1 = i_val;
997         i_lnb_lof2 = var_GetInteger( p_access, "dvb-lnb-lof2" );
998         i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" );
999     }
1000
1001     if( i_lnb_slof && i_frequency >= i_lnb_slof )
1002     {
1003         i_frequency -= i_lnb_lof2;
1004     }
1005     else
1006     {
1007         i_frequency -= i_lnb_lof1;
1008     }
1009     fep.frequency = i_frequency >= 0 ? i_frequency : -i_frequency;
1010
1011     fep.inversion = DecodeInversion( p_access );
1012
1013     fep.u.qpsk.symbol_rate = var_GetInteger( p_access, "dvb-srate" );
1014
1015     fep.u.qpsk.fec_inner = DecodeFEC( p_access, "dvb-fec" );
1016
1017     if( DoDiseqc( p_access ) < 0 )
1018     {
1019         return VLC_EGENERIC;
1020     }
1021
1022     /* Empty the event queue */
1023     for( ; ; )
1024     {
1025         struct dvb_frontend_event event;
1026         if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
1027               && errno == EWOULDBLOCK )
1028             break;
1029     }
1030
1031     /* Now send it all to the frontend device */
1032     if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
1033     {
1034         msg_Err( p_access, "frontend error: %m" );
1035         return VLC_EGENERIC;
1036     }
1037
1038     return VLC_SUCCESS;
1039 }
1040
1041 /*****************************************************************************
1042  * FrontendSetQAM : controls the FE device
1043  *****************************************************************************/
1044 static int FrontendSetQAM( access_t *p_access )
1045 {
1046     access_sys_t *p_sys = p_access->p_sys;
1047     frontend_t *p_frontend = p_sys->p_frontend;
1048     struct dvb_frontend_parameters fep;
1049     unsigned int i_val;
1050
1051     /* Prepare the fep structure */
1052
1053     fep.frequency = var_GetInteger( p_access, "dvb-frequency" );
1054
1055     fep.inversion = DecodeInversion( p_access );
1056
1057     /* Default symbol-rate is for dvb-s, and doesn't fit
1058      * for dvb-c, so if it's over the limit of frontend, default to
1059      * somewhat common value
1060      */
1061     i_val = var_GetInteger( p_access, "dvb-srate" );
1062     if( i_val < p_frontend->info.symbol_rate_max &&
1063         i_val > p_frontend->info.symbol_rate_min )
1064         fep.u.qam.symbol_rate = i_val;
1065     else
1066         fep.u.qam.symbol_rate = 6875000;
1067
1068     fep.u.qam.fec_inner = DecodeFEC( p_access, "dvb-fec" );
1069
1070     fep.u.qam.modulation = DecodeModulation( p_access, QAM_AUTO );
1071
1072     /* Empty the event queue */
1073     for( ; ; )
1074     {
1075         struct dvb_frontend_event event;
1076         if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
1077               && errno == EWOULDBLOCK )
1078             break;
1079     }
1080
1081     /* Now send it all to the frontend device */
1082     if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
1083     {
1084         msg_Err( p_access, "frontend error: %m" );
1085         return VLC_EGENERIC;
1086     }
1087
1088     return VLC_SUCCESS;
1089 }
1090
1091 /*****************************************************************************
1092  * FrontendSetOFDM : controls the FE device
1093  *****************************************************************************/
1094 static fe_bandwidth_t DecodeBandwidth( access_t *p_access )
1095 {
1096     fe_bandwidth_t      fe_bandwidth = 0;
1097     int i_bandwith = var_GetInteger( p_access, "dvb-bandwidth" );
1098
1099     msg_Dbg( p_access, "using bandwidth=%d", i_bandwith );
1100
1101     switch( i_bandwith )
1102     {
1103         case 0: fe_bandwidth = BANDWIDTH_AUTO; break;
1104         case 6: fe_bandwidth = BANDWIDTH_6_MHZ; break;
1105         case 7: fe_bandwidth = BANDWIDTH_7_MHZ; break;
1106         case 8: fe_bandwidth = BANDWIDTH_8_MHZ; break;
1107         default:
1108             msg_Dbg( p_access, "terrestrial dvb has bandwidth not set, using auto" );
1109             fe_bandwidth = BANDWIDTH_AUTO;
1110             break;
1111     }
1112     return fe_bandwidth;
1113 }
1114
1115 static fe_transmit_mode_t DecodeTransmission( access_t *p_access )
1116 {
1117     fe_transmit_mode_t  fe_transmission = 0;
1118     int i_transmission = var_GetInteger( p_access, "dvb-transmission" );
1119
1120     msg_Dbg( p_access, "using transmission=%d", i_transmission );
1121
1122     switch( i_transmission )
1123     {
1124         case 0: fe_transmission = TRANSMISSION_MODE_AUTO; break;
1125         case 2: fe_transmission = TRANSMISSION_MODE_2K; break;
1126         case 8: fe_transmission = TRANSMISSION_MODE_8K; break;
1127         default:
1128             msg_Dbg( p_access, "terrestrial dvb has transmission mode not set, using auto");
1129             fe_transmission = TRANSMISSION_MODE_AUTO;
1130             break;
1131     }
1132     return fe_transmission;
1133 }
1134
1135 static fe_guard_interval_t DecodeGuardInterval( access_t *p_access )
1136 {
1137     fe_guard_interval_t fe_guard = 0;
1138     int i_guard = var_GetInteger( p_access, "dvb-guard" );
1139
1140     msg_Dbg( p_access, "using guard=%d", i_guard );
1141
1142     switch( i_guard )
1143     {
1144         case 0: fe_guard = GUARD_INTERVAL_AUTO; break;
1145         case 4: fe_guard = GUARD_INTERVAL_1_4; break;
1146         case 8: fe_guard = GUARD_INTERVAL_1_8; break;
1147         case 16: fe_guard = GUARD_INTERVAL_1_16; break;
1148         case 32: fe_guard = GUARD_INTERVAL_1_32; break;
1149         default:
1150             msg_Dbg( p_access, "terrestrial dvb has guard interval not set, using auto");
1151             fe_guard = GUARD_INTERVAL_AUTO;
1152             break;
1153     }
1154     return fe_guard;
1155 }
1156
1157 static fe_hierarchy_t DecodeHierarchy( access_t *p_access )
1158 {
1159     fe_hierarchy_t      fe_hierarchy = 0;
1160     int i_hierarchy = var_GetInteger( p_access, "dvb-hierarchy" );
1161
1162     msg_Dbg( p_access, "using hierarchy=%d", i_hierarchy );
1163
1164     switch( i_hierarchy )
1165     {
1166         case -1: fe_hierarchy = HIERARCHY_NONE; break;
1167         case 0: fe_hierarchy = HIERARCHY_AUTO; break;
1168         case 1: fe_hierarchy = HIERARCHY_1; break;
1169         case 2: fe_hierarchy = HIERARCHY_2; break;
1170         case 4: fe_hierarchy = HIERARCHY_4; break;
1171         default:
1172             msg_Dbg( p_access, "terrestrial dvb has hierarchy not set, using auto");
1173             fe_hierarchy = HIERARCHY_AUTO;
1174             break;
1175     }
1176     return fe_hierarchy;
1177 }
1178
1179 static int FrontendSetOFDM( access_t * p_access )
1180 {
1181     access_sys_t *p_sys = p_access->p_sys;
1182     struct dvb_frontend_parameters fep;
1183
1184     /* Prepare the fep structure */
1185
1186     fep.frequency = var_GetInteger( p_access, "dvb-frequency" );
1187
1188     fep.inversion = DecodeInversion( p_access );
1189
1190     fep.u.ofdm.bandwidth = DecodeBandwidth( p_access );
1191     fep.u.ofdm.code_rate_HP = DecodeFEC( p_access, "dvb-code-rate-hp" );
1192     fep.u.ofdm.code_rate_LP = DecodeFEC( p_access, "dvb-code-rate-lp" );
1193     fep.u.ofdm.constellation = DecodeModulation( p_access, QAM_AUTO );
1194     fep.u.ofdm.transmission_mode = DecodeTransmission( p_access );
1195     fep.u.ofdm.guard_interval = DecodeGuardInterval( p_access );
1196     fep.u.ofdm.hierarchy_information = DecodeHierarchy( p_access );
1197
1198     /* Empty the event queue */
1199     for( ; ; )
1200     {
1201         struct dvb_frontend_event event;
1202         if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
1203               && errno == EWOULDBLOCK )
1204             break;
1205     }
1206
1207     /* Now send it all to the frontend device */
1208     if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
1209     {
1210         msg_Err( p_access, "frontend error: %m" );
1211         return VLC_EGENERIC;
1212     }
1213
1214     return VLC_SUCCESS;
1215 }
1216
1217 /*****************************************************************************
1218  * FrontendSetATSC : controls the FE device
1219  *****************************************************************************/
1220 static int FrontendSetATSC( access_t *p_access )
1221 {
1222     access_sys_t *p_sys = p_access->p_sys;
1223     struct dvb_frontend_parameters fep;
1224
1225     /* Prepare the fep structure */
1226
1227     fep.frequency = var_GetInteger( p_access, "dvb-frequency" );
1228
1229     fep.u.vsb.modulation = DecodeModulation( p_access, VSB_8 );
1230
1231     /* Empty the event queue */
1232     for( ; ; )
1233     {
1234         struct dvb_frontend_event event;
1235         if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
1236               && errno == EWOULDBLOCK )
1237             break;
1238     }
1239
1240     /* Now send it all to the frontend device */
1241     if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
1242     {
1243         msg_Err( p_access, "frontend error: %m" );
1244         return VLC_EGENERIC;
1245     }
1246
1247     return VLC_SUCCESS;
1248 }
1249
1250
1251 /*
1252  * Demux
1253  */
1254
1255 /*****************************************************************************
1256  * DMXSetFilter : controls the demux to add a filter
1257  *****************************************************************************/
1258 int DMXSetFilter( access_t * p_access, int i_pid, int * pi_fd, int i_type )
1259 {
1260     struct dmx_pes_filter_params s_filter_params;
1261     unsigned int i_adapter, i_device;
1262     char dmx[128];
1263
1264     i_adapter = var_GetInteger( p_access, "dvb-adapter" );
1265     i_device = var_GetInteger( p_access, "dvb-device" );
1266
1267     if( snprintf( dmx, sizeof(dmx), DMX, i_adapter, i_device )
1268             >= (int)sizeof(dmx) )
1269     {
1270         msg_Err( p_access, "snprintf() truncated string for DMX" );
1271         dmx[sizeof(dmx) - 1] = '\0';
1272     }
1273
1274     msg_Dbg( p_access, "Opening device %s", dmx );
1275     if( (*pi_fd = vlc_open(dmx, O_RDWR)) < 0 )
1276     {
1277         msg_Err( p_access, "DMXSetFilter: opening device failed (%m)" );
1278         return VLC_EGENERIC;
1279     }
1280
1281     /* We fill the DEMUX structure : */
1282     s_filter_params.pid     =   i_pid;
1283     s_filter_params.input   =   DMX_IN_FRONTEND;
1284     s_filter_params.output  =   DMX_OUT_TS_TAP;
1285     s_filter_params.flags   =   DMX_IMMEDIATE_START;
1286
1287     switch ( i_type )
1288     {   /* First device */
1289         case 1:
1290             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO0 for PID %d", i_pid);
1291             s_filter_params.pes_type = DMX_PES_VIDEO0;
1292             break;
1293         case 2:
1294             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO0 for PID %d", i_pid);
1295             s_filter_params.pes_type = DMX_PES_AUDIO0;
1296             break;
1297         case 3:
1298             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT0 for PID %d", i_pid);
1299             s_filter_params.pes_type = DMX_PES_TELETEXT0;
1300             break;
1301         case 4:
1302             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE0 for PID %d", i_pid);
1303             s_filter_params.pes_type = DMX_PES_SUBTITLE0;
1304             break;
1305         case 5:
1306             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR0 for PID %d", i_pid);
1307             s_filter_params.pes_type = DMX_PES_PCR0;
1308             break;
1309         /* Second device */
1310         case 6:
1311             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO1 for PID %d", i_pid);
1312             s_filter_params.pes_type = DMX_PES_VIDEO1;
1313             break;
1314         case 7:
1315             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO1 for PID %d", i_pid);
1316             s_filter_params.pes_type = DMX_PES_AUDIO1;
1317             break;
1318         case 8:
1319             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT1 for PID %d", i_pid);
1320             s_filter_params.pes_type = DMX_PES_TELETEXT1;
1321             break;
1322         case 9:
1323             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE1 for PID %d", i_pid);
1324             s_filter_params.pes_type = DMX_PES_SUBTITLE1;
1325             break;
1326         case 10:
1327             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR1 for PID %d", i_pid);
1328             s_filter_params.pes_type = DMX_PES_PCR1;
1329             break;
1330         /* Third device */
1331         case 11:
1332             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO2 for PID %d", i_pid);
1333             s_filter_params.pes_type = DMX_PES_VIDEO2;
1334             break;
1335         case 12:
1336             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO2 for PID %d", i_pid);
1337             s_filter_params.pes_type = DMX_PES_AUDIO2;
1338             break;
1339         case 13:
1340             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT2 for PID %d", i_pid);
1341             s_filter_params.pes_type = DMX_PES_TELETEXT2;
1342             break;
1343         case 14:
1344             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE2 for PID %d", i_pid);
1345             s_filter_params.pes_type = DMX_PES_SUBTITLE2;
1346             break;
1347         case 15:
1348             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR2 for PID %d", i_pid);
1349             s_filter_params.pes_type = DMX_PES_PCR2;
1350             break;
1351         /* Forth device */
1352         case 16:
1353             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_VIDEO3 for PID %d", i_pid);
1354             s_filter_params.pes_type = DMX_PES_VIDEO3;
1355             break;
1356         case 17:
1357             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_AUDIO3 for PID %d", i_pid);
1358             s_filter_params.pes_type = DMX_PES_AUDIO3;
1359             break;
1360         case 18:
1361             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_TELETEXT3 for PID %d", i_pid);
1362             s_filter_params.pes_type = DMX_PES_TELETEXT3;
1363             break;
1364         case 19:
1365             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_SUBTITLE3 for PID %d", i_pid);
1366             s_filter_params.pes_type = DMX_PES_SUBTITLE3;
1367             break;
1368         case 20:
1369             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_PCR3 for PID %d", i_pid);
1370             s_filter_params.pes_type = DMX_PES_PCR3;
1371             break;
1372         /* Usually used by Nova cards */
1373         case 21:
1374         default:
1375             msg_Dbg(p_access, "DMXSetFilter: DMX_PES_OTHER for PID %d", i_pid);
1376             s_filter_params.pes_type = DMX_PES_OTHER;
1377             break;
1378     }
1379
1380     /* We then give the order to the device : */
1381     if( ioctl( *pi_fd, DMX_SET_PES_FILTER, &s_filter_params ) )
1382     {
1383         msg_Err( p_access, "setting demux PES filter failed: %m" );
1384         return VLC_EGENERIC;
1385     }
1386     return VLC_SUCCESS;
1387 }
1388
1389 /*****************************************************************************
1390  * DMXUnsetFilter : removes a filter
1391  *****************************************************************************/
1392 int DMXUnsetFilter( access_t * p_access, int i_fd )
1393 {
1394     if( ioctl( i_fd, DMX_STOP ) < 0 )
1395     {
1396         msg_Err( p_access, "stopping demux failed: %m" );
1397         return VLC_EGENERIC;
1398     }
1399
1400     msg_Dbg( p_access, "DMXUnsetFilter: closing demux %d", i_fd );
1401     close( i_fd );
1402     return VLC_SUCCESS;
1403 }
1404
1405
1406 /*
1407  * DVR device
1408  */
1409
1410 /*****************************************************************************
1411  * DVROpen :
1412  *****************************************************************************/
1413 int DVROpen( access_t * p_access )
1414 {
1415     access_sys_t *p_sys = p_access->p_sys;
1416     unsigned int i_adapter, i_device;
1417     char dvr[128];
1418
1419     i_adapter = var_GetInteger( p_access, "dvb-adapter" );
1420     i_device = var_GetInteger( p_access, "dvb-device" );
1421
1422     if( snprintf( dvr, sizeof(dvr), DVR, i_adapter, i_device )
1423             >= (int)sizeof(dvr) )
1424     {
1425         msg_Err( p_access, "snprintf() truncated string for DVR" );
1426         dvr[sizeof(dvr) - 1] = '\0';
1427     }
1428
1429     msg_Dbg( p_access, "Opening device %s", dvr );
1430     if( (p_sys->i_handle = vlc_open(dvr, O_RDONLY)) < 0 )
1431     {
1432         msg_Err( p_access, "DVROpen: opening device failed (%m)" );
1433         return VLC_EGENERIC;
1434     }
1435
1436     if( fcntl( p_sys->i_handle, F_SETFL, O_NONBLOCK ) == -1 )
1437     {
1438         msg_Warn( p_access, "DVROpen: couldn't set non-blocking mode (%m)" );
1439     }
1440
1441     return VLC_SUCCESS;
1442 }
1443
1444 /*****************************************************************************
1445  * DVRClose :
1446  *****************************************************************************/
1447 void DVRClose( access_t * p_access )
1448 {
1449     access_sys_t *p_sys = p_access->p_sys;
1450
1451     close( p_sys->i_handle );
1452 }
1453
1454
1455 /*
1456  * CAM device
1457  */
1458
1459 /*****************************************************************************
1460  * CAMOpen :
1461  *****************************************************************************/
1462 int CAMOpen( access_t *p_access )
1463 {
1464     access_sys_t *p_sys = p_access->p_sys;
1465     char ca[128];
1466     int i_adapter, i_device;
1467     ca_caps_t caps;
1468
1469     i_adapter = var_GetInteger( p_access, "dvb-adapter" );
1470     i_device = var_GetInteger( p_access, "dvb-device" );
1471
1472     if( snprintf( ca, sizeof(ca), CA, i_adapter, i_device ) >= (int)sizeof(ca) )
1473     {
1474         msg_Err( p_access, "snprintf() truncated string for CA" );
1475         ca[sizeof(ca) - 1] = '\0';
1476     }
1477     memset( &caps, 0, sizeof( ca_caps_t ));
1478
1479     msg_Dbg( p_access, "Opening device %s", ca );
1480     if( (p_sys->i_ca_handle = vlc_open(ca, O_RDWR | O_NONBLOCK)) < 0 )
1481     {
1482         msg_Warn( p_access, "CAMInit: opening CAM device failed (%m)" );
1483         p_sys->i_ca_handle = 0;
1484         return VLC_EGENERIC;
1485     }
1486
1487     if ( ioctl( p_sys->i_ca_handle, CA_GET_CAP, &caps ) != 0 )
1488     {
1489         msg_Err( p_access, "CAMInit: ioctl() error getting CAM capabilities" );
1490         close( p_sys->i_ca_handle );
1491         p_sys->i_ca_handle = 0;
1492         return VLC_EGENERIC;
1493     }
1494
1495     /* Output CA capabilities */
1496     msg_Dbg( p_access, "CAMInit: CA interface with %d %s", caps.slot_num,
1497         caps.slot_num == 1 ? "slot" : "slots" );
1498     if ( caps.slot_type & CA_CI )
1499         msg_Dbg( p_access, "CAMInit: CI high level interface type" );
1500     if ( caps.slot_type & CA_CI_LINK )
1501         msg_Dbg( p_access, "CAMInit: CI link layer level interface type" );
1502     if ( caps.slot_type & CA_CI_PHYS )
1503         msg_Dbg( p_access, "CAMInit: CI physical layer level interface type (not supported) " );
1504     if ( caps.slot_type & CA_DESCR )
1505         msg_Dbg( p_access, "CAMInit: built-in descrambler detected" );
1506     if ( caps.slot_type & CA_SC )
1507         msg_Dbg( p_access, "CAMInit: simple smart card interface" );
1508
1509     msg_Dbg( p_access, "CAMInit: %d available %s", caps.descr_num,
1510         caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
1511     if ( caps.descr_type & CA_ECD )
1512         msg_Dbg( p_access, "CAMInit: ECD scrambling system supported" );
1513     if ( caps.descr_type & CA_NDS )
1514         msg_Dbg( p_access, "CAMInit: NDS scrambling system supported" );
1515     if ( caps.descr_type & CA_DSS )
1516         msg_Dbg( p_access, "CAMInit: DSS scrambling system supported" );
1517  
1518     if ( caps.slot_num == 0 )
1519     {
1520         msg_Err( p_access, "CAMInit: CAM module with no slots" );
1521         close( p_sys->i_ca_handle );
1522         p_sys->i_ca_handle = 0;
1523         return VLC_EGENERIC;
1524     }
1525
1526     if( caps.slot_type & CA_CI_LINK )
1527     {
1528         p_sys->i_ca_type = CA_CI_LINK;
1529     }
1530     else if( caps.slot_type & CA_CI )
1531     {
1532         p_sys->i_ca_type = CA_CI;
1533     }
1534     else
1535     {
1536         p_sys->i_ca_type = -1;
1537         msg_Err( p_access, "CAMInit: incompatible CAM interface" );
1538         close( p_sys->i_ca_handle );
1539         p_sys->i_ca_handle = 0;
1540         return VLC_EGENERIC;
1541     }
1542
1543     p_sys->i_nb_slots = caps.slot_num;
1544     memset( p_sys->pb_active_slot, 0, sizeof(bool) * MAX_CI_SLOTS );
1545     memset( p_sys->pb_slot_mmi_expected, 0, sizeof(bool) * MAX_CI_SLOTS );
1546     memset( p_sys->pb_slot_mmi_undisplayed, 0,
1547             sizeof(bool) * MAX_CI_SLOTS );
1548
1549     return en50221_Init( p_access );
1550 }
1551
1552 /*****************************************************************************
1553  * CAMPoll :
1554  *****************************************************************************/
1555 int CAMPoll( access_t * p_access )
1556 {
1557     access_sys_t *p_sys = p_access->p_sys;
1558     int i_ret = VLC_EGENERIC;
1559
1560     if ( p_sys->i_ca_handle == 0 )
1561     {
1562         return VLC_EGENERIC;
1563     }
1564
1565     switch( p_sys->i_ca_type )
1566     {
1567     case CA_CI_LINK:
1568         i_ret = en50221_Poll( p_access );
1569         break;
1570     case CA_CI:
1571         i_ret = VLC_SUCCESS;
1572         break;
1573     default:
1574         msg_Err( p_access, "CAMPoll: This should not happen" );
1575         break;
1576     }
1577
1578     return i_ret;
1579 }
1580
1581 #ifdef ENABLE_HTTPD
1582 /*****************************************************************************
1583  * CAMStatus :
1584  *****************************************************************************/
1585 void CAMStatus( access_t * p_access )
1586 {
1587     access_sys_t *p_sys = p_access->p_sys;
1588     char *p;
1589     ca_caps_t caps;
1590     int i_slot, i;
1591
1592     if ( p_sys->psz_request != NULL && *p_sys->psz_request )
1593     {
1594         /* Check if we have an undisplayed MMI message : in that case we ignore
1595          * the user input to avoid confusing the CAM. */
1596         for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1597         {
1598             if ( p_sys->pb_slot_mmi_undisplayed[i_slot] == true )
1599             {
1600                 p_sys->psz_request = NULL;
1601                 msg_Dbg( p_access,
1602                          "ignoring user request because of a new MMI object" );
1603                 break;
1604             }
1605         }
1606     }
1607
1608     if ( p_sys->psz_request != NULL && *p_sys->psz_request )
1609     {
1610         /* We have a mission to accomplish. */
1611         en50221_mmi_object_t mmi_object;
1612         char *psz_request = p_sys->psz_request;
1613         char psz_value[255];
1614         int i_slot;
1615         bool b_ok = false;
1616
1617         p_sys->psz_request = NULL;
1618
1619         if ( HTTPExtractValue( psz_request, "slot", psz_value,
1620                                    sizeof(psz_value) ) == NULL )
1621         {
1622             p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
1623             goto out;
1624         }
1625         i_slot = atoi(psz_value);
1626
1627         if ( HTTPExtractValue( psz_request, "open", psz_value,
1628                                    sizeof(psz_value) ) != NULL )
1629         {
1630             en50221_OpenMMI( p_access, i_slot );
1631             return;
1632         }
1633
1634         if ( HTTPExtractValue( psz_request, "close", psz_value,
1635                                    sizeof(psz_value) ) != NULL )
1636         {
1637             en50221_CloseMMI( p_access, i_slot );
1638             return;
1639         }
1640
1641         if ( HTTPExtractValue( psz_request, "cancel", psz_value,
1642                                    sizeof(psz_value) ) == NULL )
1643         {
1644             b_ok = true;
1645         }
1646
1647         if ( HTTPExtractValue( psz_request, "type", psz_value,
1648                                    sizeof(psz_value) ) == NULL )
1649         {
1650             p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
1651             goto out;
1652         }
1653
1654         if ( !strcmp( psz_value, "enq" ) )
1655         {
1656             mmi_object.i_object_type = EN50221_MMI_ANSW;
1657             mmi_object.u.answ.b_ok = b_ok;
1658             if ( b_ok == false )
1659             {
1660                 mmi_object.u.answ.psz_answ = strdup("");
1661             }
1662             else
1663             {
1664                 if ( HTTPExtractValue( psz_request, "answ", psz_value,
1665                                            sizeof(psz_value) ) == NULL )
1666                 {
1667                     p_sys->psz_mmi_info = strdup( "invalid request parameter\n" );
1668                     goto out;
1669                 }
1670
1671                 mmi_object.u.answ.psz_answ = strdup(psz_value);
1672             }
1673         }
1674         else
1675         {
1676             mmi_object.i_object_type = EN50221_MMI_MENU_ANSW;
1677             if ( b_ok == false )
1678             {
1679                 mmi_object.u.menu_answ.i_choice = 0;
1680             }
1681             else
1682             {
1683                 if ( HTTPExtractValue( psz_request, "choice", psz_value,
1684                                            sizeof(psz_value) ) == NULL )
1685                     mmi_object.u.menu_answ.i_choice = 0;
1686                 else
1687                     mmi_object.u.menu_answ.i_choice = atoi(psz_value);
1688             }
1689         }
1690
1691         en50221_SendMMIObject( p_access, i_slot, &mmi_object );
1692         return;
1693     }
1694
1695     /* Check that we have all necessary MMI information. */
1696     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1697     {
1698         if ( p_sys->pb_slot_mmi_expected[i_slot] == true )
1699             return;
1700     }
1701
1702     p = p_sys->psz_mmi_info = malloc( 10000 );
1703
1704     if ( ioctl( p_sys->i_ca_handle, CA_GET_CAP, &caps ) != 0 )
1705     {
1706         char buf[1000];
1707         strerror_r( errno, buf, sizeof( buf ) );
1708         p += sprintf( p, "ioctl CA_GET_CAP failed (%s)\n", buf );
1709         goto out;
1710     }
1711
1712     /* Output CA capabilities */
1713     p += sprintf( p, "CA interface with %d %s, type:\n<table>", caps.slot_num,
1714                   caps.slot_num == 1 ? "slot" : "slots" );
1715 #define CHECK_CAPS( x, s )                                                  \
1716     if ( caps.slot_type & (CA_##x) )                                        \
1717         p += sprintf( p, "<tr><td>" s "</td></tr>\n" );
1718
1719     CHECK_CAPS( CI, "CI high level interface" );
1720     CHECK_CAPS( CI_LINK, "CI link layer level interface" );
1721     CHECK_CAPS( CI_PHYS, "CI physical layer level interface (not supported)" );
1722     CHECK_CAPS( DESCR, "built-in descrambler" );
1723     CHECK_CAPS( SC, "simple smartcard interface" );
1724 #undef CHECK_CAPS
1725
1726     p += sprintf( p, "</table>%d available %s\n<table>", caps.descr_num,
1727         caps.descr_num == 1 ? "descrambler (key)" : "descramblers (keys)" );
1728 #define CHECK_DESC( x )                                                     \
1729     if ( caps.descr_type & (CA_##x) )                                       \
1730         p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );
1731
1732     CHECK_DESC( ECD );
1733     CHECK_DESC( NDS );
1734     CHECK_DESC( DSS );
1735 #undef CHECK_DESC
1736
1737     p += sprintf( p, "</table>" );
1738
1739     for ( i_slot = 0; i_slot < p_sys->i_nb_slots; i_slot++ )
1740     {
1741         ca_slot_info_t sinfo;
1742
1743         p_sys->pb_slot_mmi_undisplayed[i_slot] = false;
1744         p += sprintf( p, "<p>CA slot #%d: ", i_slot );
1745
1746         sinfo.num = i_slot;
1747         if ( ioctl( p_sys->i_ca_handle, CA_GET_SLOT_INFO, &sinfo ) != 0 )
1748         {
1749             char buf[1000];
1750             strerror_r( errno, buf, sizeof( buf ) );
1751             p += sprintf( p, "ioctl CA_GET_SLOT_INFO failed (%s)<br>\n", buf );
1752             continue;
1753         }
1754
1755 #define CHECK_TYPE( x, s )                                                  \
1756         if ( sinfo.type & (CA_##x) )                                        \
1757             p += sprintf( p, "%s", s );
1758
1759         CHECK_TYPE( CI, "high level, " );
1760         CHECK_TYPE( CI_LINK, "link layer level, " );
1761         CHECK_TYPE( CI_PHYS, "physical layer level, " );
1762 #undef CHECK_TYPE
1763
1764         if ( sinfo.flags & CA_CI_MODULE_READY )
1765         {
1766             en50221_mmi_object_t *p_object = en50221_GetMMIObject( p_access,
1767                                                                        i_slot );
1768
1769             p += sprintf( p, "module present and ready<p>\n" );
1770             p += sprintf( p, "<form action=index.html method=get>\n" );
1771             p += sprintf( p, "<input type=hidden name=slot value=\"%d\">\n",
1772                           i_slot );
1773
1774             if ( p_object == NULL )
1775             {
1776                 p += sprintf( p, "<input type=submit name=open value=\"Open session\">\n" );
1777             }
1778             else
1779             {
1780                 switch ( p_object->i_object_type )
1781                 {
1782                 case EN50221_MMI_ENQ:
1783                     p += sprintf( p, "<input type=hidden name=type value=enq>\n" );
1784                     p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
1785                                   p_object->u.enq.psz_text );
1786                     if ( p_object->u.enq.b_blind == false )
1787                         p += sprintf( p, "<tr><td><input type=text name=answ></td></tr>\n" );
1788                     else
1789                         p += sprintf( p, "<tr><td><input type=password name=answ></td></tr>\n" );
1790                     break;
1791
1792                 case EN50221_MMI_MENU:
1793                     p += sprintf( p, "<input type=hidden name=type value=menu>\n" );
1794                     p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
1795                                   p_object->u.menu.psz_title );
1796                     p += sprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
1797                                   p_object->u.menu.psz_subtitle );
1798                     for ( i = 0; i < p_object->u.menu.i_choices; i++ )
1799                         p += sprintf( p, "<input type=radio name=choice value=\"%d\">%s<br>\n", i + 1, p_object->u.menu.ppsz_choices[i] );
1800                     p += sprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
1801                                   p_object->u.menu.psz_bottom );
1802                     break;
1803
1804                 case EN50221_MMI_LIST:
1805                     p += sprintf( p, "<input type=hidden name=type value=menu>\n" );
1806                     p += sprintf( p, "<input type=hidden name=choice value=0>\n" );
1807                     p += sprintf( p, "<table border=1><tr><th>%s</th></tr>\n",
1808                                   p_object->u.menu.psz_title );
1809                     p += sprintf( p, "<tr><td>%s</td></tr><tr><td>\n",
1810                                   p_object->u.menu.psz_subtitle );
1811                     for ( i = 0; i < p_object->u.menu.i_choices; i++ )
1812                         p += sprintf( p, "%s<br>\n",
1813                                       p_object->u.menu.ppsz_choices[i] );
1814                     p += sprintf( p, "</td></tr><tr><td>%s</td></tr>\n",
1815                                   p_object->u.menu.psz_bottom );
1816                     break;
1817
1818                 default:
1819                     p += sprintf( p, "<table><tr><th>Unknown MMI object type</th></tr>\n" );
1820                 }
1821
1822                 p += sprintf( p, "</table><p><input type=submit name=ok value=\"OK\">\n" );
1823                 p += sprintf( p, "<input type=submit name=cancel value=\"Cancel\">\n" );
1824                 p += sprintf( p, "<input type=submit name=close value=\"Close Session\">\n" );
1825             }
1826             p += sprintf( p, "</form>\n" );
1827         }
1828         else if ( sinfo.flags & CA_CI_MODULE_PRESENT )
1829             p += sprintf( p, "module present, not ready<br>\n" );
1830         else
1831             p += sprintf( p, "module not present<br>\n" );
1832     }
1833
1834 out:
1835     vlc_mutex_lock( &p_sys->httpd_mutex );
1836     p_sys->b_request_mmi_info = false;
1837     vlc_cond_signal( &p_sys->httpd_cond );
1838     vlc_mutex_unlock( &p_sys->httpd_mutex );
1839 }
1840 #endif
1841
1842 /*****************************************************************************
1843  * CAMSet :
1844  *****************************************************************************/
1845 int CAMSet( access_t * p_access, dvbpsi_pmt_t *p_pmt )
1846 {
1847     access_sys_t *p_sys = p_access->p_sys;
1848
1849     if( p_sys->i_ca_handle == 0 )
1850     {
1851         dvbpsi_DeletePMT( p_pmt );
1852         return VLC_EGENERIC;
1853     }
1854
1855     en50221_SetCAPMT( p_access, p_pmt );
1856
1857     return VLC_SUCCESS;
1858 }
1859
1860 /*****************************************************************************
1861  * CAMClose :
1862  *****************************************************************************/
1863 void CAMClose( access_t * p_access )
1864 {
1865     access_sys_t *p_sys = p_access->p_sys;
1866
1867     en50221_End( p_access );
1868
1869     if ( p_sys->i_ca_handle )
1870     {
1871         close( p_sys->i_ca_handle );
1872     }
1873 }
1874