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