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