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