linux_dvb.c 58.7 KB
Newer Older
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
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 *
 * Authors: Damien Lucas <nitrox@via.ecp.fr>
 *          Johan Bilien <jobi@via.ecp.fr>
 *          Jean-Paul Saman <jpsaman@wxs.nl>
 *          Christopher Ross <chris@tebibyte.org>
 *          Christophe Massiot <massiot@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA    02111, USA.
 *****************************************************************************/

#include <vlc/vlc.h>
#include <vlc/input.h>

#include <sys/ioctl.h>
31
#include <errno.h>
32
33
34
35
36
37
38
39
40
41
42
43
44

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/poll.h>

/* DVB Card Drivers */
#include <linux/dvb/version.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/frontend.h>
45
#include <linux/dvb/ca.h>
46

Christophe Massiot's avatar
Christophe Massiot committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* Include dvbpsi headers */
#ifdef HAVE_DVBPSI_DR_H
#   include <dvbpsi/dvbpsi.h>
#   include <dvbpsi/descriptor.h>
#   include <dvbpsi/pat.h>
#   include <dvbpsi/pmt.h>
#   include <dvbpsi/dr.h>
#   include <dvbpsi/psi.h>
#else
#   include "dvbpsi.h"
#   include "descriptor.h"
#   include "tables/pat.h"
#   include "tables/pmt.h"
#   include "descriptors/dr.h"
#   include "psi.h"
#endif
63

64
65
66
67
#ifdef ENABLE_HTTPD
#   include "vlc_httpd.h"
#endif

Christophe Massiot's avatar
Christophe Massiot committed
68
#include "dvb.h"
69
70
71
72

/*
 * Frontends
 */
73
74
struct frontend_t
{
Christophe Massiot's avatar
Christophe Massiot committed
75
    fe_status_t i_last_status;
76
    struct dvb_frontend_info info;
77
};
78

79
80
#define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */

81
/* Local prototypes */
82
83
84
85
static int FrontendInfo( access_t * );
static int FrontendSetQPSK( access_t * );
static int FrontendSetQAM( access_t * );
static int FrontendSetOFDM( access_t * );
86
87
88
89

/*****************************************************************************
 * FrontendOpen : Determine frontend device information and capabilities
 *****************************************************************************/
90
int E_(FrontendOpen)( access_t *p_access )
91
{
92
    access_sys_t *p_sys = p_access->p_sys;
93
94
95
96
97
    frontend_t * p_frontend;
    unsigned int i_adapter, i_device;
    vlc_bool_t b_probe;
    char frontend[128];

98
99
100
    i_adapter = var_GetInteger( p_access, "dvb-adapter" );
    i_device = var_GetInteger( p_access, "dvb-device" );
    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
106
107
        frontend[sizeof(frontend) - 1] = '\0';
    }

108
    p_sys->p_frontend = p_frontend = malloc( sizeof(frontend_t) );
109

110
    msg_Dbg( p_access, "Opening device %s", frontend );
Christophe Massiot's avatar
Christophe Massiot committed
111
    if( (p_sys->i_frontend_handle = open(frontend, O_RDWR | O_NONBLOCK)) < 0 )
112
    {
113
        msg_Err( p_access, "FrontEndOpen: opening device failed (%s)",
114
115
                 strerror(errno) );
        free( p_frontend );
116
        return VLC_EGENERIC;
117
118
    }

119
    if( b_probe )
120
121
122
123
    {
        char * psz_expected = NULL;
        char * psz_real;

124
        if( FrontendInfo( p_access ) < 0 )
125
        {
Christophe Massiot's avatar
Christophe Massiot committed
126
            close( p_sys->i_frontend_handle );
127
            free( p_frontend );
128
            return VLC_EGENERIC;
129
130
        }

131
        switch( p_frontend->info.type )
132
133
134
135
136
137
138
139
140
141
142
143
144
        {
        case FE_OFDM:
            psz_real = "DVB-T";
            break;
        case FE_QAM:
            psz_real = "DVB-C";
            break;
        case FE_QPSK:
            psz_real = "DVB-S";
            break;
        default:
            psz_real = "unknown";
        }
145

146
        /* Sanity checks */
147
148
149
        if( (!strncmp( p_access->psz_access, "qpsk", 4 ) ||
             !strncmp( p_access->psz_access, "dvb-s", 5 ) ||
             !strncmp( p_access->psz_access, "satellite", 9 ) ) &&
150
151
152
153
             (p_frontend->info.type != FE_QPSK) )
        {
            psz_expected = "DVB-S";
        }
154
155
        if( (!strncmp( p_access->psz_access, "cable", 5 ) ||
             !strncmp( p_access->psz_access, "dvb-c", 5 ) ) &&
156
157
158
159
             (p_frontend->info.type != FE_QAM) )
        {
            psz_expected = "DVB-C";
        }
160
161
        if( (!strncmp( p_access->psz_access, "terrestrial", 11 ) ||
             !strncmp( p_access->psz_access, "dvb-t", 5 ) ) &&
162
163
164
165
166
             (p_frontend->info.type != FE_OFDM) )
        {
            psz_expected = "DVB-T";
        }

167
        if( psz_expected != NULL )
168
        {
169
            msg_Err( p_access, "the user asked for %s, and the tuner is %s",
170
                     psz_expected, psz_real );
Christophe Massiot's avatar
Christophe Massiot committed
171
            close( p_sys->i_frontend_handle );
172
            free( p_frontend );
173
            return VLC_EGENERIC;
174
175
176
177
        }
    }
    else /* no frontend probing is done so use default border values. */
    {
178
        msg_Dbg( p_access, "using default values for frontend info" );
179

180
        msg_Dbg( p_access, "method of access is %s", p_access->psz_access );
181
        p_frontend->info.type = FE_QPSK;
182
183
        if( !strncmp( p_access->psz_access, "qpsk", 4 ) ||
            !strncmp( p_access->psz_access, "dvb-s", 5 ) )
184
            p_frontend->info.type = FE_QPSK;
185
186
        else if( !strncmp( p_access->psz_access, "cable", 5 ) ||
                 !strncmp( p_access->psz_access, "dvb-c", 5 ) )
187
            p_frontend->info.type = FE_QAM;
188
189
        else if( !strncmp( p_access->psz_access, "terrestrial", 11 ) ||
                 !strncmp( p_access->psz_access, "dvb-t", 5 ) )
190
191
192
            p_frontend->info.type = FE_OFDM;
    }

193
    return VLC_SUCCESS;
194
195
196
197
198
}

/*****************************************************************************
 * FrontendClose : Close the frontend
 *****************************************************************************/
199
void E_(FrontendClose)( access_t *p_access )
200
{
201
    access_sys_t *p_sys = p_access->p_sys;
202

203
    if( p_sys->p_frontend )
204
    {
Christophe Massiot's avatar
Christophe Massiot committed
205
        close( p_sys->i_frontend_handle );
206
207
208
        free( p_sys->p_frontend );

        p_sys->p_frontend = NULL;
209
210
211
212
213
214
    }
}

/*****************************************************************************
 * FrontendSet : Tune !
 *****************************************************************************/
215
int E_(FrontendSet)( access_t *p_access )
216
{
217
    access_sys_t *p_sys = p_access->p_sys;
218

219
    switch( p_sys->p_frontend->info.type )
220
    {
Christophe Massiot's avatar
Christophe Massiot committed
221
    /* DVB-S */
222
    case FE_QPSK:
223
        if( FrontendSetQPSK( p_access ) < 0 )
224
        {
225
226
            msg_Err( p_access, "DVB-S: tuning failed" );
            return VLC_EGENERIC;
227
228
        }
        break;
229

230
231
    /* DVB-C */
    case FE_QAM:
232
        if( FrontendSetQAM( p_access ) < 0 )
233
        {
234
235
            msg_Err( p_access, "DVB-C: tuning failed" );
            return VLC_EGENERIC;
236
237
238
239
240
        }
        break;

    /* DVB-T */
    case FE_OFDM:
241
        if( FrontendSetOFDM( p_access ) < 0 )
242
        {
243
244
            msg_Err( p_access, "DVB-T: tuning failed" );
            return VLC_EGENERIC;
245
246
247
248
        }
        break;

    default:
249
250
251
        msg_Err( p_access, "Could not determine frontend type on %s",
                 p_sys->p_frontend->info.name );
        return VLC_EGENERIC;
252
    }
Christophe Massiot's avatar
Christophe Massiot committed
253
    p_sys->p_frontend->i_last_status = 0;
254
    p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
255
    return VLC_SUCCESS;
256
257
}

Christophe Massiot's avatar
Christophe Massiot committed
258
259
260
261
262
263
264
265
266
267
/*****************************************************************************
 * FrontendPoll : Poll for frontend events
 *****************************************************************************/
void E_(FrontendPoll)( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t * p_frontend = p_sys->p_frontend;
    struct dvb_frontend_event event;
    fe_status_t i_status, i_diff;

268
    for( ;; )
Christophe Massiot's avatar
Christophe Massiot committed
269
    {
270
271
272
273
274
        int i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event );

        if( i_ret < 0 )
        {
            if( errno == EWOULDBLOCK )
275
                return; /* no more events */
Christophe Massiot's avatar
Christophe Massiot committed
276

277
            msg_Err( p_access, "reading frontend event failed (%d) %s",
278
                     i_ret, strerror(errno) );
279
            return;
280
281
282
283
284
        }

        i_status = event.status;
        i_diff = i_status ^ p_frontend->i_last_status;
        p_frontend->i_last_status = i_status;
Christophe Massiot's avatar
Christophe Massiot committed
285

286
        {
Christophe Massiot's avatar
Christophe Massiot committed
287
#define IF_UP( x )                                                          \
288
289
290
291
        }                                                                   \
        if ( i_diff & (x) )                                                 \
        {                                                                   \
            if ( i_status & (x) )
Christophe Massiot's avatar
Christophe Massiot committed
292

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
            IF_UP( FE_HAS_SIGNAL )
                msg_Dbg( p_access, "frontend has acquired signal" );
            else
                msg_Dbg( p_access, "frontend has lost signal" );

            IF_UP( FE_HAS_CARRIER )
                msg_Dbg( p_access, "frontend has acquired carrier" );
            else
                msg_Dbg( p_access, "frontend has lost carrier" );

            IF_UP( FE_HAS_VITERBI )
                msg_Dbg( p_access, "frontend has acquired stable FEC" );
            else
                msg_Dbg( p_access, "frontend has lost FEC" );

            IF_UP( FE_HAS_SYNC )
                msg_Dbg( p_access, "frontend has acquired sync" );
            else
                msg_Dbg( p_access, "frontend has lost sync" );

            IF_UP( FE_HAS_LOCK )
            {
                int32_t i_value;
                msg_Dbg( p_access, "frontend has acquired lock" );
                p_sys->i_frontend_timeout = 0;

                /* Read some statistics */
                if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 )
                    msg_Dbg( p_access, "- Bit error rate: %d", i_value );
                if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &i_value ) >= 0 )
                    msg_Dbg( p_access, "- Signal strength: %d", i_value );
                if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 )
                    msg_Dbg( p_access, "- SNR: %d", i_value );
            }
            else
            {
                msg_Dbg( p_access, "frontend has lost lock" );
                p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
            }

            IF_UP( FE_REINIT )
            {
                /* The frontend was reinited. */
                msg_Warn( p_access, "reiniting frontend");
                E_(FrontendSet)( p_access );
            }
Christophe Massiot's avatar
Christophe Massiot committed
339
        }
340
#undef IF_UP
Christophe Massiot's avatar
Christophe Massiot committed
341
342
    }
}
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436

#ifdef ENABLE_HTTPD
/*****************************************************************************
 * FrontendStatus : Read frontend status
 *****************************************************************************/
void E_(FrontendStatus)( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t *p_frontend = p_sys->p_frontend;
    char *p = p_sys->psz_frontend_info = malloc( 10000 );
    fe_status_t i_status;
    int i_ret;

    /* Determine type of frontend */
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_INFO,
                        &p_frontend->info )) < 0 )
    {
        p += sprintf( p, "ioctl FE_GET_INFO failed (%d) %s\n", i_ret,
                      strerror(errno) );
        goto out;
    }

    /* Print out frontend capabilities. */
    p += sprintf( p, "<table border=1><tr><th>name</th><td>%s</td></tr>\n",
                  p_frontend->info.name );
    switch( p_frontend->info.type )
    {
        case FE_QPSK:
            p += sprintf( p, "<tr><th>type</th><td>QPSK (DVB-S)</td></tr>\n" );
            break;
        case FE_QAM:
            p += sprintf( p, "<tr><th>type</th><td>QAM (DVB-C)</td></tr>\n" );
            break;
        case FE_OFDM:
            p += sprintf( p, "<tr><th>type</th><td>OFDM (DVB-T)</td></tr>\n" );
            break;
#if 0 /* DVB_API_VERSION == 3 */
        case FE_MEMORY:
            p += sprintf( p, "<tr><th>type</th><td>MEMORY</td></tr>\n" );
            break;
        case FE_NET:
            p += sprintf( p, "<tr><th>type</th><td>NETWORK</td></tr>\n" );
            break;
#endif
        default:
            p += sprintf( p, "<tr><th>type</th><td>UNKNOWN (%d)</td></tr>\n",
                          p_frontend->info.type );
            goto out;
    }
#define CHECK_INFO( x )                                                     \
    p += sprintf( p,                                                        \
                  "<tr><th>" STRINGIFY(x) "</th><td>%u</td></tr>\n",        \
                  p_frontend->info.x );

    CHECK_INFO( frequency_min );
    CHECK_INFO( frequency_max );
    CHECK_INFO( frequency_stepsize );
    CHECK_INFO( frequency_tolerance );
    CHECK_INFO( symbol_rate_min );
    CHECK_INFO( symbol_rate_max );
    CHECK_INFO( symbol_rate_tolerance );
    CHECK_INFO( notifier_delay );
#undef CHECK_INFO

    p += sprintf( p, "</table><p>Frontend capability list:\n<table border=1>" );

#define CHECK_CAPS( x )                                                     \
    if ( p_frontend->info.caps & (FE_##x) )                                 \
        p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );

    CHECK_CAPS( IS_STUPID );
    CHECK_CAPS( CAN_INVERSION_AUTO );
    CHECK_CAPS( CAN_FEC_1_2 );
    CHECK_CAPS( CAN_FEC_2_3 );
    CHECK_CAPS( CAN_FEC_3_4 );
    CHECK_CAPS( CAN_FEC_4_5 );
    CHECK_CAPS( CAN_FEC_5_6 );
    CHECK_CAPS( CAN_FEC_6_7 );
    CHECK_CAPS( CAN_FEC_7_8 );
    CHECK_CAPS( CAN_FEC_8_9 );
    CHECK_CAPS( CAN_FEC_AUTO );
    CHECK_CAPS( CAN_QPSK );
    CHECK_CAPS( CAN_QAM_16 );
    CHECK_CAPS( CAN_QAM_32 );
    CHECK_CAPS( CAN_QAM_64 );
    CHECK_CAPS( CAN_QAM_128 );
    CHECK_CAPS( CAN_QAM_256 );
    CHECK_CAPS( CAN_QAM_AUTO );
    CHECK_CAPS( CAN_TRANSMISSION_MODE_AUTO );
    CHECK_CAPS( CAN_BANDWIDTH_AUTO );
    CHECK_CAPS( CAN_GUARD_INTERVAL_AUTO );
    CHECK_CAPS( CAN_HIERARCHY_AUTO );
    CHECK_CAPS( CAN_MUTE_TS );
    CHECK_CAPS( CAN_RECOVER );
437
#if 0 /* Disabled because of older distributions */
438
    CHECK_CAPS( CAN_CLEAN_SETUP );
439
#endif
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
#undef CHECK_CAPS

    p += sprintf( p, "</table><p>Current frontend status:\n<table border=1>" );

    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_READ_STATUS, &i_status ))
           < 0 )
    {
        p += sprintf( p, "</table>ioctl FE_READ_STATUS failed (%d) %s\n", i_ret,
                      strerror(errno) );
        goto out;
    }

#define CHECK_STATUS( x )                                                   \
    if ( i_status & (FE_##x) )                                              \
        p += sprintf( p, "<tr><td>" STRINGIFY(x) "</td></tr>\n" );

    CHECK_STATUS( HAS_SIGNAL );
    CHECK_STATUS( HAS_CARRIER );
    CHECK_STATUS( HAS_VITERBI );
    CHECK_STATUS( HAS_SYNC );
    CHECK_STATUS( HAS_LOCK );
    CHECK_STATUS( REINIT );
#undef CHECK_STATUS

    if ( i_status & FE_HAS_LOCK )
    {
        int32_t i_value;
        p += sprintf( p, "</table><p>Signal status:\n<table border=1>" );
        if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &i_value ) >= 0 )
            p += sprintf( p, "<tr><th>Bit error rate</th><td>%d</td></tr>\n",
                          i_value );
        if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH,
                   &i_value ) >= 0 )
            p += sprintf( p, "<tr><th>Signal strength</th><td>%d</td></tr>\n",
                          i_value );
        if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &i_value ) >= 0 )
            p += sprintf( p, "<tr><th>SNR</th><td>%d</td></tr>\n",
                          i_value );
    }
    p += sprintf( p, "</table>" );

out:
    vlc_mutex_lock( &p_sys->httpd_mutex );
    p_sys->b_request_frontend_info = VLC_FALSE;
    vlc_cond_signal( &p_sys->httpd_cond );
    vlc_mutex_unlock( &p_sys->httpd_mutex );
}
#endif

489
490
491
/*****************************************************************************
 * FrontendInfo : Return information about given frontend
 *****************************************************************************/
492
static int FrontendInfo( access_t *p_access )
493
{
494
495
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t *p_frontend = p_sys->p_frontend;
496
497
498
    int i_ret;

    /* Determine type of frontend */
Christophe Massiot's avatar
Christophe Massiot committed
499
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_INFO,
500
                        &p_frontend->info )) < 0 )
501
    {
502
        msg_Err( p_access, "ioctl FE_GET_INFO failed (%d) %s", i_ret,
503
                 strerror(errno) );
504
        return VLC_EGENERIC;
505
506
507
    }

    /* Print out frontend capabilities. */
508
509
510
    msg_Dbg(p_access, "Frontend Info:" );
    msg_Dbg(p_access, "  name = %s", p_frontend->info.name );
    switch( p_frontend->info.type )
511
512
    {
        case FE_QPSK:
513
            msg_Dbg( p_access, "  type = QPSK (DVB-S)" );
514
515
            break;
        case FE_QAM:
516
            msg_Dbg( p_access, "  type = QAM (DVB-C)" );
517
518
            break;
        case FE_OFDM:
519
            msg_Dbg( p_access, "  type = OFDM (DVB-T)" );
520
521
522
            break;
#if 0 /* DVB_API_VERSION == 3 */
        case FE_MEMORY:
523
            msg_Dbg(p_access, "  type = MEMORY" );
524
525
            break;
        case FE_NET:
526
            msg_Dbg(p_access, "  type = NETWORK" );
527
528
529
            break;
#endif
        default:
530
            msg_Err( p_access, "  unknown frontend type (%d)",
531
                     p_frontend->info.type );
532
            return VLC_EGENERIC;
533
    }
534
    msg_Dbg(p_access, "  frequency_min = %u (kHz)",
535
            p_frontend->info.frequency_min);
536
    msg_Dbg(p_access, "  frequency_max = %u (kHz)",
537
            p_frontend->info.frequency_max);
538
    msg_Dbg(p_access, "  frequency_stepsize = %u",
539
            p_frontend->info.frequency_stepsize);
540
    msg_Dbg(p_access, "  frequency_tolerance = %u",
541
            p_frontend->info.frequency_tolerance);
542
    msg_Dbg(p_access, "  symbol_rate_min = %u (kHz)",
543
            p_frontend->info.symbol_rate_min);
544
    msg_Dbg(p_access, "  symbol_rate_max = %u (kHz)",
545
            p_frontend->info.symbol_rate_max);
546
    msg_Dbg(p_access, "  symbol_rate_tolerance (ppm) = %u",
547
            p_frontend->info.symbol_rate_tolerance);
548
    msg_Dbg(p_access, "  notifier_delay (ms) = %u",
549
550
            p_frontend->info.notifier_delay );

551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
    msg_Dbg(p_access, "Frontend Info capability list:");
    if( p_frontend->info.caps & FE_IS_STUPID)
        msg_Dbg(p_access, "  no capabilities - frontend is stupid!");
    if( p_frontend->info.caps & FE_CAN_INVERSION_AUTO)
        msg_Dbg(p_access, "  inversion auto");
    if( p_frontend->info.caps & FE_CAN_FEC_1_2)
        msg_Dbg(p_access, "  forward error correction 1/2");
    if( p_frontend->info.caps & FE_CAN_FEC_2_3)
        msg_Dbg(p_access, "  forward error correction 2/3");
    if( p_frontend->info.caps & FE_CAN_FEC_3_4)
        msg_Dbg(p_access, "  forward error correction 3/4");
    if( p_frontend->info.caps & FE_CAN_FEC_4_5)
        msg_Dbg(p_access, "  forward error correction 4/5");
    if( p_frontend->info.caps & FE_CAN_FEC_5_6)
        msg_Dbg(p_access, "  forward error correction 5/6");
    if( p_frontend->info.caps & FE_CAN_FEC_6_7)
        msg_Dbg(p_access, "  forward error correction 6/7");
    if( p_frontend->info.caps & FE_CAN_FEC_7_8)
        msg_Dbg(p_access, "  forward error correction 7/8");
    if( p_frontend->info.caps & FE_CAN_FEC_8_9)
        msg_Dbg(p_access, "  forward error correction 8/9");
    if( p_frontend->info.caps & FE_CAN_FEC_AUTO)
        msg_Dbg(p_access, "  forward error correction auto");
    if( p_frontend->info.caps & FE_CAN_QPSK)
        msg_Dbg(p_access, "  card can do QPSK");
    if( p_frontend->info.caps & FE_CAN_QAM_16)
        msg_Dbg(p_access, "  card can do QAM 16");
    if( p_frontend->info.caps & FE_CAN_QAM_32)
        msg_Dbg(p_access, "  card can do QAM 32");
    if( p_frontend->info.caps & FE_CAN_QAM_64)
        msg_Dbg(p_access, "  card can do QAM 64");
    if( p_frontend->info.caps & FE_CAN_QAM_128)
        msg_Dbg(p_access, "  card can do QAM 128");
    if( p_frontend->info.caps & FE_CAN_QAM_256)
        msg_Dbg(p_access, "  card can do QAM 256");
    if( p_frontend->info.caps & FE_CAN_QAM_AUTO)
        msg_Dbg(p_access, "  card can do QAM auto");
    if( p_frontend->info.caps & FE_CAN_TRANSMISSION_MODE_AUTO)
        msg_Dbg(p_access, "  transmission mode auto");
    if( p_frontend->info.caps & FE_CAN_BANDWIDTH_AUTO)
        msg_Dbg(p_access, "  bandwidth mode auto");
    if( p_frontend->info.caps & FE_CAN_GUARD_INTERVAL_AUTO)
        msg_Dbg(p_access, "  guard interval mode auto");
    if( p_frontend->info.caps & FE_CAN_HIERARCHY_AUTO)
        msg_Dbg(p_access, "  hierarchy mode auto");
    if( p_frontend->info.caps & FE_CAN_MUTE_TS)
        msg_Dbg(p_access, "  card can mute TS");
598
599
    if( p_frontend->info.caps & FE_CAN_RECOVER)
        msg_Dbg(p_access, "  card can recover from a cable unplug");
600
601
602
    msg_Dbg(p_access, "End of capability list");

    return VLC_SUCCESS;
603
604
605
606
607
}

/*****************************************************************************
 * Decoding the DVB parameters (common)
 *****************************************************************************/
608
static fe_spectral_inversion_t DecodeInversion( access_t *p_access )
609
610
611
612
{
    vlc_value_t         val;
    fe_spectral_inversion_t fe_inversion = 0;

613
614
    var_Get( p_access, "dvb-inversion", &val );
    msg_Dbg( p_access, "using inversion=%d", val.i_int );
615

616
    switch( val.i_int )
617
618
619
620
621
    {
        case 0: fe_inversion = INVERSION_OFF; break;
        case 1: fe_inversion = INVERSION_ON; break;
        case 2: fe_inversion = INVERSION_AUTO; break;
        default:
622
            msg_Dbg( p_access, "dvb has inversion not set, using auto");
623
624
625
626
627
628
            fe_inversion = INVERSION_AUTO;
            break;
    }
    return fe_inversion;
}

629
static fe_code_rate_t DecodeFEC( access_t *p_access, int i_val )
630
631
632
{
    fe_code_rate_t      fe_fec = FEC_NONE;

633
    msg_Dbg( p_access, "using fec=%d", i_val );
634

635
    switch( i_val )
636
    {
637
        case 0: fe_fec = FEC_NONE; break;
638
639
640
641
642
643
644
645
646
647
648
649
        case 1: fe_fec = FEC_1_2; break;
        case 2: fe_fec = FEC_2_3; break;
        case 3: fe_fec = FEC_3_4; break;
        case 4: fe_fec = FEC_4_5; break;
        case 5: fe_fec = FEC_5_6; break;
        case 6: fe_fec = FEC_6_7; break;
        case 7: fe_fec = FEC_7_8; break;
        case 8: fe_fec = FEC_8_9; break;
        case 9: fe_fec = FEC_AUTO; break;
        default:
            /* cannot happen */
            fe_fec = FEC_NONE;
650
            msg_Err( p_access, "argument has invalid FEC (%d)", i_val);
651
            break;
652
    }
653
654
655
    return fe_fec;
}

656
static fe_modulation_t DecodeModulation( access_t *p_access )
657
658
659
660
{
    vlc_value_t         val;
    fe_modulation_t     fe_modulation = 0;

661
    var_Get( p_access, "dvb-modulation", &val );
662

663
    switch( val.i_int )
664
665
666
667
668
669
670
671
672
    {
        case -1: fe_modulation = QPSK; break;
        case 0: fe_modulation = QAM_AUTO; break;
        case 16: fe_modulation = QAM_16; break;
        case 32: fe_modulation = QAM_32; break;
        case 64: fe_modulation = QAM_64; break;
        case 128: fe_modulation = QAM_128; break;
        case 256: fe_modulation = QAM_256; break;
        default:
673
            msg_Dbg( p_access, "terrestrial/cable dvb has constellation/modulation not set, using auto");
674
675
            fe_modulation = QAM_AUTO;
            break;
676
    }
677
678
679
680
681
682
    return fe_modulation;
}

/*****************************************************************************
 * FrontendSetQPSK : controls the FE device
 *****************************************************************************/
683
static fe_sec_voltage_t DecodeVoltage( access_t *p_access )
684
685
686
687
{
    vlc_value_t         val;
    fe_sec_voltage_t    fe_voltage;

688
689
    var_Get( p_access, "dvb-voltage", &val );
    msg_Dbg( p_access, "using voltage=%d", val.i_int );
690

691
    switch( val.i_int )
692
693
694
695
696
697
    {
        case 0: fe_voltage = SEC_VOLTAGE_OFF; break;
        case 13: fe_voltage = SEC_VOLTAGE_13; break;
        case 18: fe_voltage = SEC_VOLTAGE_18; break;
        default:
            fe_voltage = SEC_VOLTAGE_OFF;
698
            msg_Err( p_access, "argument has invalid voltage (%d)", val.i_int );
699
            break;
700
    }
701
702
703
    return fe_voltage;
}

704
static fe_sec_tone_mode_t DecodeTone( access_t *p_access )
705
706
707
708
{
    vlc_value_t         val;
    fe_sec_tone_mode_t  fe_tone;

709
710
    var_Get( p_access, "dvb-tone", &val );
    msg_Dbg( p_access, "using tone=%d", val.i_int );
711

712
    switch( val.i_int )
713
714
715
716
717
    {
        case 0: fe_tone = SEC_TONE_OFF; break;
        case 1: fe_tone = SEC_TONE_ON; break;
        default:
            fe_tone = SEC_TONE_OFF;
718
            msg_Err( p_access, "argument has invalid tone mode (%d)", val.i_int);
719
            break;
720
    }
721
722
723
724
725
726
727
728
729
    return fe_tone;
}

struct diseqc_cmd_t
{
    struct dvb_diseqc_master_cmd cmd;
    uint32_t wait;
};

730
static int DoDiseqc( access_t *p_access )
731
{
732
    access_sys_t *p_sys = p_access->p_sys;
733
734
735
736
737
738
    vlc_value_t val;
    int i_frequency, i_lnb_slof;
    fe_sec_voltage_t fe_voltage;
    fe_sec_tone_mode_t fe_tone;
    int i_err;

739
    var_Get( p_access, "dvb-frequency", &val );
740
    i_frequency = val.i_int;
741
    var_Get( p_access, "dvb-lnb-slof", &val );
742
743
    i_lnb_slof = val.i_int;

744
745
    var_Get( p_access, "dvb-tone", &val );
    if( val.i_int == -1 /* auto */ )
746
    {
747
        if( i_frequency >= i_lnb_slof )
748
749
750
            val.i_int = 1;
        else
            val.i_int = 0;
751
        var_Set( p_access, "dvb-tone", val );
752
753
    }

754
755
    fe_voltage = DecodeVoltage( p_access );
    fe_tone = DecodeTone( p_access );
756

Christophe Massiot's avatar
Christophe Massiot committed
757
758
759
760
761
762
763
764
765
766
767
    /* Switch off continuous tone. */
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_TONE, SEC_TONE_OFF )) < 0 )
    {
        msg_Err( p_access, "ioctl FE_SET_TONE failed, tone=%s (%d) %s",
                 fe_tone == SEC_TONE_ON ? "on" : "off", i_err,
                 strerror(errno) );
        return i_err;
    }

    /* Configure LNB voltage. */
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_VOLTAGE, fe_voltage )) < 0 )
768
    {
769
        msg_Err( p_access, "ioctl FE_SET_VOLTAGE failed, voltage=%d (%d) %s",
770
771
772
773
                 fe_voltage, i_err, strerror(errno) );
        return i_err;
    }

Christophe Massiot's avatar
Christophe Massiot committed
774
775
776
777
778
779
780
781
782
783
784
785
    var_Get( p_access, "dvb-high-voltage", &val );
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_ENABLE_HIGH_LNB_VOLTAGE,
                        val.b_bool )) < 0 && val.b_bool )
    {
        msg_Err( p_access,
                 "ioctl FE_ENABLE_HIGH_LNB_VOLTAGE failed, val=%d (%d) %s",
                 val.b_bool, i_err, strerror(errno) );
    }

    /* Wait for at least 15 ms. */
    msleep(15000);

786
    var_Get( p_access, "dvb-satno", &val );
Christophe Massiot's avatar
Christophe Massiot committed
787
    if( val.i_int > 0 && val.i_int < 5 )
788
789
    {
        /* digital satellite equipment control,
790
         * specification is available from http://www.eutelsat.com/
791
         */
Christophe Massiot's avatar
Christophe Massiot committed
792
793
794
795
796
797
798
799
800
801
802
803
804
805

        /* 1.x compatible equipment */
        struct diseqc_cmd_t cmd =  { {{0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00}, 4}, 0 };

        /* param: high nibble: reset bits, low nibble set bits,
         * bits are: option, position, polarization, band
         */
        cmd.cmd.msg[3] = 0xf0 /* reset bits */
                          | (((val.i_int - 1) * 4) & 0xc)
                          | (fe_voltage == SEC_VOLTAGE_13 ? 0 : 2)
                          | (fe_tone == SEC_TONE_ON ? 1 : 0);

        if( (i_err = ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_MASTER_CMD,
                           &cmd.cmd )) < 0 )
806
        {
Christophe Massiot's avatar
Christophe Massiot committed
807
            msg_Err( p_access, "ioctl FE_SEND_MASTER_CMD failed (%d) %s",
808
809
810
811
                     i_err, strerror(errno) );
            return i_err;
        }

Christophe Massiot's avatar
Christophe Massiot committed
812
        msleep(15000 + cmd.wait * 1000);
813

Christophe Massiot's avatar
Christophe Massiot committed
814
815
816
        /* A or B simple diseqc ("diseqc-compatible") */
        if( (i_err = ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_BURST,
                      ((val.i_int - 1) % 2) ? SEC_MINI_B : SEC_MINI_A )) < 0 )
817
        {
Christophe Massiot's avatar
Christophe Massiot committed
818
819
820
            msg_Err( p_access, "ioctl FE_SEND_BURST failed (%d) %s",
                     i_err, strerror(errno) );
            return i_err;
821
822
823
824
825
        }

        msleep(15000);
    }

Christophe Massiot's avatar
Christophe Massiot committed
826
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_TONE, fe_tone )) < 0 )
827
    {
828
        msg_Err( p_access, "ioctl FE_SET_TONE failed, tone=%s (%d) %s",
829
830
831
832
833
                 fe_tone == SEC_TONE_ON ? "on" : "off", i_err,
                 strerror(errno) );
        return i_err;
    }

Christophe Massiot's avatar
Christophe Massiot committed
834
    msleep(50000);
835
836
837
    return 0;
}

838
static int FrontendSetQPSK( access_t *p_access )
839
{
840
    access_sys_t *p_sys = p_access->p_sys;
841
842
843
    struct dvb_frontend_parameters fep;
    int i_ret;
    vlc_value_t val;
844
    int i_frequency, i_lnb_slof = 0, i_lnb_lof1, i_lnb_lof2 = 0;
845
846

    /* Prepare the fep structure */
847
    var_Get( p_access, "dvb-frequency", &val );
848
849
    i_frequency = val.i_int;

850
851
852
853
    var_Get( p_access, "dvb-lnb-lof1", &val );
    if ( val.i_int == 0 )
    {
        /* Automatic mode. */
854
855
856
857
858
859
        if ( i_frequency >= 950000 && i_frequency <= 2150000 )
        {
            msg_Dbg( p_access, "frequency %d is in IF-band", i_frequency );
            i_lnb_lof1 = 0;
        }
        else if ( i_frequency >= 2500000 && i_frequency <= 2700000 )
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
        {
            msg_Dbg( p_access, "frequency %d is in S-band", i_frequency );
            i_lnb_lof1 = 3650000;
        }
        else if ( i_frequency >= 3400000 && i_frequency <= 4200000 )
        {
            msg_Dbg( p_access, "frequency %d is in C-band (lower)",
                     i_frequency );
            i_lnb_lof1 = 5150000;
        }
        else if ( i_frequency >= 4500000 && i_frequency <= 4800000 )
        {
            msg_Dbg( p_access, "frequency %d is in C-band (higher)",
                     i_frequency );
            i_lnb_lof1 = 5950000;
        }
        else if ( i_frequency >= 10700000 && i_frequency <= 13250000 )
        {
            msg_Dbg( p_access, "frequency %d is in Ku-band",
                     i_frequency );
            i_lnb_lof1 = 9750000;
            i_lnb_lof2 = 10600000;
            i_lnb_slof = 11700000;
        }
        else
        {
            msg_Err( p_access, "frequency %d is out of any known band",
                     i_frequency );
            msg_Err( p_access, "specify dvb-lnb-lof1 manually for the local "
                     "oscillator frequency" );
            return VLC_EGENERIC;
        }
        val.i_int = i_lnb_lof1;
        var_Set( p_access, "dvb-lnb-lof1", val );
        val.i_int = i_lnb_lof2;
        var_Set( p_access, "dvb-lnb-lof2", val );
        val.i_int = i_lnb_slof;
        var_Set( p_access, "dvb-lnb-slof", val );
    }
    else
    {
        i_lnb_lof1 = val.i_int;
902
        var_Get( p_access, "dvb-lnb-lof2", &val );
903
904
905
906
907
908
909
910
911
        i_lnb_lof2 = val.i_int;
        var_Get( p_access, "dvb-lnb-slof", &val );
        i_lnb_slof = val.i_int;
    }

    if( i_lnb_slof && i_frequency >= i_lnb_slof )
    {
        i_frequency -= i_lnb_lof2;
    }
912
    else
913
914
915
916
    {
        i_frequency -= i_lnb_lof1;
    }
    fep.frequency = i_frequency >= 0 ? i_frequency : -i_frequency;
917

918
    fep.inversion = DecodeInversion( p_access );
919

920
    var_Get( p_access, "dvb-srate", &val );
921
922
    fep.u.qpsk.symbol_rate = val.i_int;

923
924
    var_Get( p_access, "dvb-fec", &val );
    fep.u.qpsk.fec_inner = DecodeFEC( p_access, val.i_int );
925

926
    if( DoDiseqc( p_access ) < 0 )
927
    {
928
        return VLC_EGENERIC;
929
930
931
    }

    /* Empty the event queue */
932
    for( ; ; )
933
934
    {
        struct dvb_frontend_event event;
Christophe Massiot's avatar
Christophe Massiot committed
935
936
        if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
              && errno == EWOULDBLOCK )
937
938
939
940
            break;
    }

    /* Now send it all to the frontend device */
Christophe Massiot's avatar
Christophe Massiot committed
941
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep )) < 0 )
942
    {
943
        msg_Err( p_access, "DVB-S: setting frontend failed (%d) %s", i_ret,
944
                 strerror(errno) );
945
        return VLC_EGENERIC;
946
947
    }

Christophe Massiot's avatar
Christophe Massiot committed
948
    return VLC_SUCCESS;
949
950
951
952
953
}

/*****************************************************************************
 * FrontendSetQAM : controls the FE device
 *****************************************************************************/
954
static int FrontendSetQAM( access_t *p_access )
955
{
956
    access_sys_t *p_sys = p_access->p_sys;
957
958
959
960
961
962
    struct dvb_frontend_parameters fep;
    vlc_value_t val;
    int i_ret;

    /* Prepare the fep structure */

963
    var_Get( p_access, "dvb-frequency", &val );
964
965
    fep.frequency = val.i_int;

966
    fep.inversion = DecodeInversion( p_access );
967

968
    var_Get( p_access, "dvb-srate", &val );
969
970
    fep.u.qam.symbol_rate = val.i_int;

971
972
    var_Get( p_access, "dvb-fec", &val );
    fep.u.qam.fec_inner = DecodeFEC( p_access, val.i_int );
973

974
    fep.u.qam.modulation = DecodeModulation( p_access );
975

976
    /* Empty the event queue */
977
    for( ; ; )
978
979
    {
        struct dvb_frontend_event event;
Christophe Massiot's avatar
Christophe Massiot committed
980
981
        if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
              && errno == EWOULDBLOCK )
982
983
984
            break;
    }

985
    /* Now send it all to the frontend device */
Christophe Massiot's avatar
Christophe Massiot committed
986
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep )) < 0 )
987
    {
988
        msg_Err( p_access, "DVB-C: setting frontend failed (%d) %s", i_ret,
989
                 strerror(errno) );
990
        return VLC_EGENERIC;
991
992
    }

Christophe Massiot's avatar
Christophe Massiot committed
993
    return VLC_SUCCESS;
994
995
996
997
998
}

/*****************************************************************************
 * FrontendSetOFDM : controls the FE device
 *****************************************************************************/
999
static fe_bandwidth_t DecodeBandwidth( access_t *p_access )
1000
1001
1002
1003
{
    vlc_value_t         val;
    fe_bandwidth_t      fe_bandwidth = 0;

1004
1005
    var_Get( p_access, "dvb-bandwidth", &val );
    msg_Dbg( p_access, "using bandwidth=%d", val.i_int );
1006

1007
    switch( val.i_int )
1008
1009
1010
1011
1012
1013
    {
        case 0: fe_bandwidth = BANDWIDTH_AUTO; break;
        case 6: fe_bandwidth = BANDWIDTH_6_MHZ; break;
        case 7: fe_bandwidth = BANDWIDTH_7_MHZ; break;
        case 8: fe_bandwidth = BANDWIDTH_8_MHZ; break;
        default:
1014
            msg_Dbg( p_access, "terrestrial dvb has bandwidth not set, using auto" );
1015
1016
1017
1018
1019
1020
            fe_bandwidth = BANDWIDTH_AUTO;
            break;
    }
    return fe_bandwidth;
}

1021
static fe_transmit_mode_t DecodeTransmission( access_t *p_access )
1022
1023
1024
1025
{
    vlc_value_t         val;
    fe_transmit_mode_t  fe_transmission = 0;

1026
1027
    var_Get( p_access, "dvb-transmission", &val );
    msg_Dbg( p_access, "using transmission=%d", val.i_int );
1028

1029
    switch( val.i_int )
1030
1031
1032
1033
1034
    {
        case 0: fe_transmission = TRANSMISSION_MODE_AUTO; break;
        case 2: fe_transmission = TRANSMISSION_MODE_2K; break;
        case 8: fe_transmission = TRANSMISSION_MODE_8K; break;
        default:
1035
            msg_Dbg( p_access, "terrestrial dvb has transmission mode not set, using auto");
1036
1037
            fe_transmission = TRANSMISSION_MODE_AUTO;
            break;
1038
    }
1039
1040
1041
    return fe_transmission;
}

1042
static fe_guard_interval_t DecodeGuardInterval( access_t *p_access )
1043
1044
1045
1046
{
    vlc_value_t         val;
    fe_guard_interval_t fe_guard = 0;

1047
1048
    var_Get( p_access, "dvb-guard", &val );
    msg_Dbg( p_access, "using guard=%d", val.i_int );
1049

1050
    switch( val.i_int )
1051
1052
1053
1054
1055
1056
1057
    {
        case 0: fe_guard = GUARD_INTERVAL_AUTO; break;
        case 4: fe_guard = GUARD_INTERVAL_1_4; break;
        case 8: fe_guard = GUARD_INTERVAL_1_8; break;
        case 16: fe_guard = GUARD_INTERVAL_1_16; break;
        case 32: fe_guard = GUARD_INTERVAL_1_32; break;
        default:
1058
            msg_Dbg( p_access, "terrestrial dvb has guard interval not set, using auto");
1059
1060
1061
1062
1063
1064
            fe_guard = GUARD_INTERVAL_AUTO;
            break;
    }
    return fe_guard;
}

1065
static fe_hierarchy_t DecodeHierarchy( access_t *p_access )
1066
1067
1068
1069
{
    vlc_value_t         val;
    fe_hierarchy_t      fe_hierarchy = 0;

1070
1071
    var_Get( p_access, "dvb-hierarchy", &val );
    msg_Dbg( p_access, "using hierarchy=%d", val.i_int );
1072

1073
    switch( val.i_int )
1074
1075
1076
1077
1078
1079
1080
    {
        case -1: fe_hierarchy = HIERARCHY_NONE; break;
        case 0: fe_hierarchy = HIERARCHY_AUTO; break;
        case 1: fe_hierarchy = HIERARCHY_1; break;
        case 2: fe_hierarchy = HIERARCHY_2; break;
        case 4: fe_hierarchy = HIERARCHY_4; break;
        default:
1081
            msg_Dbg( p_access, "terrestrial dvb has hierarchy not set, using auto");
1082
1083
1084
1085
1086
1087
            fe_hierarchy = HIERARCHY_AUTO;
            break;
    }
    return fe_hierarchy;
}

1088
static int FrontendSetOFDM( access_t * p_access )
1089
{
1090
    access_sys_t *p_sys = p_access->p_sys;
1091
1092
1093
1094
1095
1096
    struct dvb_frontend_parameters fep;
    vlc_value_t val;
    int ret;

    /* Prepare the fep structure */

1097
    var_Get( p_access, "dvb-frequency", &val );
1098
1099
    fep.frequency = val.i_int;

1100
    fep.inversion = DecodeInversion( p_access );
1101

1102
1103