linux_dvb.c 43.3 KB
Newer Older
1
/*****************************************************************************
2
 * linux_dvb.c : functions to control a DVB card under Linux with v4l2
3
 *****************************************************************************
David Kaplan's avatar
David Kaplan committed
4
 * Copyright (C) 1998-2010 the VideoLAN team
5
6
7
 *
 * Authors: Damien Lucas <nitrox@via.ecp.fr>
 *          Johan Bilien <jobi@via.ecp.fr>
Jean-Paul Saman's avatar
Jean-Paul Saman committed
8
 *          Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
9
10
 *          Christopher Ross <chris@tebibyte.org>
 *          Christophe Massiot <massiot@via.ecp.fr>
David Kaplan's avatar
David Kaplan committed
11
 *          David Kaplan <david@of1.org>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 *
 * 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.
 *****************************************************************************/

28
29
30
31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
zorglub's avatar
zorglub committed
33
#include <vlc_access.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <vlc_fs.h>
35

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
36
#include <errno.h>
37
#include <sys/types.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38
#include <sys/ioctl.h>
39
40
#include <fcntl.h>
#include <unistd.h>
41
#include <poll.h>
42
43
44
45
46
47

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

Christophe Massiot's avatar
Christophe Massiot committed
48
#include "dvb.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
49
#include "scan.h"
50

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
52
53
54
#define DMX      "/dev/dvb/adapter%d/demux%d"
#define FRONTEND "/dev/dvb/adapter%d/frontend%d"
#define DVR      "/dev/dvb/adapter%d/dvr%d"

55
56
57
/*
 * Frontends
 */
58
59
struct frontend_t
{
Christophe Massiot's avatar
Christophe Massiot committed
60
    fe_status_t i_last_status;
61
    struct dvb_frontend_info info;
62
};
63

64
65
#define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */

66
/* Local prototypes */
67
68
69
70
static int FrontendInfo( access_t * );
static int FrontendSetQPSK( access_t * );
static int FrontendSetQAM( access_t * );
static int FrontendSetOFDM( access_t * );
71
static int FrontendSetATSC( access_t * );
72
73
74
75

/*****************************************************************************
 * FrontendOpen : Determine frontend device information and capabilities
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
76
int FrontendOpen( access_t *p_access )
77
{
78
    access_sys_t *p_sys = p_access->p_sys;
79
80
    frontend_t * p_frontend;
    unsigned int i_adapter, i_device;
81
    bool b_probe;
82
83
    char frontend[128];

84
85
86
    i_adapter = var_GetInteger( p_access, "dvb-adapter" );
    i_device = var_GetInteger( p_access, "dvb-device" );
    b_probe = var_GetBool( p_access, "dvb-probe" );
87

88
    if( snprintf( frontend, sizeof(frontend), FRONTEND, i_adapter, i_device ) >= (int)sizeof(frontend) )
89
    {
90
        msg_Err( p_access, "snprintf() truncated string for FRONTEND" );
91
92
93
        frontend[sizeof(frontend) - 1] = '\0';
    }

94
    p_sys->p_frontend = p_frontend = malloc( sizeof(frontend_t) );
ivoire's avatar
ivoire committed
95
96
    if( !p_frontend )
        return VLC_ENOMEM;
97

98
    msg_Dbg( p_access, "Opening device %s", frontend );
99
    if( (p_sys->i_frontend_handle = vlc_open(frontend, O_RDWR | O_NONBLOCK)) < 0 )
100
    {
101
        msg_Err( p_access, "FrontEndOpen: opening device failed (%m)" );
102
        free( p_frontend );
103
        return VLC_EGENERIC;
104
105
    }

106
    if( b_probe )
107
    {
zorglub's avatar
zorglub committed
108
109
        const char * psz_expected = NULL;
        const char * psz_real;
110

111
        if( FrontendInfo( p_access ) < 0 )
112
        {
Christophe Massiot's avatar
Christophe Massiot committed
113
            close( p_sys->i_frontend_handle );
114
            free( p_frontend );
115
            return VLC_EGENERIC;
116
117
        }

118
        switch( p_frontend->info.type )
119
120
121
122
123
124
125
126
127
128
        {
        case FE_OFDM:
            psz_real = "DVB-T";
            break;
        case FE_QAM:
            psz_real = "DVB-C";
            break;
        case FE_QPSK:
            psz_real = "DVB-S";
            break;
129
130
131
        case FE_ATSC:
            psz_real = "ATSC";
            break;
132
133
134
        default:
            psz_real = "unknown";
        }
135

136
        /* Sanity checks */
137
138
139
        if( (!strncmp( p_access->psz_access, "qpsk", 4 ) ||
             !strncmp( p_access->psz_access, "dvb-s", 5 ) ||
             !strncmp( p_access->psz_access, "satellite", 9 ) ) &&
140
141
142
143
             (p_frontend->info.type != FE_QPSK) )
        {
            psz_expected = "DVB-S";
        }
144
145
        if( (!strncmp( p_access->psz_access, "cable", 5 ) ||
             !strncmp( p_access->psz_access, "dvb-c", 5 ) ) &&
146
147
148
149
             (p_frontend->info.type != FE_QAM) )
        {
            psz_expected = "DVB-C";
        }
150
151
        if( (!strncmp( p_access->psz_access, "terrestrial", 11 ) ||
             !strncmp( p_access->psz_access, "dvb-t", 5 ) ) &&
152
153
154
155
156
             (p_frontend->info.type != FE_OFDM) )
        {
            psz_expected = "DVB-T";
        }

157
158
159
160
161
162
163
        if( (!strncmp( p_access->psz_access, "usdigital", 9 ) ||
             !strncmp( p_access->psz_access, "atsc", 4 ) ) &&
             (p_frontend->info.type != FE_ATSC) )
        {
            psz_expected = "ATSC";
        }

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

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

193
    return VLC_SUCCESS;
194
195
196
197
198
}

/*****************************************************************************
 * FrontendClose : Close the frontend
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
199
void 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 !
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
int 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 ) )
224
        {
225
            msg_Err( p_access, "DVB-S tuning error" );
226
            return VLC_EGENERIC;
227
228
        }
        break;
229

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

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

248
249
    /* ATSC */
    case FE_ATSC:
250
        if( FrontendSetATSC( p_access ) )
251
        {
252
            msg_Err( p_access, "ATSC tuning error" );
253
254
255
256
            return VLC_EGENERIC;
        }
        break;

257
    default:
258
        msg_Err( p_access, "tuner type %s not supported",
259
260
                 p_sys->p_frontend->info.name );
        return VLC_EGENERIC;
261
    }
Christophe Massiot's avatar
Christophe Massiot committed
262
    p_sys->p_frontend->i_last_status = 0;
263
    p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
264
    return VLC_SUCCESS;
265
266
}

Christophe Massiot's avatar
Christophe Massiot committed
267
268
269
/*****************************************************************************
 * FrontendPoll : Poll for frontend events
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
270
void FrontendPoll( access_t *p_access )
Christophe Massiot's avatar
Christophe Massiot committed
271
272
273
274
275
276
{
    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;

277
    for( ;; )
Christophe Massiot's avatar
Christophe Massiot committed
278
    {
279
        if( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0 )
280
        {
281
282
            if( errno != EWOULDBLOCK )
                msg_Err( p_access, "frontend event error: %m" );
283
            return;
284
285
286
287
288
        }

        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
289

290
        {
Christophe Massiot's avatar
Christophe Massiot committed
291
#define IF_UP( x )                                                          \
292
293
294
295
        }                                                                   \
        if ( i_diff & (x) )                                                 \
        {                                                                   \
            if ( i_status & (x) )
Christophe Massiot's avatar
Christophe Massiot committed
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
            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 )
            {
Laurent Aimar's avatar
Laurent Aimar committed
319
320
                frontend_statistic_t stat;

321
322
323
324
                msg_Dbg( p_access, "frontend has acquired lock" );
                p_sys->i_frontend_timeout = 0;

                /* Read some statistics */
Laurent Aimar's avatar
Laurent Aimar committed
325
326
327
328
329
330
331
332
333
                if( !FrontendGetStatistic( p_access, &stat ) )
                {
                    if( stat.i_ber >= 0 )
                        msg_Dbg( p_access, "- Bit error rate: %d", stat.i_ber );
                    if( stat.i_signal_strenth >= 0 )
                        msg_Dbg( p_access, "- Signal strength: %d", stat.i_signal_strenth );
                    if( stat.i_snr >= 0 )
                        msg_Dbg( p_access, "- SNR: %d", stat.i_snr );
                }
334
335
336
337
338
339
340
341
342
343
344
            }
            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");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
345
                FrontendSet( p_access );
346
            }
Christophe Massiot's avatar
Christophe Massiot committed
347
        }
348
#undef IF_UP
Christophe Massiot's avatar
Christophe Massiot committed
349
350
    }
}
David Kaplan's avatar
David Kaplan committed
351

Laurent Aimar's avatar
Laurent Aimar committed
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
int FrontendGetStatistic( access_t *p_access, frontend_statistic_t *p_stat )
{
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t * p_frontend = p_sys->p_frontend;

    if( (p_frontend->i_last_status & FE_HAS_LOCK) == 0 )
        return VLC_EGENERIC;

    memset( p_stat, 0, sizeof(*p_stat) );
    if( ioctl( p_sys->i_frontend_handle, FE_READ_BER, &p_stat->i_ber ) < 0 )
        p_stat->i_ber = -1;
    if( ioctl( p_sys->i_frontend_handle, FE_READ_SIGNAL_STRENGTH, &p_stat->i_signal_strenth ) < 0 )
        p_stat->i_signal_strenth = -1;
    if( ioctl( p_sys->i_frontend_handle, FE_READ_SNR, &p_stat->i_snr ) < 0 )
        p_stat->i_snr = -1;

    return VLC_SUCCESS;
}
David Kaplan's avatar
David Kaplan committed
370

Laurent Aimar's avatar
Laurent Aimar committed
371
372
373
374
375
376
377
378
379
void FrontendGetStatus( access_t *p_access, frontend_status_t *p_status )
{
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t * p_frontend = p_sys->p_frontend;

    p_status->b_has_signal = (p_frontend->i_last_status & FE_HAS_SIGNAL) != 0;
    p_status->b_has_carrier = (p_frontend->i_last_status & FE_HAS_CARRIER) != 0;
    p_status->b_has_lock = (p_frontend->i_last_status & FE_HAS_LOCK) != 0;
}
David Kaplan's avatar
David Kaplan committed
380
381
382
383
384
385
386
387
388
389

static int ScanParametersDvbS( access_t *p_access, scan_parameter_t *p_scan )
{
    const frontend_t *p_frontend = p_access->p_sys->p_frontend;

    memset( p_scan, 0, sizeof(*p_scan) );
    p_scan->type = SCAN_DVB_S;

    p_scan->frequency.i_min = p_frontend->info.frequency_min;
    p_scan->frequency.i_max = p_frontend->info.frequency_max;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
390
391
    /* set satellite config file path */
    p_scan->sat_info.psz_name = var_InheritString( p_access, "dvb-satellite" );
David Kaplan's avatar
David Kaplan committed
392
393
394
395

    return VLC_SUCCESS;
}

396
397
398
399
400
401
402
403
404
405
406
static int ScanParametersDvbC( access_t *p_access, scan_parameter_t *p_scan )
{
    const frontend_t *p_frontend = p_access->p_sys->p_frontend;

    memset( p_scan, 0, sizeof(*p_scan) );
    p_scan->type = SCAN_DVB_C;
    p_scan->b_exhaustive = false;

    /* */
    p_scan->frequency.i_min = p_frontend->info.frequency_min;
    p_scan->frequency.i_max = p_frontend->info.frequency_max;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
407
408
    p_scan->frequency.i_step = p_frontend->info.frequency_stepsize
        ? p_frontend->info.frequency_stepsize : 166667;
409
410
    p_scan->frequency.i_count = (p_scan->frequency.i_max-p_scan->frequency.i_min)/p_scan->frequency.i_step;

Konstantin Pavlov's avatar
Konstantin Pavlov committed
411
    /* if user supplies modulation or frontend can do auto, don't scan them */
412
413
414
415
416
417
418
419
420
421
    if( var_GetInteger( p_access, "dvb-modulation" ) ||
        p_frontend->info.caps & FE_CAN_QAM_AUTO )
    {
        p_scan->b_modulation_set = true;
    } else {
        p_scan->b_modulation_set = false;
        /* our scanning code flips modulation from 16..256 automaticly*/
        p_scan->i_modulation = 0;
    }

422
423
424
425
426
427
    /* if user supplies symbolrate, don't scan those */
    if( var_GetInteger( p_access, "dvb-srate" ) )
        p_scan->b_symbolrate_set = true;
    else
        p_scan->b_symbolrate_set = false;

428
429
430
431
432
433
434
    /* */
    p_scan->bandwidth.i_min  = 6;
    p_scan->bandwidth.i_max  = 8;
    p_scan->bandwidth.i_step = 1;
    p_scan->bandwidth.i_count = 3;
    return VLC_SUCCESS;
}
David Kaplan's avatar
David Kaplan committed
435

Laurent Aimar's avatar
Laurent Aimar committed
436
437
438
439
440
441
442
443
444
445
446
static int ScanParametersDvbT( access_t *p_access, scan_parameter_t *p_scan )
{
    const frontend_t *p_frontend = p_access->p_sys->p_frontend;

    memset( p_scan, 0, sizeof(*p_scan) );
    p_scan->type = SCAN_DVB_T;
    p_scan->b_exhaustive = false;

    /* */
    p_scan->frequency.i_min = p_frontend->info.frequency_min;
    p_scan->frequency.i_max = p_frontend->info.frequency_max;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
447
448
    p_scan->frequency.i_step = p_frontend->info.frequency_stepsize
        ? p_frontend->info.frequency_stepsize : 166667;
Laurent Aimar's avatar
Laurent Aimar committed
449
450
451
452
453
454
455
456
457
    p_scan->frequency.i_count = (p_scan->frequency.i_max-p_scan->frequency.i_min)/p_scan->frequency.i_step;

    /* */
    p_scan->bandwidth.i_min  = 6;
    p_scan->bandwidth.i_max  = 8;
    p_scan->bandwidth.i_step = 1;
    p_scan->bandwidth.i_count = 3;
    return VLC_SUCCESS;
}
David Kaplan's avatar
David Kaplan committed
458

Laurent Aimar's avatar
Laurent Aimar committed
459
460
461
462
463
int  FrontendGetScanParameter( access_t *p_access, scan_parameter_t *p_scan )
{
    access_sys_t *p_sys = p_access->p_sys;
    const frontend_t *p_frontend = p_sys->p_frontend;

David Kaplan's avatar
David Kaplan committed
464
    if( p_frontend->info.type == FE_OFDM )              /* DVB-T */
Laurent Aimar's avatar
Laurent Aimar committed
465
        return ScanParametersDvbT( p_access, p_scan );
David Kaplan's avatar
David Kaplan committed
466
    else if( p_frontend->info.type == FE_QAM )          /* DVB-C */
467
        return ScanParametersDvbC( p_access, p_scan );
David Kaplan's avatar
David Kaplan committed
468
469
    else if( p_frontend->info.type == FE_QPSK )
        return ScanParametersDvbS( p_access, p_scan );  /* DVB-S */
Laurent Aimar's avatar
Laurent Aimar committed
470

471
    msg_Err( p_access, "frontend scanning not supported" );
Laurent Aimar's avatar
Laurent Aimar committed
472
473
    return VLC_EGENERIC;
}
474

475
476
477
/*****************************************************************************
 * FrontendInfo : Return information about given frontend
 *****************************************************************************/
478
static int FrontendInfo( access_t *p_access )
479
{
480
481
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t *p_frontend = p_sys->p_frontend;
482
483

    /* Determine type of frontend */
484
    if( ioctl( p_sys->i_frontend_handle, FE_GET_INFO, &p_frontend->info ) < 0 )
485
    {
486
        msg_Err( p_access, "frontend info request error: %m" );
487
        return VLC_EGENERIC;
488
489
490
    }

    /* Print out frontend capabilities. */
491
492
493
    msg_Dbg(p_access, "Frontend Info:" );
    msg_Dbg(p_access, "  name = %s", p_frontend->info.name );
    switch( p_frontend->info.type )
494
495
    {
        case FE_QPSK:
496
            msg_Dbg( p_access, "  type = QPSK (DVB-S)" );
497
498
            break;
        case FE_QAM:
499
            msg_Dbg( p_access, "  type = QAM (DVB-C)" );
500
501
            break;
        case FE_OFDM:
502
            msg_Dbg( p_access, "  type = OFDM (DVB-T)" );
503
            break;
504
505
506
        case FE_ATSC:
            msg_Dbg( p_access, "  type = ATSC (USA)" );
            break;
507
508
#if 0 /* DVB_API_VERSION == 3 */
        case FE_MEMORY:
509
            msg_Dbg(p_access, "  type = MEMORY" );
510
511
            break;
        case FE_NET:
512
            msg_Dbg(p_access, "  type = NETWORK" );
513
514
515
            break;
#endif
        default:
516
            msg_Err( p_access, "  unknown frontend type (%d)",
517
                     p_frontend->info.type );
518
            return VLC_EGENERIC;
519
    }
520
    msg_Dbg(p_access, "  frequency_min = %u (kHz)",
521
            p_frontend->info.frequency_min);
522
    msg_Dbg(p_access, "  frequency_max = %u (kHz)",
523
            p_frontend->info.frequency_max);
524
    msg_Dbg(p_access, "  frequency_stepsize = %u",
525
            p_frontend->info.frequency_stepsize);
526
    msg_Dbg(p_access, "  frequency_tolerance = %u",
527
            p_frontend->info.frequency_tolerance);
528
    msg_Dbg(p_access, "  symbol_rate_min = %u (kHz)",
529
            p_frontend->info.symbol_rate_min);
530
    msg_Dbg(p_access, "  symbol_rate_max = %u (kHz)",
531
            p_frontend->info.symbol_rate_max);
532
    msg_Dbg(p_access, "  symbol_rate_tolerance (ppm) = %u",
533
            p_frontend->info.symbol_rate_tolerance);
534
    msg_Dbg(p_access, "  notifier_delay (ms) = %u",
535
536
            p_frontend->info.notifier_delay );

537
    msg_Dbg(p_access, "Frontend Info capability list:");
538
    if( p_frontend->info.caps == FE_IS_STUPID)
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
        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)
561
        msg_Dbg(p_access, "  QPSK modulation");
562
    if( p_frontend->info.caps & FE_CAN_QAM_16)
563
        msg_Dbg(p_access, "  QAM 16 modulation");
564
    if( p_frontend->info.caps & FE_CAN_QAM_32)
565
        msg_Dbg(p_access, "  QAM 32 modulation");
566
    if( p_frontend->info.caps & FE_CAN_QAM_64)
567
        msg_Dbg(p_access, "  QAM 64 modulation");
568
    if( p_frontend->info.caps & FE_CAN_QAM_128)
569
        msg_Dbg(p_access, "  QAM 128 modulation");
570
    if( p_frontend->info.caps & FE_CAN_QAM_256)
571
        msg_Dbg(p_access, "  QAM 256 modulation");
572
    if( p_frontend->info.caps & FE_CAN_QAM_AUTO)
573
        msg_Dbg(p_access, "  QAM auto modulation");
574
575
576
577
578
579
580
581
    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");
582
    if( p_frontend->info.caps & FE_CAN_8VSB)
583
        msg_Dbg(p_access, "  8-level VSB modulation");
584
    if( p_frontend->info.caps & FE_CAN_16VSB)
585
586
        msg_Dbg(p_access, "  16-level VSB modulation");
    if( p_frontend->info.caps & FE_HAS_EXTENDED_CAPS)
Rémi Denis-Courmont's avatar
Typo    
Rémi Denis-Courmont committed
587
        msg_Dbg(p_access, "  extended capabilities");
588
    /* 3 capabilities that don't exist yet HERE */
589
590
#if (DVB_API_VERSION > 5) \
 || ((DVB_API_VERSION == 5 && DVB_API_VERSION_MINOR >= 2))
591
592
593
594
595
596
597
598
599
600
601
602
603
    if( p_frontend->info.caps & FE_CAN_TURBO_FEC)
        msg_Dbg(p_access, "  Turbo FEC modulation");
#else
# warning Please update your Linux kernel headers!
#endif
    if( p_frontend->info.caps & FE_CAN_2G_MODULATION)
        msg_Dbg(p_access, "  2nd generation modulation (DVB-S2)");
    /* FE_NEEDS_BENDING is deprecated */
    if( p_frontend->info.caps & FE_CAN_RECOVER)
        msg_Dbg(p_access, "  cable unplug recovery");
    if( p_frontend->info.caps & FE_CAN_MUTE_TS)
        msg_Dbg(p_access, "  spurious TS muting");
   msg_Dbg(p_access, "End of capability list");
604
605

    return VLC_SUCCESS;
606
607
608
609
610
}

/*****************************************************************************
 * Decoding the DVB parameters (common)
 *****************************************************************************/
611
static fe_spectral_inversion_t DecodeInversion( access_t *p_access )
612
{
ivoire's avatar
ivoire committed
613
    int i_val;
614
615
    fe_spectral_inversion_t fe_inversion = 0;

ivoire's avatar
ivoire committed
616
617
    i_val = var_GetInteger( p_access, "dvb-inversion" );
    msg_Dbg( p_access, "using inversion=%d", i_val );
618

ivoire's avatar
ivoire committed
619
    switch( i_val )
620
621
622
623
624
    {
        case 0: fe_inversion = INVERSION_OFF; break;
        case 1: fe_inversion = INVERSION_ON; break;
        case 2: fe_inversion = INVERSION_AUTO; break;
        default:
625
            msg_Dbg( p_access, "dvb has inversion not set, using auto");
626
627
628
629
630
631
            fe_inversion = INVERSION_AUTO;
            break;
    }
    return fe_inversion;
}

632
static fe_code_rate_t DecodeFEC( access_t *p_access, const char *varname )
633
{
634
    switch( var_GetInteger(p_access, varname) )
635
    {
636
637
638
639
640
641
642
643
644
645
646
        case 0:  return FEC_NONE;
        case 1:  return FEC_1_2;
        case 2:  return FEC_2_3;
        case 3:  return FEC_3_4;
        case 4:  return FEC_4_5;
        case 5:  return FEC_5_6;
        case 6:  return FEC_6_7;
        case 7:  return FEC_7_8;
        case 8:  return FEC_8_9;
        case 9:  return FEC_AUTO;
        default: return FEC_NONE;
647
    }
648
649
}

650
651
static fe_modulation_t DecodeModulation( access_t *p_access,
                                         fe_modulation_t def )
652
653
654
655
{
    switch( var_GetInteger( p_access, "dvb-modulation" ) )
    {
        case -1:    return QPSK;
656
        case 0:     return QAM_AUTO;
657
        case 8:     return VSB_8;
658
        case 16:    return QAM_16;
659
660
661
662
        case 32:    return QAM_32;
        case 64:    return QAM_64;
        case 128:   return QAM_128;
        case 256:   return QAM_256;
663
        default:    return def;
664
    }
665
666
667
668
669
}

/*****************************************************************************
 * FrontendSetQPSK : controls the FE device
 *****************************************************************************/
670
static fe_sec_voltage_t DecodeVoltage( access_t *p_access )
671
{
672
    switch( var_GetInteger( p_access, "dvb-voltage" ) )
673
    {
674
675
676
677
        case 0:  return SEC_VOLTAGE_OFF;
        case 13: return SEC_VOLTAGE_13;
        case 18: return SEC_VOLTAGE_18;
        default: return SEC_VOLTAGE_OFF;
678
    }
679
680
}

681
static fe_sec_tone_mode_t DecodeTone( access_t *p_access )
682
{
683
    switch( var_GetInteger( p_access, "dvb-tone" ) )
684
    {
685
686
687
        case 0:  return SEC_TONE_OFF;
        case 1:  return SEC_TONE_ON;
        default: return SEC_TONE_OFF;
688
    }
689
690
691
692
693
694
695
696
}

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

697
static int DoDiseqc( access_t *p_access )
698
{
699
    access_sys_t *p_sys = p_access->p_sys;
ivoire's avatar
ivoire committed
700
701
    int i_val;
    bool b_val;
702
703
704
705
    int i_frequency, i_lnb_slof;
    fe_sec_voltage_t fe_voltage;
    fe_sec_tone_mode_t fe_tone;

ivoire's avatar
ivoire committed
706
707
    i_frequency = var_GetInteger( p_access, "dvb-frequency" );
    i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" );
708

ivoire's avatar
ivoire committed
709
710
    i_val = var_GetInteger( p_access, "dvb-tone" );
    if( i_val == -1 /* auto */ )
711
    {
712
        if( i_frequency >= i_lnb_slof )
ivoire's avatar
ivoire committed
713
            i_val = 1;
714
        else
ivoire's avatar
ivoire committed
715
716
            i_val = 0;
        var_SetInteger( p_access, "dvb-tone", i_val );
717
718
    }

719
720
    fe_voltage = DecodeVoltage( p_access );
    fe_tone = DecodeTone( p_access );
721

Christophe Massiot's avatar
Christophe Massiot committed
722
    /* Switch off continuous tone. */
723
    if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, SEC_TONE_OFF ) < 0 )
Christophe Massiot's avatar
Christophe Massiot committed
724
    {
725
726
        msg_Err( p_access, "switching tone %s error: %m", "off" );
        return VLC_EGENERIC;
Christophe Massiot's avatar
Christophe Massiot committed
727
728
729
    }

    /* Configure LNB voltage. */
730
    if( ioctl( p_sys->i_frontend_handle, FE_SET_VOLTAGE, fe_voltage ) < 0 )
731
    {
732
733
        msg_Err( p_access, "voltage error: %m" );
        return VLC_EGENERIC;
734
735
    }

ivoire's avatar
ivoire committed
736
    b_val = var_GetBool( p_access, "dvb-high-voltage" );
737
738
    if( ioctl( p_sys->i_frontend_handle,
               FE_ENABLE_HIGH_LNB_VOLTAGE, b_val ) < 0 && b_val )
Christophe Massiot's avatar
Christophe Massiot committed
739
    {
740
        msg_Err( p_access, "high LNB voltage error: %m" );
Christophe Massiot's avatar
Christophe Massiot committed
741
742
743
744
745
    }

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

ivoire's avatar
ivoire committed
746
747
    i_val = var_GetInteger( p_access, "dvb-satno" );
    if( i_val > 0 && i_val < 5 )
748
749
    {
        /* digital satellite equipment control,
750
         * specification is available from http://www.eutelsat.com/
751
         */
Christophe Massiot's avatar
Christophe Massiot committed
752
753
754
755
756
757
758
759

        /* 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 */
ivoire's avatar
ivoire committed
760
                          | (((i_val - 1) * 4) & 0xc)
Christophe Massiot's avatar
Christophe Massiot committed
761
762
763
                          | (fe_voltage == SEC_VOLTAGE_13 ? 0 : 2)
                          | (fe_tone == SEC_TONE_ON ? 1 : 0);

764
765
        if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_MASTER_CMD,
                   &cmd.cmd ) )
766
        {
767
768
            msg_Err( p_access, "master command sending error: %m" );
            return VLC_EGENERIC;
769
770
        }

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

Christophe Massiot's avatar
Christophe Massiot committed
773
        /* A or B simple diseqc ("diseqc-compatible") */
774
775
        if( ioctl( p_sys->i_frontend_handle, FE_DISEQC_SEND_BURST,
                  ((i_val - 1) % 2) ? SEC_MINI_B : SEC_MINI_A ) )
776
        {
777
778
            msg_Err( p_access, "burst sending error: %m" );
            return VLC_EGENERIC;
779
780
781
782
783
        }

        msleep(15000);
    }

784
    if( ioctl( p_sys->i_frontend_handle, FE_SET_TONE, fe_tone ) )
785
    {
786
787
788
        msg_Err( p_access, "switching tone %s error: %m",
                 (fe_tone == SEC_TONE_ON) ? "on" : "off" );
        return VLC_EGENERIC;
789
790
    }

Christophe Massiot's avatar
Christophe Massiot committed
791
    msleep(50000);
792
793
794
    return 0;
}

795
static int FrontendSetQPSK( access_t *p_access )
796
{
797
    access_sys_t *p_sys = p_access->p_sys;
798
    struct dvb_frontend_parameters fep;
ivoire's avatar
ivoire committed
799
    int i_val;
800
    int i_frequency, i_lnb_slof = 0, i_lnb_lof1, i_lnb_lof2 = 0;
801
802

    /* Prepare the fep structure */
ivoire's avatar
ivoire committed
803
    i_frequency = var_GetInteger( p_access, "dvb-frequency" );
804

ivoire's avatar
ivoire committed
805
806
    i_val = var_GetInteger( p_access, "dvb-lnb-lof1" );
    if( i_val == 0 )
807
808
    {
        /* Automatic mode. */
809
810
811
812
813
814
        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 )
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
        {
            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;
        }
ivoire's avatar
ivoire committed
847
848
849
        var_SetInteger( p_access, "dvb-lnb-lof1", i_lnb_lof1 );
        var_SetInteger( p_access, "dvb-lnb-lof2", i_lnb_lof2 );
        var_SetInteger( p_access, "dvb-lnb-slof", i_lnb_slof );
850
851
852
    }
    else
    {
ivoire's avatar
ivoire committed
853
854
855
        i_lnb_lof1 = i_val;
        i_lnb_lof2 = var_GetInteger( p_access, "dvb-lnb-lof2" );
        i_lnb_slof = var_GetInteger( p_access, "dvb-lnb-slof" );
856
857
858
859
860
861
    }

    if( i_lnb_slof && i_frequency >= i_lnb_slof )
    {
        i_frequency -= i_lnb_lof2;
    }
862
    else
863
864
865
866
    {
        i_frequency -= i_lnb_lof1;
    }
    fep.frequency = i_frequency >= 0 ? i_frequency : -i_frequency;
867

868
    fep.inversion = DecodeInversion( p_access );
869

ivoire's avatar
ivoire committed
870
    fep.u.qpsk.symbol_rate = var_GetInteger( p_access, "dvb-srate" );
871

872
    fep.u.qpsk.fec_inner = DecodeFEC( p_access, "dvb-fec" );
873

874
    if( DoDiseqc( p_access ) < 0 )
875
    {
876
        return VLC_EGENERIC;
877
878
879
    }

    /* Empty the event queue */
880
    for( ; ; )
881
882
    {
        struct dvb_frontend_event event;
Christophe Massiot's avatar
Christophe Massiot committed
883
884
        if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
              && errno == EWOULDBLOCK )
885
886
887
888
            break;
    }

    /* Now send it all to the frontend device */
889
    if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
890
    {
891
        msg_Err( p_access, "frontend error: %m" );
892
        return VLC_EGENERIC;
893
894
    }

Christophe Massiot's avatar
Christophe Massiot committed
895
    return VLC_SUCCESS;
896
897
898
899
900
}

/*****************************************************************************
 * FrontendSetQAM : controls the FE device
 *****************************************************************************/
901
static int FrontendSetQAM( access_t *p_access )
902
{
903
    access_sys_t *p_sys = p_access->p_sys;
904
    frontend_t *p_frontend = p_sys->p_frontend;
905
    struct dvb_frontend_parameters fep;
906
    unsigned int i_val;
907
908
909

    /* Prepare the fep structure */

ivoire's avatar
ivoire committed
910
    fep.frequency = var_GetInteger( p_access, "dvb-frequency" );
911

912
    fep.inversion = DecodeInversion( p_access );
913

914
915
916
917
    /* Default symbol-rate is for dvb-s, and doesn't fit
     * for dvb-c, so if it's over the limit of frontend, default to
     * somewhat common value
     */
ivoire's avatar
ivoire committed
918
919
920
921
    i_val = var_GetInteger( p_access, "dvb-srate" );
    if( i_val < p_frontend->info.symbol_rate_max &&
        i_val > p_frontend->info.symbol_rate_min )
        fep.u.qam.symbol_rate = i_val;
922

923
    fep.u.qam.fec_inner = DecodeFEC( p_access, "dvb-fec" );
924

925
    fep.u.qam.modulation = DecodeModulation( p_access, QAM_AUTO );
926

927
    /* Empty the event queue */
928
    for( ; ; )
929
930
    {
        struct dvb_frontend_event event;
Christophe Massiot's avatar
Christophe Massiot committed
931
932
        if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
              && errno == EWOULDBLOCK )
933
934
935
            break;
    }

936
    /* Now send it all to the frontend device */
937
    if( ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep ) < 0 )
938
    {
939
        msg_Err( p_access, "frontend error: %m" );
940
        return VLC_EGENERIC;
941
942
    }

Christophe Massiot's avatar
Christophe Massiot committed
943
    return VLC_SUCCESS;
944
945
946
947
948
}

/*****************************************************************************
 * FrontendSetOFDM : controls the FE device
 *****************************************************************************/
949
static fe_bandwidth_t DecodeBandwidth( access_t *p_access )
950
951
{
    fe_bandwidth_t      fe_bandwidth = 0;
952
    int i_bandwidth = var_GetInteger( p_access, "dvb-bandwidth" );
953

954
    msg_Dbg( p_access, "using bandwidth=%d", i_bandwidth );
955

956
    switch( i_bandwidth )
957
958
959
960
961
962
    {
        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:
963
            msg_Dbg( p_access, "terrestrial dvb has bandwidth not set, using auto" );
964
965
966
967
968
969
            fe_bandwidth = BANDWIDTH_AUTO;
            break;
    }
    return fe_bandwidth;
}

970
static fe_transmit_mode_t DecodeTransmission( access_t *p_access )
971
972
{
    fe_transmit_mode_t  fe_transmission = 0;
ivoire's avatar
ivoire committed
973
    int i_transmission = var_GetInteger( p_access, "dvb-transmission" );
974

ivoire's avatar
ivoire committed
975
    msg_Dbg( p_access, "using transmission=%d", i_transmission );
976

ivoire's avatar
ivoire committed
977
    switch( i_transmission )
978
979
980
981
982
    {
        case 0: fe_transmission = TRANSMISSION_MODE_AUTO; break;
        case 2: fe_transmission = TRANSMISSION_MODE_2K; break