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