linux_dvb.c 61.2 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
 *
 * 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 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *          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.
 *****************************************************************************/

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

31
#include <vlc_common.h>
Clément Stenac's avatar
Clément Stenac committed
32
#include <vlc_access.h>
33
#include <sys/ioctl.h>
34
#include <errno.h>
35 36 37 38 39 40 41 42 43 44 45 46 47

#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>
48
#include <linux/dvb/ca.h>
49

Christophe Massiot's avatar
Christophe Massiot committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/* 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
66

67 68 69 70
#ifdef ENABLE_HTTPD
#   include "vlc_httpd.h"
#endif

Christophe Massiot's avatar
Christophe Massiot committed
71
#include "dvb.h"
72 73 74 75

/*
 * Frontends
 */
76 77
struct frontend_t
{
Christophe Massiot's avatar
Christophe Massiot committed
78
    fe_status_t i_last_status;
79
    struct dvb_frontend_info info;
80
};
81

82 83
#define FRONTEND_LOCK_TIMEOUT 10000000 /* 10 s */

84
/* Local prototypes */
85 86 87 88
static int FrontendInfo( access_t * );
static int FrontendSetQPSK( access_t * );
static int FrontendSetQAM( access_t * );
static int FrontendSetOFDM( access_t * );
89
static int FrontendSetATSC( access_t * );
90 91 92 93

/*****************************************************************************
 * FrontendOpen : Determine frontend device information and capabilities
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
94
int FrontendOpen( access_t *p_access )
95
{
96
    access_sys_t *p_sys = p_access->p_sys;
97 98
    frontend_t * p_frontend;
    unsigned int i_adapter, i_device;
99
    bool b_probe;
100 101
    char frontend[128];

102 103 104
    i_adapter = var_GetInteger( p_access, "dvb-adapter" );
    i_device = var_GetInteger( p_access, "dvb-device" );
    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 110 111
        frontend[sizeof(frontend) - 1] = '\0';
    }

112
    p_sys->p_frontend = p_frontend = malloc( sizeof(frontend_t) );
113

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

122
    if( b_probe )
123
    {
Clément Stenac's avatar
Clément Stenac committed
124 125
        const char * psz_expected = NULL;
        const char * psz_real;
126

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

134
        switch( p_frontend->info.type )
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;
145 146 147
        case FE_ATSC:
            psz_real = "ATSC";
            break;
148 149 150
        default:
            psz_real = "unknown";
        }
151

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

173 174 175 176 177 178 179
        if( (!strncmp( p_access->psz_access, "usdigital", 9 ) ||
             !strncmp( p_access->psz_access, "atsc", 4 ) ) &&
             (p_frontend->info.type != FE_ATSC) )
        {
            psz_expected = "ATSC";
        }

180
        if( psz_expected != NULL )
181
        {
182
            msg_Err( p_access, "the user asked for %s, and the tuner is %s",
183
                     psz_expected, psz_real );
Christophe Massiot's avatar
Christophe Massiot committed
184
            close( p_sys->i_frontend_handle );
185
            free( p_frontend );
186
            return VLC_EGENERIC;
187 188 189 190
        }
    }
    else /* no frontend probing is done so use default border values. */
    {
191
        msg_Dbg( p_access, "using default values for frontend info" );
192

193
        msg_Dbg( p_access, "method of access is %s", p_access->psz_access );
194
        p_frontend->info.type = FE_QPSK;
195 196
        if( !strncmp( p_access->psz_access, "qpsk", 4 ) ||
            !strncmp( p_access->psz_access, "dvb-s", 5 ) )
197
            p_frontend->info.type = FE_QPSK;
198 199
        else if( !strncmp( p_access->psz_access, "cable", 5 ) ||
                 !strncmp( p_access->psz_access, "dvb-c", 5 ) )
200
            p_frontend->info.type = FE_QAM;
201 202
        else if( !strncmp( p_access->psz_access, "terrestrial", 11 ) ||
                 !strncmp( p_access->psz_access, "dvb-t", 5 ) )
203
            p_frontend->info.type = FE_OFDM;
204 205 206
        else if( !strncmp( p_access->psz_access, "usdigital", 9 ) ||
                 !strncmp( p_access->psz_access, "atsc", 4 ) )
            p_frontend->info.type = FE_ATSC;
207 208
    }

209
    return VLC_SUCCESS;
210 211 212 213 214
}

/*****************************************************************************
 * FrontendClose : Close the frontend
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
void FrontendClose( access_t *p_access )
216
{
217
    access_sys_t *p_sys = p_access->p_sys;
218

219
    if( p_sys->p_frontend )
220
    {
Christophe Massiot's avatar
Christophe Massiot committed
221
        close( p_sys->i_frontend_handle );
222 223 224
        free( p_sys->p_frontend );

        p_sys->p_frontend = NULL;
225 226 227 228 229 230
    }
}

/*****************************************************************************
 * FrontendSet : Tune !
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
231
int FrontendSet( access_t *p_access )
232
{
233
    access_sys_t *p_sys = p_access->p_sys;
234

235
    switch( p_sys->p_frontend->info.type )
236
    {
Christophe Massiot's avatar
Christophe Massiot committed
237
    /* DVB-S */
238
    case FE_QPSK:
239
        if( FrontendSetQPSK( p_access ) < 0 )
240
        {
241 242
            msg_Err( p_access, "DVB-S: tuning failed" );
            return VLC_EGENERIC;
243 244
        }
        break;
245

246 247
    /* DVB-C */
    case FE_QAM:
248
        if( FrontendSetQAM( p_access ) < 0 )
249
        {
250 251
            msg_Err( p_access, "DVB-C: tuning failed" );
            return VLC_EGENERIC;
252 253 254 255 256
        }
        break;

    /* DVB-T */
    case FE_OFDM:
257
        if( FrontendSetOFDM( p_access ) < 0 )
258
        {
259 260
            msg_Err( p_access, "DVB-T: tuning failed" );
            return VLC_EGENERIC;
261 262 263
        }
        break;

264 265 266 267 268 269 270 271 272
    /* ATSC */
    case FE_ATSC:
        if( FrontendSetATSC( p_access ) < 0 )
        {
            msg_Err( p_access, "ATSC: tuning failed" );
            return VLC_EGENERIC;
        }
        break;

273
    default:
274 275 276
        msg_Err( p_access, "Could not determine frontend type on %s",
                 p_sys->p_frontend->info.name );
        return VLC_EGENERIC;
277
    }
Christophe Massiot's avatar
Christophe Massiot committed
278
    p_sys->p_frontend->i_last_status = 0;
279
    p_sys->i_frontend_timeout = mdate() + FRONTEND_LOCK_TIMEOUT;
280
    return VLC_SUCCESS;
281 282
}

Christophe Massiot's avatar
Christophe Massiot committed
283 284 285
/*****************************************************************************
 * FrontendPoll : Poll for frontend events
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
286
void FrontendPoll( access_t *p_access )
Christophe Massiot's avatar
Christophe Massiot committed
287 288 289 290 291 292
{
    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;

293
    for( ;; )
Christophe Massiot's avatar
Christophe Massiot committed
294
    {
295 296 297 298 299
        int i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event );

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

302 303
            msg_Err( p_access, "reading frontend event failed (%d): %m",
                     i_ret );
304
            return;
305 306 307 308 309
        }

        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
310

311
        {
Christophe Massiot's avatar
Christophe Massiot committed
312
#define IF_UP( x )                                                          \
313 314 315 316
        }                                                                   \
        if ( i_diff & (x) )                                                 \
        {                                                                   \
            if ( i_status & (x) )
Christophe Massiot's avatar
Christophe Massiot committed
317

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
            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 )
            {
340
                int32_t i_value = 0;
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
                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");
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
362
                FrontendSet( p_access );
363
            }
Christophe Massiot's avatar
Christophe Massiot committed
364
        }
365
#undef IF_UP
Christophe Massiot's avatar
Christophe Massiot committed
366 367
    }
}
368 369 370 371 372

#ifdef ENABLE_HTTPD
/*****************************************************************************
 * FrontendStatus : Read frontend status
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
373
void FrontendStatus( access_t *p_access )
374 375 376 377 378 379 380 381 382 383 384
{
    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 )
    {
385 386 387
        char buf[1000];
        strerror_r( errno, buf, sizeof( buf ) );
        p += sprintf( p, "ioctl FE_GET_INFO failed (%d) %s\n", i_ret, buf );
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 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        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 );
463
#if 0 /* Disabled because of older distributions */
464
    CHECK_CAPS( CAN_CLEAN_SETUP );
465
#endif
466 467 468 469 470 471 472
#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 )
    {
473 474 475 476
        char buf[1000];
        strerror_r( errno, buf, sizeof( buf ) );
        p += sprintf( p, "</table>ioctl FE_READ_STATUS failed (%d) %s\n",
                      i_ret, buf );
477 478 479 480 481 482 483 484 485 486 487 488 489
        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 );
490 491
    if( i_status == 0 )
        p += sprintf( p, "<tr><td>Tuning failed</td></tr>\n" );
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
#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 );
513
    p_sys->b_request_frontend_info = false;
514 515 516 517 518
    vlc_cond_signal( &p_sys->httpd_cond );
    vlc_mutex_unlock( &p_sys->httpd_mutex );
}
#endif

519 520 521
/*****************************************************************************
 * FrontendInfo : Return information about given frontend
 *****************************************************************************/
522
static int FrontendInfo( access_t *p_access )
523
{
524 525
    access_sys_t *p_sys = p_access->p_sys;
    frontend_t *p_frontend = p_sys->p_frontend;
526 527 528
    int i_ret;

    /* Determine type of frontend */
Christophe Massiot's avatar
Christophe Massiot committed
529
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_GET_INFO,
530
                        &p_frontend->info )) < 0 )
531
    {
532
        msg_Err( p_access, "ioctl FE_GET_INFO failed (%d): %m", i_ret );
533
        return VLC_EGENERIC;
534 535 536
    }

    /* Print out frontend capabilities. */
537 538 539
    msg_Dbg(p_access, "Frontend Info:" );
    msg_Dbg(p_access, "  name = %s", p_frontend->info.name );
    switch( p_frontend->info.type )
540 541
    {
        case FE_QPSK:
542
            msg_Dbg( p_access, "  type = QPSK (DVB-S)" );
543 544
            break;
        case FE_QAM:
545
            msg_Dbg( p_access, "  type = QAM (DVB-C)" );
546 547
            break;
        case FE_OFDM:
548
            msg_Dbg( p_access, "  type = OFDM (DVB-T)" );
549
            break;
550 551 552
        case FE_ATSC:
            msg_Dbg( p_access, "  type = ATSC (USA)" );
            break;
553 554
#if 0 /* DVB_API_VERSION == 3 */
        case FE_MEMORY:
555
            msg_Dbg(p_access, "  type = MEMORY" );
556 557
            break;
        case FE_NET:
558
            msg_Dbg(p_access, "  type = NETWORK" );
559 560 561
            break;
#endif
        default:
562
            msg_Err( p_access, "  unknown frontend type (%d)",
563
                     p_frontend->info.type );
564
            return VLC_EGENERIC;
565
    }
566
    msg_Dbg(p_access, "  frequency_min = %u (kHz)",
567
            p_frontend->info.frequency_min);
568
    msg_Dbg(p_access, "  frequency_max = %u (kHz)",
569
            p_frontend->info.frequency_max);
570
    msg_Dbg(p_access, "  frequency_stepsize = %u",
571
            p_frontend->info.frequency_stepsize);
572
    msg_Dbg(p_access, "  frequency_tolerance = %u",
573
            p_frontend->info.frequency_tolerance);
574
    msg_Dbg(p_access, "  symbol_rate_min = %u (kHz)",
575
            p_frontend->info.symbol_rate_min);
576
    msg_Dbg(p_access, "  symbol_rate_max = %u (kHz)",
577
            p_frontend->info.symbol_rate_max);
578
    msg_Dbg(p_access, "  symbol_rate_tolerance (ppm) = %u",
579
            p_frontend->info.symbol_rate_tolerance);
580
    msg_Dbg(p_access, "  notifier_delay (ms) = %u",
581 582
            p_frontend->info.notifier_delay );

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
    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");
630 631
    if( p_frontend->info.caps & FE_CAN_RECOVER)
        msg_Dbg(p_access, "  card can recover from a cable unplug");
632 633 634 635
    if( p_frontend->info.caps & FE_CAN_8VSB)
        msg_Dbg(p_access, "  card can do 8vsb");
    if( p_frontend->info.caps & FE_CAN_16VSB)
        msg_Dbg(p_access, "  card can do 16vsb");
636 637 638
    msg_Dbg(p_access, "End of capability list");

    return VLC_SUCCESS;
639 640 641 642 643
}

/*****************************************************************************
 * Decoding the DVB parameters (common)
 *****************************************************************************/
644
static fe_spectral_inversion_t DecodeInversion( access_t *p_access )
645 646 647 648
{
    vlc_value_t         val;
    fe_spectral_inversion_t fe_inversion = 0;

649 650
    var_Get( p_access, "dvb-inversion", &val );
    msg_Dbg( p_access, "using inversion=%d", val.i_int );
651

652
    switch( val.i_int )
653 654 655 656 657
    {
        case 0: fe_inversion = INVERSION_OFF; break;
        case 1: fe_inversion = INVERSION_ON; break;
        case 2: fe_inversion = INVERSION_AUTO; break;
        default:
658
            msg_Dbg( p_access, "dvb has inversion not set, using auto");
659 660 661 662 663 664
            fe_inversion = INVERSION_AUTO;
            break;
    }
    return fe_inversion;
}

665
static fe_code_rate_t DecodeFEC( access_t *p_access, int i_val )
666 667 668
{
    fe_code_rate_t      fe_fec = FEC_NONE;

669
    msg_Dbg( p_access, "using fec=%d", i_val );
670

671
    switch( i_val )
672
    {
673
        case 0: fe_fec = FEC_NONE; break;
674 675 676 677 678 679 680 681 682 683 684 685
        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;
686
            msg_Err( p_access, "argument has invalid FEC (%d)", i_val);
687
            break;
688
    }
689 690 691
    return fe_fec;
}

692
static fe_modulation_t DecodeModulationQAM( access_t *p_access )
693
{
694
    switch( var_GetInteger( p_access, "dvb-modulation" ) )
695
    {
696 697 698 699 700 701
        case 0:     return QAM_AUTO;
        case 16:    return QAM_16;
        case 32:    return QAM_32;
        case 64:    return QAM_64;
        case 128:   return QAM_128;
        case 256:   return QAM_256;
702
        default:
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
            msg_Dbg( p_access, "QAM modulation not set, using auto");
            return QAM_AUTO;
    }
}
static fe_modulation_t DecodeModulationOFDM( access_t *p_access )
{
    switch( var_GetInteger( p_access, "dvb-modulation" ) )
    {
        case -1:    return QPSK;
        case 0:     return QAM_AUTO;
        case 16:    return QAM_16;
        case 32:    return QAM_32;
        case 64:    return QAM_64;
        case 128:   return QAM_128;
        case 256:   return QAM_256;
        default:
            msg_Dbg( p_access, "OFDM modulation not set, using QAM auto");
            return QAM_AUTO;
    }
}
static fe_modulation_t DecodeModulationATSC( access_t *p_access )
{
    switch( var_GetInteger( p_access, "dvb-modulation" ) )
    {
        case 8:     return VSB_8;
        case 16:    return VSB_16;
        default:
            msg_Dbg( p_access, "ATSC modulation not set, using VSB 8");
            return VSB_8;
732
    }
733 734 735 736 737
}

/*****************************************************************************
 * FrontendSetQPSK : controls the FE device
 *****************************************************************************/
738
static fe_sec_voltage_t DecodeVoltage( access_t *p_access )
739 740 741 742
{
    vlc_value_t         val;
    fe_sec_voltage_t    fe_voltage;

743 744
    var_Get( p_access, "dvb-voltage", &val );
    msg_Dbg( p_access, "using voltage=%d", val.i_int );
745

746
    switch( val.i_int )
747 748 749 750 751 752
    {
        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;
753
            msg_Err( p_access, "argument has invalid voltage (%d)", val.i_int );
754
            break;
755
    }
756 757 758
    return fe_voltage;
}

759
static fe_sec_tone_mode_t DecodeTone( access_t *p_access )
760 761 762 763
{
    vlc_value_t         val;
    fe_sec_tone_mode_t  fe_tone;

764 765
    var_Get( p_access, "dvb-tone", &val );
    msg_Dbg( p_access, "using tone=%d", val.i_int );
766

767
    switch( val.i_int )
768 769 770 771 772
    {
        case 0: fe_tone = SEC_TONE_OFF; break;
        case 1: fe_tone = SEC_TONE_ON; break;
        default:
            fe_tone = SEC_TONE_OFF;
773
            msg_Err( p_access, "argument has invalid tone mode (%d)", val.i_int);
774
            break;
775
    }
776 777 778 779 780 781 782 783 784
    return fe_tone;
}

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

785
static int DoDiseqc( access_t *p_access )
786
{
787
    access_sys_t *p_sys = p_access->p_sys;
788 789 790 791 792 793
    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;

794
    var_Get( p_access, "dvb-frequency", &val );
795
    i_frequency = val.i_int;
796
    var_Get( p_access, "dvb-lnb-slof", &val );
797 798
    i_lnb_slof = val.i_int;

799 800
    var_Get( p_access, "dvb-tone", &val );
    if( val.i_int == -1 /* auto */ )
801
    {
802
        if( i_frequency >= i_lnb_slof )
803 804 805
            val.i_int = 1;
        else
            val.i_int = 0;
806
        var_Set( p_access, "dvb-tone", val );
807 808
    }

809 810
    fe_voltage = DecodeVoltage( p_access );
    fe_tone = DecodeTone( p_access );
811

Christophe Massiot's avatar
Christophe Massiot committed
812 813 814
    /* Switch off continuous tone. */
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_TONE, SEC_TONE_OFF )) < 0 )
    {
815 816
        msg_Err( p_access, "ioctl FE_SET_TONE failed, tone=%s (%d) %m",
                 fe_tone == SEC_TONE_ON ? "on" : "off", i_err );
Christophe Massiot's avatar
Christophe Massiot committed
817 818 819 820 821
        return i_err;
    }

    /* Configure LNB voltage. */
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_VOLTAGE, fe_voltage )) < 0 )
822
    {
823 824
        msg_Err( p_access, "ioctl FE_SET_VOLTAGE failed, voltage=%d (%d) %m",
                 fe_voltage, i_err );
825 826 827
        return i_err;
    }

Christophe Massiot's avatar
Christophe Massiot committed
828 829 830 831 832
    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,
833 834
                 "ioctl FE_ENABLE_HIGH_LNB_VOLTAGE failed, val=%d (%d) %m",
                 val.b_bool, i_err );
Christophe Massiot's avatar
Christophe Massiot committed
835 836 837 838 839
    }

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

840
    var_Get( p_access, "dvb-satno", &val );
Christophe Massiot's avatar
Christophe Massiot committed
841
    if( val.i_int > 0 && val.i_int < 5 )
842 843
    {
        /* digital satellite equipment control,
844
         * specification is available from http://www.eutelsat.com/
845
         */
Christophe Massiot's avatar
Christophe Massiot committed
846 847 848 849 850 851 852 853 854 855 856 857 858 859

        /* 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 )
860
        {
861 862
            msg_Err( p_access, "ioctl FE_SEND_MASTER_CMD failed (%d) %m",
                     i_err );
863 864 865
            return i_err;
        }

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

Christophe Massiot's avatar
Christophe Massiot committed
868 869 870
        /* 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 )
871
        {
872 873
            msg_Err( p_access, "ioctl FE_SEND_BURST failed (%d) %m",
                     i_err );
Christophe Massiot's avatar
Christophe Massiot committed
874
            return i_err;
875 876 877 878 879
        }

        msleep(15000);
    }

Christophe Massiot's avatar
Christophe Massiot committed
880
    if( (i_err = ioctl( p_sys->i_frontend_handle, FE_SET_TONE, fe_tone )) < 0 )
881
    {
882 883
        msg_Err( p_access, "ioctl FE_SET_TONE failed, tone=%s (%d) %m",
                 fe_tone == SEC_TONE_ON ? "on" : "off", i_err );
884 885 886
        return i_err;
    }

Christophe Massiot's avatar
Christophe Massiot committed
887
    msleep(50000);
888 889 890
    return 0;
}

891
static int FrontendSetQPSK( access_t *p_access )
892
{
893
    access_sys_t *p_sys = p_access->p_sys;
894 895 896
    struct dvb_frontend_parameters fep;
    int i_ret;
    vlc_value_t val;
897
    int i_frequency, i_lnb_slof = 0, i_lnb_lof1, i_lnb_lof2 = 0;
898 899

    /* Prepare the fep structure */
900
    var_Get( p_access, "dvb-frequency", &val );
901 902
    i_frequency = val.i_int;

903 904 905 906
    var_Get( p_access, "dvb-lnb-lof1", &val );
    if ( val.i_int == 0 )
    {
        /* Automatic mode. */
907 908 909 910 911 912
        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 )
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
        {
            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;
955
        var_Get( p_access, "dvb-lnb-lof2", &val );
956 957 958 959 960 961 962 963 964
        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;
    }
965
    else
966 967 968 969
    {
        i_frequency -= i_lnb_lof1;
    }
    fep.frequency = i_frequency >= 0 ? i_frequency : -i_frequency;
970

971
    fep.inversion = DecodeInversion( p_access );
972

973
    var_Get( p_access, "dvb-srate", &val );
974 975
    fep.u.qpsk.symbol_rate = val.i_int;

976 977
    var_Get( p_access, "dvb-fec", &val );
    fep.u.qpsk.fec_inner = DecodeFEC( p_access, val.i_int );
978

979
    if( DoDiseqc( p_access ) < 0 )
980
    {
981
        return VLC_EGENERIC;
982 983 984
    }

    /* Empty the event queue */
985
    for( ; ; )
986 987
    {
        struct dvb_frontend_event event;
Christophe Massiot's avatar
Christophe Massiot committed
988 989
        if ( ioctl( p_sys->i_frontend_handle, FE_GET_EVENT, &event ) < 0
              && errno == EWOULDBLOCK )
990 991 992 993
            break;
    }

    /* Now send it all to the frontend device */
Christophe Massiot's avatar
Christophe Massiot committed
994
    if( (i_ret = ioctl( p_sys->i_frontend_handle, FE_SET_FRONTEND, &fep )) < 0 )
995
    {
996
        msg_Err( p_access, "DVB-S: setting frontend failed (%d) %m", i_ret );
997
        return VLC_EGENERIC;
998 999
    }

Christophe Massiot's avatar
Christophe Massiot committed
1000
    return VLC_SUCCESS;
1001 1002 1003 1004 1005
}

/*****************************************************************************
 * FrontendSetQAM : controls the FE device
 *****************************************************************************/
1006
static int FrontendSetQAM( access_t *p_access )
1007
{
1008
    access_sys_t *p_sys = p_access->p_sys;
1009 1010 1011 1012 1013 1014
    struct dvb_frontend_parameters fep;
    vlc_value_t val;
    int i_ret;

    /* Prepare the fep structure */

1015
    var_Get( p_access, "dvb-frequency", &val );
1016 1017
    fep.frequency = val.i_int;

1018
    fep.inversion = DecodeInversion( p_access );
1019

1020
    var_Get( p_access, "dvb-srate", &val );
1021 1022
    fep.u.qam.symbol_rate = val.i_int;

1023 1024
    var_Get( p_access, "dvb-fec", &val );
    fep.u.qam.fec_inner = DecodeFEC( p_access, val.i_int );
1025

1026
    fep.u.qam.modulation = DecodeModulationQAM( p_access );
1027

1028
    /* Empty the event queue */