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