예제 #1
0
// documented in dvbchannel.h
double DVBChannel::GetSignalStrength(bool *ok) const
{
    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        double val = master->GetSignalStrength(ok);
        ReturnMasterLock(master);
        return val;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    // We use uint16_t for sig because this is correct for DVB API 4.0,
    // and works better than the correct int16_t for the 3.x API
    uint16_t sig = 0;

    int ret = ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &sig);
    if (ret < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Getting Frontend signal strength failed." + ENO);
    }

    if (ok)
        *ok = (0 == ret);

    return sig * (1.0 / 65535.0);
}
예제 #2
0
// documented in dvbchannel.h
bool DVBChannel::HasLock(bool *ok) const
{
    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        bool haslock = master->HasLock(ok);
        ReturnMasterLock(master);
        return haslock;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    fe_status_t status;
    memset(&status, 0, sizeof(status));

    int ret = ioctl(fd_frontend, FE_READ_STATUS, &status);
    if (ret < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Getting Frontend status failed." + ENO);
    }

    if (ok)
        *ok = (0 == ret);

    return status & FE_HAS_LOCK;
}
예제 #3
0
/** \fn DVBChannel::ProbeTuningParams(DTVMultiplex&) const
 *  \brief Fetches DTVMultiplex params from driver
 *
 *   Note: Only updates tuning on success.
 *
 *  \return true on success, false on failure
 */
bool DVBChannel::ProbeTuningParams(DTVMultiplex &tuning) const
{
    QMutexLocker locker(&hw_lock);

    if (fd_frontend < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC + "Card not open!");

        return false;
    }

    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        bool ok = master->ProbeTuningParams(tuning);
        ReturnMasterLock(master);
        return ok;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    if (diseqc_tree)
    {
        // TODO We need to implement the inverse of
        // lnb->GetIntermediateFrequency() for ProbeTuningParams()
        // to accurately reflect the frequency before LNB transform.
        return false;
    }

    if (tunerType == DTVTunerType::kTunerTypeDVBS2)
    {
        // TODO implement probing of tuning parameters with FE_GET_PROPERTY
        return false;
    }

    dvb_frontend_parameters params;
    if (ioctl(fd_frontend, FE_GET_FRONTEND, &params) < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Getting Frontend tuning parameters failed." + ENO);

        return false;
    }

    uint    mplex      = tuning.mplex;
    QString sistandard = tuning.sistandard; sistandard.detach();

    tuning = dvbparams_to_dtvmultiplex(tunerType, params);

    tuning.mplex       = mplex;
    tuning.sistandard  = sistandard;

    return true;
}
예제 #4
0
bool DVBChannel::IsMaster(void) const
{
    const DVBChannel *master = GetMasterLock();
    bool is_master = (master == this);
    ReturnMasterLock(master);
    return is_master;
}
예제 #5
0
/** \fn DVBChannel::IsTuningParamsProbeSupported(void) const
 *  \brief Returns true iff tuning info probing is working.
 */
bool DVBChannel::IsTuningParamsProbeSupported(void) const
{
    QMutexLocker locker(&hw_lock);

    if (fd_frontend < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC + "Card not open!");

        return false;
    }

    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        bool ok = master->IsTuningParamsProbeSupported();
        ReturnMasterLock(master);
        return ok;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    if (diseqc_tree)
    {
        // TODO We need to implement the inverse of
        // lnb->GetIntermediateFrequency() for ProbeTuningParams()
        // to accurately reflect the frequency before LNB transform.
        return false;
    }

    dvb_frontend_parameters params;

    int res = ioctl(fd_frontend, FE_GET_FRONTEND, &params);
    if (res < 0)
    {
        LOG(VB_CHANNEL, LOG_ERR, LOC + "Getting device frontend failed." + ENO);
    }

    return (res >= 0);
}
예제 #6
0
void DVBChannel::Close(DVBChannel *who)
{
    LOG(VB_CHANNEL, LOG_INFO, LOC + "Closing DVB channel");

    IsOpenMap::iterator it = is_open.find(who);
    if (it == is_open.end())
        return; // this caller didn't have it open in the first place..

    is_open.erase(it);

    QMutexLocker locker(&hw_lock);

    DVBChannel *master = GetMasterLock();
    if (master != NULL && master != this)
    {
        if (dvbcam->IsRunning())
            dvbcam->SetPMT(this, NULL);
        master->Close(this);
        fd_frontend = -1;
        ReturnMasterLock(master);
        return;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    if (!is_open.empty())
        return; // not all callers have closed the DVB channel yet..

    if (diseqc_tree)
        diseqc_tree->Close();

    if (fd_frontend >= 0)
    {
        close(fd_frontend);
        fd_frontend = -1;

        dvbcam->Stop();
    }
}
예제 #7
0
// documented in dvbchannel.h
double DVBChannel::GetUncorrectedBlockCount(bool *ok) const
{
    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        double val = master->GetUncorrectedBlockCount(ok);
        ReturnMasterLock(master);
        return val;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    uint32_t ublocks = 0;
    int ret = ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &ublocks);
    if (ret < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Getting Frontend uncorrected block count failed." + ENO);
    }

    if (ok)
        *ok = (0 == ret);

    return (double) ublocks;
}
예제 #8
0
// documented in dvbchannel.h
double DVBChannel::GetBitErrorRate(bool *ok) const
{
    const DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        double val = master->GetBitErrorRate(ok);
        ReturnMasterLock(master);
        return val;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    uint32_t ber = 0;
    int ret = ioctl(fd_frontend, FE_READ_BER, &ber);
    if (ret < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Getting Frontend signal error rate failed." + ENO);
    }

    if (ok)
        *ok = (0 == ret);

    return (double) ber;
}
예제 #9
0
/**
 *  \brief Tunes the card to a frequency but does not deal with PIDs.
 *
 *   This is used by DVB Channel Scanner, the EIT Parser, and by TVRec.
 *
 *  \param tuning      Info on transport to tune to
 *  \param inputid     Optional, forces specific input (for DiSEqC)
 *  \param force_reset If true, frequency tuning is done
 *                     even if it should not be needed.
 *  \param same_input  Optional, doesn't change input (for retuning).
 *  \return true on success, false on failure
 */
bool DVBChannel::Tune(const DTVMultiplex &tuning,
                      uint inputid,
                      bool force_reset,
                      bool same_input)
{
    QMutexLocker lock(&tune_lock);
    QMutexLocker locker(&hw_lock);

    DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        LOG(VB_CHANNEL, LOG_INFO, LOC + "tuning on slave channel");
        SetSIStandard(tuning.sistandard);
        bool ok = master->Tune(tuning, inputid, force_reset, false);
        ReturnMasterLock(master);
        return ok;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..


    int intermediate_freq = 0;
    bool can_fec_auto = false;
    bool reset = (force_reset || first_tune);

    if (tunerType.IsDiSEqCSupported() && !diseqc_tree)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "DVB-S needs device tree for LNB handling");
        return false;
    }

    desired_tuning = tuning;

    if (fd_frontend < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC + "Tune(): Card not open!");

        return false;
    }

    // Remove any events in queue before tuning.
    drain_dvb_events(fd_frontend);

    // send DVB-S setup
    if (diseqc_tree)
    {
        // configure for new input
        if (!same_input)
            diseqc_settings.Load(inputid);

        // execute diseqc commands
        if (!diseqc_tree->Execute(diseqc_settings, tuning))
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                "Tune(): Failed to setup DiSEqC devices");
            return false;
        }

        // retrieve actual intermediate frequency
        DiSEqCDevLNB *lnb = diseqc_tree->FindLNB(diseqc_settings);
        if (!lnb)
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                "Tune(): No LNB for this configuration");
            return false;
        }

        if (lnb->GetDeviceID() != last_lnb_dev_id)
        {
            last_lnb_dev_id = lnb->GetDeviceID();
            // make sure we tune to frequency, if the lnb has changed
            reset = first_tune = true;
        }

        intermediate_freq = lnb->GetIntermediateFrequency(
            diseqc_settings, tuning);

        // if card can auto-FEC, use it -- sometimes NITs are inaccurate
        if (capabilities & FE_CAN_FEC_AUTO)
            can_fec_auto = true;

        // Check DVB-S intermediate frequency here since it requires a fully
        // initialized diseqc tree
        CheckFrequency(intermediate_freq);
    }

    LOG(VB_CHANNEL, LOG_INFO, LOC + "Old Params: " + prev_tuning.toString() +
            "\n\t\t\t" + LOC + "New Params: " + tuning.toString());

    // DVB-S is in kHz, other DVB is in Hz
    bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tunerType) ||
                    (DTVTunerType::kTunerTypeDVBS2 == tunerType));
    int     freq_mult = (is_dvbs) ? 1 : 1000;
    QString suffix    = (is_dvbs) ? "kHz" : "Hz";

    if (reset || !prev_tuning.IsEqual(tunerType, tuning, 500 * freq_mult))
    {
        LOG(VB_CHANNEL, LOG_INFO, LOC + QString("Tune(): Tuning to %1%2")
                .arg(intermediate_freq ? intermediate_freq : tuning.frequency)
                .arg(suffix));

#if DVB_API_VERSION >=5
        if (DTVTunerType::kTunerTypeDVBS2 == tunerType)
        {
            struct dtv_property p_clear;
            struct dtv_properties cmdseq_clear;

            p_clear.cmd        = DTV_CLEAR;
            cmdseq_clear.num   = 1;
            cmdseq_clear.props = &p_clear;

            if ((ioctl(fd_frontend, FE_SET_PROPERTY, &cmdseq_clear)) < 0)
            {
                LOG(VB_GENERAL, LOG_ERR, LOC +
                    "Tune(): Clearing DTV properties cache failed." + ENO);
                return false;
            }

            struct dtv_properties *cmds = dtvmultiplex_to_dtvproperties(
                tunerType, tuning, intermediate_freq, can_fec_auto);

            if (!cmds) {
                LOG(VB_GENERAL, LOG_ERR, LOC +
                    "Failed to convert DTVMultiplex to DTV_PROPERTY sequence");
                return false;
            }

            if (VERBOSE_LEVEL_CHECK(VB_CHANNEL, LOG_DEBUG))
            {
                for (uint i = 0; i < cmds->num; i++)
                {
                    LOG(VB_CHANNEL, LOG_DEBUG, LOC +
                        QString("prop %1: cmd = %2, data %3")
                            .arg(i).arg(cmds->props[i].cmd)
                            .arg(cmds->props[i].u.data));
                }
            }

            int res = ioctl(fd_frontend, FE_SET_PROPERTY, cmds);

            free(cmds->props);
            free(cmds);

            if (res < 0)
            {
                LOG(VB_GENERAL, LOG_ERR, LOC +
                    "Tune(): Setting Frontend tuning parameters failed." + ENO);
                return false;
            }
        }
        else
#endif
        {
            struct dvb_frontend_parameters params = dtvmultiplex_to_dvbparams(
                tunerType, tuning, intermediate_freq, can_fec_auto);

            if (ioctl(fd_frontend, FE_SET_FRONTEND, &params) < 0)
            {
                LOG(VB_GENERAL, LOG_ERR, LOC +
                    "Tune(): Setting Frontend tuning parameters failed." + ENO);
                return false;
            }
        }

        // Extra delay to add for broken DVB drivers
        if (tuning_delay)
            usleep(tuning_delay * 1000);

        wait_for_backend(fd_frontend, 5 /* msec */);

        prev_tuning = tuning;
        first_tune = false;
    }

    SetSIStandard(tuning.sistandard);

    LOG(VB_CHANNEL, LOG_INFO, LOC + "Tune(): Frequency tuning successful.");

    return true;
}
예제 #10
0
bool DVBChannel::Open(DVBChannel *who)
{
    LOG(VB_CHANNEL, LOG_INFO, LOC + "Opening DVB channel");

    QMutexLocker locker(&hw_lock);

    if (fd_frontend >= 0)
    {
        is_open[who] = true;
        return true;
    }

    DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        if (!master->Open(who))
        {
            ReturnMasterLock(master);
            return false;
        }

        fd_frontend         = master->fd_frontend;
        frontend_name       = master->frontend_name;
        tunerType           = master->tunerType;
        capabilities        = master->capabilities;
        ext_modulations     = master->ext_modulations;
        frequency_minimum   = master->frequency_minimum;
        frequency_maximum   = master->frequency_maximum;
        symbol_rate_minimum = master->symbol_rate_minimum;
        symbol_rate_maximum = master->symbol_rate_maximum;

        is_open[who] = true;

        if (!InitializeInputs())
        {
            Close();
            ReturnMasterLock(master);
            return false;
        }

        ReturnMasterLock(master);
        return true;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    QString devname = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
    QByteArray devn = devname.toAscii();

    for (int tries = 1; ; ++tries)
    {
        fd_frontend = open(devn.constData(), O_RDWR | O_NONBLOCK);
        if (fd_frontend >= 0)
            break;
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Opening DVB frontend device failed." + ENO);
        if (tries >= 20 || (errno != EBUSY && errno != EAGAIN))
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                QString("Failed to open DVB frontend device due to "
                        "fatal error or too many attempts."));
            return false;
        }
        usleep(50000);
    }

    dvb_frontend_info info;
    memset(&info, 0, sizeof(info));
    if (ioctl(fd_frontend, FE_GET_INFO, &info) < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Failed to get frontend information." + ENO);

        close(fd_frontend);
        fd_frontend = -1;
        return false;
    }

    frontend_name       = info.name;
    tunerType           = info.type;
#if HAVE_FE_CAN_2G_MODULATION
    if (tunerType == DTVTunerType::kTunerTypeDVBS1 &&
        (info.caps & FE_CAN_2G_MODULATION))
        tunerType = DTVTunerType::kTunerTypeDVBS2;
#endif // HAVE_FE_CAN_2G_MODULATION
    capabilities        = info.caps;
    frequency_minimum   = info.frequency_min;
    frequency_maximum   = info.frequency_max;
    symbol_rate_minimum = info.symbol_rate_min;
    symbol_rate_maximum = info.symbol_rate_max;

    LOG(VB_RECORD, LOG_INFO, LOC +
        QString("Using DVB card %1, with frontend '%2'.")
            .arg(device).arg(frontend_name));

    // Turn on the power to the LNB
    if (tunerType.IsDiSEqCSupported())
    {
        diseqc_tree = diseqc_dev.FindTree(GetCardID());
        if (diseqc_tree)
            diseqc_tree->Open(fd_frontend);
    }

    dvbcam->Start();

    first_tune = true;

    if (!InitializeInputs())
    {
        Close();
        return false;
    }

    if (fd_frontend >= 0)
        is_open[who] = true;

    return (fd_frontend >= 0);
}
예제 #11
0
void DVBChannel::CheckOptions(DTVMultiplex &tuning) const
{
    if ((tuning.inversion == DTVInversion::kInversionAuto) &&
        !(capabilities & FE_CAN_INVERSION_AUTO))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "'Auto' inversion parameter unsupported by this driver, "
            "falling back to 'off'.");
        tuning.inversion = DTVInversion::kInversionOff;
    }

    // DVB-S needs a fully initialized diseqc tree and is checked later in Tune
    if (!diseqc_tree)
    {
        const DVBChannel *master = GetMasterLock();
        if (master == NULL || !master->diseqc_tree)
            CheckFrequency(tuning.frequency);
        ReturnMasterLock(master);
    }

    if (tunerType.IsFECVariable() &&
        symbol_rate_minimum && symbol_rate_maximum &&
        (symbol_rate_minimum <= symbol_rate_maximum) &&
        (tuning.symbolrate < symbol_rate_minimum ||
         tuning.symbolrate > symbol_rate_maximum))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            QString("Symbol Rate setting (%1) is out of range (min/max:%2/%3)")
                .arg(tuning.symbolrate)
                .arg(symbol_rate_minimum).arg(symbol_rate_maximum));
    }

    if (tunerType.IsFECVariable() && !CheckCodeRate(tuning.fec))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Selected fec_inner parameter unsupported by this driver.");
    }

    if (tunerType.IsModulationVariable() && !CheckModulation(tuning.modulation))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
           "Selected modulation parameter unsupported by this driver.");
    }

    if (DTVTunerType::kTunerTypeDVBT != tunerType &&
        DTVTunerType::kTunerTypeDVBT2 != tunerType)
    {
        LOG(VB_CHANNEL, LOG_INFO, LOC + tuning.toString());
        return;
    }

    // Check OFDM Tuning params

    if (!CheckCodeRate(tuning.hp_code_rate))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Selected code_rate_hp parameter unsupported by this driver.");
    }

    if (!CheckCodeRate(tuning.lp_code_rate))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Selected code_rate_lp parameter unsupported by this driver.");
    }

    if ((tuning.bandwidth == DTVBandwidth::kBandwidthAuto) &&
        !(capabilities & FE_CAN_BANDWIDTH_AUTO))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "'Auto' bandwidth parameter unsupported by this driver.");
    }

    if ((tuning.trans_mode == DTVTransmitMode::kTransmissionModeAuto) &&
        !(capabilities & FE_CAN_TRANSMISSION_MODE_AUTO))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "'Auto' transmission_mode parameter unsupported by this driver.");
    }

    if ((tuning.guard_interval == DTVGuardInterval::kGuardIntervalAuto) &&
        !(capabilities & FE_CAN_GUARD_INTERVAL_AUTO))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "'Auto' guard_interval parameter unsupported by this driver.");
    }

    if ((tuning.hierarchy == DTVHierarchy::kHierarchyAuto) &&
        !(capabilities & FE_CAN_HIERARCHY_AUTO))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "'Auto' hierarchy parameter unsupported by this driver. ");
    }

    if (!CheckModulation(tuning.modulation))
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Selected modulation parameter unsupported by this driver.");
    }

    LOG(VB_CHANNEL, LOG_INFO, LOC + tuning.toString());
}
예제 #12
0
bool DVBChannel::Open(DVBChannel *who)
{
    LOG(VB_CHANNEL, LOG_INFO, LOC + "Opening DVB channel");

    if (!m_inputid)
    {
        if (!InitializeInput())
            return false;
    }

    QMutexLocker locker(&m_hw_lock);

    if (m_fd_frontend >= 0)
    {
        m_is_open[who] = true;
        return true;
    }

    DVBChannel *master = GetMasterLock();
    if (master != this)
    {
        if (!master->Open(who))
        {
            ReturnMasterLock(master);
            return false;
        }

        m_fd_frontend         = master->m_fd_frontend;
        m_frontend_name       = master->m_frontend_name;
        m_tunerType           = master->m_tunerType;
        m_capabilities        = master->m_capabilities;
        m_ext_modulations     = master->m_ext_modulations;
        m_frequency_minimum   = master->m_frequency_minimum;
        m_frequency_maximum   = master->m_frequency_maximum;
        m_symbol_rate_minimum = master->m_symbol_rate_minimum;
        m_symbol_rate_maximum = master->m_symbol_rate_maximum;

        m_is_open[who] = true;

        if (!InitializeInput())
        {
            Close();
            ReturnMasterLock(master);
            return false;
        }

        ReturnMasterLock(master);
        return true;
    }
    ReturnMasterLock(master); // if we're the master we don't need this lock..

    QString devname = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, m_device);
    QByteArray devn = devname.toLatin1();

    for (int tries = 1; ; ++tries)
    {
        m_fd_frontend = open(devn.constData(), O_RDWR | O_NONBLOCK);
        if (m_fd_frontend >= 0)
            break;
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            "Opening DVB frontend device failed." + ENO);
        if (tries >= 20 || (errno != EBUSY && errno != EAGAIN))
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                QString("Failed to open DVB frontend device due to "
                        "fatal error or too many attempts."));
            return false;
        }
        usleep(50000);
    }

    dvb_frontend_info info;
    memset(&info, 0, sizeof(info));
    if (ioctl(m_fd_frontend, FE_GET_INFO, &info) < 0)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Failed to get frontend information." + ENO);

        close(m_fd_frontend);
        m_fd_frontend = -1;
        return false;
    }

    m_frontend_name     = info.name;
    m_tunerType         = info.type;
#if HAVE_FE_CAN_2G_MODULATION
    if (info.caps & FE_CAN_2G_MODULATION)
    {
        if (m_tunerType == DTVTunerType::kTunerTypeDVBS1)
            m_tunerType = DTVTunerType::kTunerTypeDVBS2;
        else if (m_tunerType == DTVTunerType::kTunerTypeDVBT)
            m_tunerType = DTVTunerType::kTunerTypeDVBT2;
    }
#endif // HAVE_FE_CAN_2G_MODULATION
    m_capabilities        = info.caps;
    m_frequency_minimum   = info.frequency_min;
    m_frequency_maximum   = info.frequency_max;
    m_symbol_rate_minimum = info.symbol_rate_min;
    m_symbol_rate_maximum = info.symbol_rate_max;

#if DVB_API_VERSION >=5
    unsigned int i;
    struct dtv_property prop;
    struct dtv_properties cmd;

    memset(&prop, 0, sizeof(prop));
    prop.cmd = DTV_API_VERSION;
    cmd.num = 1;
    cmd.props = &prop;
    if (ioctl(m_fd_frontend, FE_GET_PROPERTY, &cmd) == 0)
    {
        LOG(VB_RECORD, LOG_INFO, LOC +
            QString("dvb api version %1.%2").arg((prop.u.data>>8)&0xff).arg((prop.u.data)&0xff));
    }