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