Ticket #1945: dvbdevtree.2.patch

File dvbdevtree.2.patch, 71.8 KB (added by yeasah@…, 19 years ago)

Updated dvbdevtree.cpp that fixes some voltage and rotor related inefficiencies

  • libs/libmythtv/dvbdevtree.cpp

     
     1/*
     2 * \file dvbdevtree.cpp
     3 * \brief DVB-S Device Tree Control Classes.
     4 * \author Copyright (C) 2006, Yeasah Pell
     5 */
     6
     7#include <sys/time.h>
     8#include <string.h>
     9#include <cmath>
     10#include "mythcontext.h"
     11#include "mythdbcon.h"
     12#include "dvbtypes.h"
     13#include "dvbdevtree.h"
     14
     15#define LOC     QString("DVBDevTree: ")
     16#define LOC_ERR QString("DVBDevTree: ")
     17
     18//////////////////////////////////////// DVBDevSettings
     19
     20DVBDevSettings::DVBDevSettings()
     21    : m_input_id((unsigned int)-1)
     22{
     23}
     24
     25bool DVBDevSettings::Load(unsigned int card_input_id)
     26{
     27    if(card_input_id != m_input_id)
     28    {
     29        m_config.clear();
     30       
     31        // load settings from DB
     32        MSqlQuery query(MSqlQuery::InitCon());
     33        query.prepare("SELECT dtv_dev_id, value "
     34                      "FROM dtv_device_config "
     35                      "WHERE cardinputid = :INPUTID");
     36        query.bindValue(":INPUTID", card_input_id);
     37        if(query.exec() && query.isActive())
     38            while(query.next())
     39                m_config[query.value(0).toInt()] = query.value(1).toDouble();
     40       
     41        m_input_id = card_input_id;
     42    }
     43
     44    return true;
     45}
     46
     47bool DVBDevSettings::Store(unsigned int card_input_id) const
     48{
     49    {
     50        // clear out previous settings
     51        MSqlQuery query(MSqlQuery::InitCon());
     52        query.prepare("DELETE from dtv_device_config"
     53                      " WHERE cardinputid = :INPUTID");
     54        query.bindValue(":INPUTID", card_input_id);
     55        if(!query.exec())
     56            return false;
     57    }
     58
     59    {
     60        // insert new settings
     61        MSqlQuery query(MSqlQuery::InitCon());
     62        query.prepare("INSERT INTO dtv_device_config"
     63                      " (cardinputid, dtv_dev_id, value) VALUES"
     64                      " (:INPUTID, :DEV_ID, :VALUE)");
     65        for(CONFIG::const_iterator i = m_config.begin(); i != m_config.end(); i++)
     66        {
     67            query.bindValue(":INPUTID", card_input_id);
     68            query.bindValue(":DEV_ID", i->first);
     69            query.bindValue(":VALUE", i->second);
     70            if(!query.exec())
     71                return false;
     72        }
     73    }
     74
     75    return true;
     76}
     77
     78double DVBDevSettings::GetValue(int dtv_dev_id) const
     79{
     80    double ret = 0.0;
     81    CONFIG::const_iterator cfg = m_config.find(dtv_dev_id);
     82    if(cfg != m_config.end())
     83        ret = cfg->second;
     84    return ret;
     85}
     86
     87void DVBDevSettings::SetValue(int dtv_dev_id, double value)
     88{
     89    m_config[dtv_dev_id] = value;
     90    m_input_id = (unsigned int)-1;
     91}
     92
     93//////////////////////////////////////// DVBDev
     94
     95DVBDevTree* DVBDev::FindTree(unsigned int card_id)
     96{
     97    return m_trees.FindTree(card_id);
     98}
     99
     100void DVBDev::InvalidateTrees()
     101{
     102    m_trees.InvalidateTrees();
     103}
     104
     105DVBDevTrees DVBDev::m_trees;
     106
     107//////////////////////////////////////// DVBDevTrees
     108
     109DVBDevTrees::~DVBDevTrees()
     110{
     111    InvalidateTrees();
     112}
     113
     114DVBDevTree* DVBDevTrees::FindTree(unsigned int card_id)
     115{
     116    QMutexLocker lock(&m_trees_lock);
     117
     118    DVBDevTree* tree = NULL;
     119    TREES::iterator i = m_trees.find(card_id);
     120    if(i == m_trees.end())
     121    {
     122        tree = new DVBDevTree;
     123        tree->Load(card_id);
     124        m_trees.insert(std::make_pair(card_id, tree));
     125    }
     126    else
     127        tree = i->second;
     128    return tree;
     129}
     130
     131void DVBDevTrees::InvalidateTrees()
     132{
     133    QMutexLocker lock(&m_trees_lock);
     134    for(TREES::iterator i = m_trees.begin(); i != m_trees.end(); i++)
     135        delete i->second;
     136    m_trees.clear();
     137}
     138
     139//////////////////////////////////////// DVBDevTree
     140
     141DVBDevTree::DVBDevTree()
     142    : m_fd_frontend(-1), m_root(NULL), m_next_cnt(0)
     143{
     144    Reset();
     145}
     146
     147DVBDevTree::~DVBDevTree()
     148{
     149    delete m_root;
     150}
     151
     152bool DVBDevTree::Load(unsigned int card_id)
     153{
     154    // clear children
     155    delete m_root;
     156    m_delete.clear();
     157    m_root = NULL;
     158
     159    // lookup configuration for this card
     160    MSqlQuery query(MSqlQuery::InitCon());
     161    query.prepare("SELECT dtv_dev_id FROM capturecard "
     162                  "WHERE cardid = :CARDID");
     163    query.bindValue(":CARDID", card_id);
     164
     165    if(query.exec() && query.next())
     166        m_root = DVBDevDevice::CreateById(*this, query.value(0).toInt());
     167    else
     168        VERBOSE(VB_IMPORTANT, LOC_ERR +
     169                QString("No device tree for cardid %1").arg(card_id));
     170
     171    return m_root != NULL;
     172}
     173
     174bool DVBDevTree::Store(unsigned int card_id)
     175{
     176    // apply pending node deletions
     177    if(!m_delete.empty())
     178    {
     179        MSqlQuery query(MSqlQuery::InitCon());
     180        query.prepare("DELETE FROM dtv_device_tree WHERE dtv_dev_id = :DEVID");
     181        MSqlQuery squery(MSqlQuery::InitCon());
     182        squery.prepare("DELETE FROM dtv_device_config WHERE dtv_dev_id = :DEVID");
     183        for(list<unsigned int>::const_iterator i = m_delete.begin(); i != m_delete.end(); i++)
     184        {
     185            query.bindValue(":DEVID", *i);
     186            query.exec();
     187            squery.bindValue(":DEVID", *i);
     188            squery.exec();
     189        }
     190        m_delete.clear();
     191    }
     192
     193    // store changed and new nodes
     194    bool success = true;
     195    if(m_root)
     196        success = m_root->Store();
     197    MSqlQuery query(MSqlQuery::InitCon());
     198    query.prepare("UPDATE capturecard"
     199                  " SET dtv_dev_id = :DEVID"
     200                  " WHERE cardid = :CARDID");
     201    if(m_root)
     202        query.bindValue(":DEVID", m_root->DeviceID());
     203    query.bindValue(":CARDID", card_id);
     204    return success && query.exec();
     205}
     206
     207bool DVBDevTree::SetTone(bool on)
     208{
     209    bool success = false;
     210    for(unsigned int retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
     211    {
     212        if (ioctl(m_fd_frontend, FE_SET_TONE,
     213                  on ? SEC_TONE_ON : SEC_TONE_OFF) == 0)
     214            success = true;
     215        else
     216            usleep(TIMEOUT_WAIT);
     217    }
     218    if(!success)
     219        VERBOSE(VB_IMPORTANT, LOC_ERR + "FE_SET_TONE failed" + ENO);
     220    return success;
     221}
     222
     223bool DVBDevTree::MiniDiseqc(fe_sec_mini_cmd cmd)
     224{
     225    bool success = false;
     226    for(unsigned int retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
     227    {
     228        if (ioctl(m_fd_frontend, FE_DISEQC_SEND_BURST, cmd) == 0)
     229            success = true;
     230        else
     231            usleep(TIMEOUT_WAIT);
     232    }
     233    if(!success)
     234        VERBOSE(VB_IMPORTANT, LOC_ERR + "FE_DISEQC_SEND_BURST failed" + ENO);
     235    return success;
     236}
     237
     238bool DVBDevTree::SendDiseqc(const dvb_diseqc_master_cmd& cmd)
     239{
     240    bool success = false;
     241    for(unsigned int retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
     242    {
     243        if (ioctl(m_fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd) == 0)
     244            success = true;
     245        else
     246            usleep(TIMEOUT_WAIT);
     247    }
     248    if(!success)
     249        VERBOSE(VB_IMPORTANT, LOC_ERR +
     250                "FE_DISEQC_SEND_MASTER_CMD failed" + ENO);
     251    return success;
     252}
     253
     254bool DVBDevTree::Execute(const DVBDevSettings& settings,
     255                         const DVBTuning& tuning)
     256{
     257    if(m_root)
     258    {
     259        // apply any voltage change
     260        ApplyVoltage(settings, tuning);
     261
     262        // turn off tone burst first if commands need to be sent
     263        if(m_root->NeedsCommand(settings))
     264        {
     265            SetTone(false);
     266            usleep(DISEQC_SHORT_WAIT);
     267        }
     268
     269        return m_root->Execute(settings, tuning);
     270    }
     271    else
     272    {
     273        VERBOSE(VB_IMPORTANT, LOC_ERR + "No root device tree node!");
     274        return false;
     275    }
     276}
     277
     278void DVBDevTree::Reset()
     279{
     280    if(m_root)
     281        m_root->Reset();
     282    m_last_voltage = (fe_sec_voltage)-1;
     283}
     284
     285DVBDevRotor* DVBDevTree::FindRotor(const DVBDevSettings& settings,
     286                                   unsigned int index)
     287{
     288    DVBDevRotor* rotor = NULL;
     289    DVBDevDevice* node = m_root;
     290    unsigned int count = 0;
     291    while(node)
     292    {
     293        rotor = dynamic_cast<DVBDevRotor*>(node);
     294        if(rotor && ++count > index)
     295            break;
     296        node = node->SelectedChild(settings);
     297    }
     298    return rotor;
     299}
     300
     301DVBDevLnb* DVBDevTree::FindLNB(const DVBDevSettings& settings)
     302{
     303    DVBDevLnb* lnb = NULL;
     304    DVBDevDevice* node = m_root;
     305    while(node)
     306    {
     307        lnb = dynamic_cast<DVBDevLnb*>(node);
     308        if(lnb)
     309            break;
     310        node = node->SelectedChild(settings);
     311    }
     312    return lnb;
     313}
     314
     315DVBDevDevice* DVBDevTree::FindDevice(int dev_id)
     316{
     317    DVBDevDevice* dev = NULL;
     318    if(m_root)
     319        dev = m_root->FindDevice(dev_id);
     320    return dev;
     321}
     322
     323void DVBDevTree::SetRoot(DVBDevDevice* root)
     324{
     325    delete m_root;
     326    m_root = root;
     327}
     328
     329bool DVBDevTree::SendCommand(unsigned int adr,
     330                             unsigned int cmd,
     331                             unsigned int repeats,
     332                             unsigned int data_len,
     333                             unsigned char* data)
     334{
     335    // check payload validity
     336    if(data_len > 3 || (data_len > 0 && data == NULL))
     337    {
     338        VERBOSE(VB_IMPORTANT, LOC_ERR + "Bad DiSEqC command");
     339        return false;
     340    }
     341
     342    // prepare command
     343    dvb_diseqc_master_cmd mcmd;
     344    mcmd.msg[0] = DISEQC_FRM;
     345    mcmd.msg[1] = adr;
     346    mcmd.msg[2] = cmd;
     347    mcmd.msg_len = data_len + 3;
     348    if(data_len > 0)
     349        memcpy(mcmd.msg+3, data, data_len);
     350
     351    // diagnostic
     352    QString cmdstr;
     353    for(unsigned int byte = 0; byte < mcmd.msg_len; byte++)
     354        cmdstr += QString("%1 ").arg(mcmd.msg[byte], 2, 16);
     355    VERBOSE(VB_CHANNEL, LOC + "Sending DiSEqC Command: " + cmdstr);
     356
     357    // send the command
     358    for(unsigned int i = 0; i <= repeats; i++)
     359    {
     360        if (!SendDiseqc(mcmd))
     361        {
     362            VERBOSE(VB_IMPORTANT, LOC_ERR + "DiSEqC command failed" + ENO);
     363            return false;
     364        }
     365        mcmd.msg[0] |= DISEQC_FRM_REPEAT;
     366        usleep(DISEQC_SHORT_WAIT);
     367    }
     368
     369    return true;
     370}
     371
     372bool DVBDevTree::ResetDiseqc(bool hard_reset)
     373{
     374    Reset();
     375
     376    VERBOSE(VB_CHANNEL, LOC + "Resetting DiSEqC Bus");
     377
     378    // issue a global reset command
     379    if(!SendCommand(DISEQC_ADR_ALL, DISEQC_CMD_RESET))
     380    {
     381        VERBOSE(VB_IMPORTANT, LOC_ERR +
     382                "DiSEqC reset failed" + ENO);
     383        return false;
     384    }
     385    usleep(DISEQC_LONG_WAIT);
     386
     387    // power cycle the bus if requested
     388    if(hard_reset)
     389    {
     390        VERBOSE(VB_CHANNEL, LOC + "Power-cycling DiSEqC Bus");
     391        SetVoltage(SEC_VOLTAGE_OFF);
     392        usleep(DISEQC_LONG_WAIT);
     393        SetVoltage(SEC_VOLTAGE_18);
     394        usleep(DISEQC_LONG_WAIT);
     395    }
     396
     397    return true;
     398}
     399
     400void DVBDevTree::Open(int fd_frontend)
     401{
     402    m_fd_frontend = fd_frontend;
     403    ResetDiseqc(true);
     404}
     405
     406bool DVBDevTree::SetVoltage(fe_sec_voltage voltage)
     407{
     408    bool success = true;
     409    if(voltage != m_last_voltage)
     410    {
     411        int volts = 0;
     412        if(voltage == SEC_VOLTAGE_18)
     413            volts = 18;
     414        else if(voltage == SEC_VOLTAGE_13)
     415            volts = 13;
     416        VERBOSE(VB_CHANNEL, LOC +
     417                QString("Changing LNB voltage to %1V").arg(volts));
     418
     419        success = false;
     420        for(unsigned int retry = 0; !success && retry < TIMEOUT_RETRIES; retry++)
     421        {
     422            if (ioctl(m_fd_frontend, FE_SET_VOLTAGE, voltage) == 0)
     423                success = true;
     424            else
     425                usleep(TIMEOUT_WAIT);
     426        }
     427
     428        if(!success)
     429            VERBOSE(VB_IMPORTANT, LOC_ERR + "FE_SET_VOLTAGE failed" + ENO);
     430        else
     431            m_last_voltage = voltage;
     432    }
     433
     434    return success;
     435}
     436
     437bool DVBDevTree::ApplyVoltage(const DVBDevSettings& settings,
     438                              const DVBTuning& tuning)
     439{
     440    fe_sec_voltage voltage = SEC_VOLTAGE_18;
     441    if(m_root)
     442        voltage = m_root->GetVoltage(settings, tuning);
     443    return SetVoltage(voltage);
     444}
     445
     446//////////////////////////////////////// DVBDevDevice
     447
     448DVBDevDevice::DVBDevDevice(DVBDevTree& tree, int dtv_dev_id)
     449    : m_dtv_dev_id(dtv_dev_id), m_tree(tree), m_parent(NULL), m_ordinal(0),
     450      m_repeat(0)
     451{
     452}
     453
     454DVBDevDevice::~DVBDevDevice()
     455{
     456    if(DeviceID() >= 0)
     457        m_tree.AddDeferredDelete(DeviceID());
     458}
     459
     460DVBDevDevice* DVBDevDevice::FindDevice(int dev_id)
     461{
     462    DVBDevDevice* dev = NULL;
     463
     464    if(m_dtv_dev_id == dev_id)
     465        dev = this;
     466
     467    unsigned int num_children = NumChildren();
     468    for(unsigned int ch = 0; !dev && ch < num_children; ch++)
     469    {
     470        DVBDevDevice* child = GetChild(ch);
     471        if(child)
     472        {
     473            if(child->DeviceID() == dev_id)
     474                dev = child;
     475            else
     476                dev = child->FindDevice(dev_id);
     477        }
     478    }
     479    return dev;
     480}
     481
     482DVBDevDevice* DVBDevDevice::CreateById(DVBDevTree& tree,
     483                                       int dtv_dev_id)
     484{
     485    DVBDevDevice* node = NULL;
     486
     487    // load settings from DB
     488    MSqlQuery query(MSqlQuery::InitCon());
     489    query.prepare("SELECT dtv_dev_type, dtv_dev_descr"
     490                  " FROM dtv_device_tree"
     491                  " WHERE dtv_dev_id = :DTV_DEV_ID");
     492    query.bindValue(":DTV_DEV_ID", dtv_dev_id);
     493
     494    if(query.exec() && query.next())
     495    {
     496        dvbdev_t type = DevTypeFromString(query.value(0).toString());
     497        node = CreateByType(tree, type, dtv_dev_id);
     498 
     499        QString descr = query.value(1).toString();
     500        if(node)
     501        {
     502            node->SetDescription(descr);
     503            node->Load();
     504        }
     505    }
     506
     507    return node;
     508}
     509
     510DVBDevDevice* DVBDevDevice::CreateByType(DVBDevTree& tree,
     511                                         dvbdev_t type,
     512                                         int dev_id)
     513{
     514    if(dev_id == -1)
     515        dev_id = tree.NextFakeID();
     516
     517    DVBDevDevice* node = NULL;
     518    switch(type)
     519    {
     520    case DVBDEV_SWITCH:
     521        node = new DVBDevSwitch(tree, dev_id);
     522        if(node)
     523            node->SetDescription("Switch");
     524        break;
     525    case DVBDEV_ROTOR:
     526        node = new DVBDevRotor(tree, dev_id);
     527        if(node)
     528            node->SetDescription("Rotor");
     529        break;
     530    case DVBDEV_LNB:
     531        node = new DVBDevLnb(tree, dev_id);
     532        if(node)
     533            node->SetDescription("LNB");
     534        break;
     535    default:
     536        break;
     537    }
     538    if(node)
     539        node->SetDeviceType(type);
     540    return node;
     541}
     542
     543//////////////////////////////////////// DVBDevSwitch
     544
     545DVBDevSwitch::DVBDevSwitch(DVBDevTree& tree,
     546                           int dtv_dev_id)
     547    : DVBDevDevice(tree, dtv_dev_id),
     548      m_type(SWITCH_TONE), m_num_ports(2)
     549{
     550    m_children.resize(m_num_ports);
     551    for(unsigned int i = 0; i < m_num_ports; i++)
     552        m_children[i] = NULL;
     553    Reset();
     554}
     555
     556DVBDevSwitch::~DVBDevSwitch()
     557{
     558    for(CHILDREN::iterator i = m_children.begin(); i != m_children.end(); i++)
     559        delete *i;
     560}
     561
     562bool DVBDevSwitch::Execute(const DVBDevSettings& settings,
     563                           const DVBTuning& tuning)
     564{
     565    bool success = true;
     566
     567    // sanity check switch position
     568    unsigned int pos;
     569    if(!GetPosition(settings, pos))
     570        return false;
     571
     572    // determine if switch command needs to be sent based on last pos
     573    if(m_last_pos != pos)
     574    {
     575        // perform switching
     576        switch(m_type)
     577        {
     578        case SWITCH_TONE:
     579            success = ExecuteTone(settings, tuning, pos);
     580            break;
     581        case SWITCH_DISEQC_COMMITTED:
     582        case SWITCH_DISEQC_UNCOMMITTED:
     583            success = ExecuteDiseqc(settings, tuning, pos);
     584            break;
     585        case SWITCH_LEGACY_SW21:
     586        case SWITCH_LEGACY_SW42:
     587        case SWITCH_LEGACY_SW64:
     588            success = ExecuteLegacy(settings, tuning, pos);
     589            break;
     590        default:
     591            success = false;
     592            VERBOSE(VB_IMPORTANT, LOC_ERR +
     593                    QString("Unknown switch type (%1)")
     594                    .arg((unsigned int)m_type));
     595            break;
     596        }
     597
     598        // if a child device will be sending a diseqc command, wait 100ms
     599        if(m_children[pos]->NeedsCommand(settings))
     600        {
     601            VERBOSE(VB_CHANNEL, LOC + "Waiting for switch");
     602            usleep(DISEQC_LONG_WAIT);
     603        }
     604
     605        m_last_pos = pos;
     606    }
     607
     608    // chain to child if the switch was successful
     609    if(success)
     610        success = m_children[pos]->Execute(settings, tuning);
     611   
     612    return success;
     613}
     614
     615void DVBDevSwitch::Reset()
     616{
     617    m_last_pos = (unsigned int)-1;
     618    for(CHILDREN::iterator i = m_children.begin(); i != m_children.end(); i++)
     619        if(*i)
     620            (*i)->Reset();
     621}
     622
     623bool DVBDevSwitch::NeedsCommand(const DVBDevSettings& settings) const
     624{
     625    // sanity check switch position
     626    unsigned int pos;
     627    if(!GetPosition(settings, pos))
     628        return false;
     629
     630    // if position is changing, a command is definitely needed
     631    if(pos != m_last_pos)
     632        return true;
     633
     634    // otherwise, the child that will be selected may need a command
     635    else
     636        return m_children[pos]->NeedsCommand(settings);
     637}
     638
     639DVBDevDevice* DVBDevSwitch::SelectedChild(const DVBDevSettings& settings) const
     640{
     641    DVBDevDevice* child = NULL;
     642    unsigned int pos;
     643    if(GetPosition(settings, pos))
     644        child = m_children[pos];
     645    return child;
     646}
     647
     648unsigned int DVBDevSwitch::NumChildren() const
     649{
     650    return m_num_ports;
     651}
     652
     653DVBDevDevice* DVBDevSwitch::GetChild(unsigned int ordinal)
     654{
     655    if(ordinal < m_children.size())
     656        return m_children[ordinal];
     657    else
     658        return NULL;
     659}
     660
     661bool DVBDevSwitch::SetChild(unsigned int ordinal, DVBDevDevice* device)
     662{
     663    if(ordinal < m_children.size())
     664    {
     665        delete m_children[ordinal];
     666        m_children[ordinal] = device;
     667        if(device)
     668        {
     669            device->SetOrdinal(ordinal);
     670            device->SetParent(this);
     671        }
     672        return true;
     673    }
     674    return false;
     675}
     676
     677fe_sec_voltage DVBDevSwitch::GetVoltage(const DVBDevSettings& settings,
     678                                        const DVBTuning& tuning) const
     679{
     680    fe_sec_voltage voltage = SEC_VOLTAGE_18;
     681    DVBDevDevice* child = SelectedChild(settings);
     682    if(child)
     683        voltage = child->GetVoltage(settings, tuning);
     684    return voltage;
     685}
     686
     687bool DVBDevSwitch::Load()
     688{
     689    // clear old children
     690    for(CHILDREN::iterator i = m_children.begin(); i != m_children.end(); i++)
     691        delete *i;
     692    m_children.clear();
     693
     694    // populate switch parameters from db
     695    {
     696        MSqlQuery query(MSqlQuery::InitCon());
     697        query.prepare("SELECT dtv_dev_subtype, switch_ports"
     698                      " FROM dtv_device_tree"
     699                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     700        query.bindValue(":DTV_DEV_ID", DeviceID());
     701       
     702        if(query.exec() && query.next())
     703        {
     704            m_type = SwitchTypeFromString(query.value(0).toString());
     705            m_num_ports = query.value(1).toInt();
     706            m_children.resize(m_num_ports);
     707            for(unsigned int i=0; i < m_num_ports; i++)
     708                m_children[i] = NULL;
     709        }
     710    }
     711
     712    // load children from db
     713    {
     714        MSqlQuery query(MSqlQuery::InitCon());
     715        query.prepare("SELECT dtv_dev_id, ordinal FROM dtv_device_tree "
     716                      "WHERE parent = :DTV_DEV_ID");
     717        query.bindValue(":DTV_DEV_ID", DeviceID());
     718        if(query.exec())
     719        {
     720            while(query.next())
     721            {
     722                unsigned int child_dev_id = query.value(0).toInt();
     723                unsigned int ordinal = query.value(1).toInt();
     724                DVBDevDevice* child = CreateById(m_tree, child_dev_id);
     725                if(!SetChild(ordinal, child))
     726                {
     727                    VERBOSE(VB_IMPORTANT, LOC_ERR +
     728                            QString("Switch port out of range (%d > %d)")
     729                            .arg(ordinal + 1)
     730                            .arg(m_num_ports));
     731                    delete child;
     732                }
     733            }
     734        }
     735    }
     736
     737    return true;
     738}
     739
     740bool DVBDevSwitch::Store()
     741{
     742    QString type = SwitchTypeToString(m_type);
     743    MSqlQuery query(MSqlQuery::InitCon());
     744
     745    // insert new or update old
     746    if(m_dtv_dev_id >= 0)
     747    {
     748        query.prepare("UPDATE dtv_device_tree"
     749                      " SET parent = :PARENT,"
     750                      " ordinal = :ORDINAL,"
     751                      " dtv_dev_type = 'switch',"
     752                      " dtv_dev_descr = :DESCR,"
     753                      " dtv_dev_subtype = :TYPE,"
     754                      " switch_ports = :PORTS"
     755                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     756    }
     757    else
     758    {
     759        query.prepare("INSERT INTO dtv_device_tree"
     760                      " (parent, ordinal, dtv_dev_type, dtv_dev_descr,"
     761                      " dtv_dev_subtype, switch_ports)"
     762                      " VALUES (:PARENT, :ORDINAL, 'switch', :DESCR,"
     763                      " :TYPE, :PORTS)");
     764    }
     765    if(m_parent)
     766        query.bindValue(":PARENT", m_parent->DeviceID());
     767    query.bindValue(":ORDINAL", m_ordinal);
     768    query.bindValue(":DESCR", GetDescription());
     769    query.bindValue(":TYPE", type);
     770    query.bindValue(":PORTS", m_num_ports);
     771    query.bindValue(":DTV_DEV_ID", DeviceID());
     772
     773    // chain to children
     774    bool success = query.exec();
     775    if(success)
     776    {
     777        if(m_dtv_dev_id < 0)
     778            m_dtv_dev_id = query.lastInsertId().toInt();
     779        for(unsigned int ch = 0; ch < m_children.size(); ch++)
     780            if(m_children[ch])
     781                success = success && m_children[ch]->Store();
     782    }
     783
     784    return success;
     785}
     786
     787void DVBDevSwitch::SetNumPorts(unsigned int num_ports)
     788{
     789    unsigned int old_num = m_children.size();
     790    if(old_num > num_ports)
     791    {
     792        for(unsigned int ch = num_ports; ch < old_num; ch++)
     793            delete m_children[ch];
     794        m_children.resize(num_ports);
     795    }
     796    else if(old_num < num_ports)
     797    {
     798        m_children.resize(num_ports);
     799        for(unsigned int ch = old_num; ch < num_ports; ch++)
     800            m_children[ch] = NULL;
     801    }
     802    m_num_ports = num_ports;
     803}
     804
     805bool DVBDevSwitch::ExecuteLegacy(const DVBDevSettings& settings,
     806                                 const DVBTuning& tuning,
     807                                 unsigned int pos)
     808{
     809#ifdef FE_DISHNETWORK_SEND_LEGACY_CMD
     810
     811    static const unsigned char sw21_cmds[] = { 0x34, 0x65 };
     812    static const unsigned char sw42_cmds[] = { 0x46, 0x17 };
     813    static const unsigned char sw64_v_cmds[] = { 0x39, 0x4b, 0x0d };
     814    static const unsigned char sw64_h_cmds[] = { 0x1a, 0x5c, 0x2e };
     815
     816    const unsigned char *cmds = NULL;
     817    unsigned int num_ports = 0;
     818
     819    // determine polarity from lnb
     820    bool horizontal = false;
     821    DVBDevLnb* lnb = m_tree.FindLNB(settings);
     822    if(lnb)
     823        horizontal = lnb->IsHorizontal(tuning);
     824
     825    // get command table for this switch
     826    switch (m_type)
     827    {
     828    case SWITCH_LEGACY_SW21:
     829        cmds = sw21_cmds;
     830        num_ports = 2;
     831        break;
     832    case SWITCH_LEGACY_SW42:
     833        cmds = sw42_cmds;
     834        num_ports = 2;
     835        break;
     836    case SWITCH_LEGACY_SW64:
     837        if (horizontal)
     838            cmds = sw64_h_cmds;
     839        else
     840            cmds = sw64_v_cmds;
     841        num_ports = 3;
     842        break;
     843    default:
     844        return false;
     845    }
     846    pos %= num_ports;
     847
     848    VERBOSE(VB_CHANNEL, LOC + QString("Changing to Legacy switch port %1/%2")
     849            .arg(pos+1)
     850            .arg(num_ports));
     851
     852    // send command
     853    if (ioctl(m_tree.FrontendFD(), FE_DISHNETWORK_SEND_LEGACY_CMD, cmds[pos]) == -1)
     854    {
     855        VERBOSE(VB_IMPORTANT, LOC_ERR +
     856                "FE_DISHNETWORK_SEND_LEGACY_CMD failed" + ENO);
     857        return false;
     858    }
     859
     860    return true;
     861
     862#else
     863
     864    VERBOSE(VB_IMPORTANT, LOC_ERR +
     865            "DVB API does not support FE_DISHNETWORK_SEND_LEGACY_CMD.");
     866    return false;
     867
     868#endif
     869}
     870
     871bool DVBDevSwitch::ExecuteTone(const DVBDevSettings& /*settings*/,
     872                               const DVBTuning& /*tuning*/,
     873                               unsigned int pos)
     874{
     875    VERBOSE(VB_CHANNEL, LOC + QString("Changing to Tone switch port %1/2")
     876            .arg(pos+1));
     877
     878    bool success = m_tree.MiniDiseqc(pos == 0 ? SEC_MINI_A : SEC_MINI_B);
     879    if(!success)
     880        VERBOSE(VB_IMPORTANT, LOC_ERR + "Setting Tone Switch failed." + ENO);
     881    return success;
     882}
     883
     884bool DVBDevSwitch::ExecuteDiseqc(const DVBDevSettings& settings,
     885                                 const DVBTuning& tuning,
     886                                 unsigned int pos)
     887{
     888    // retrieve LNB info
     889    bool high_band = false;
     890    bool horizontal = false;
     891    DVBDevLnb* lnb = m_tree.FindLNB(settings);
     892    if(lnb)
     893    {
     894        high_band = lnb->IsHighBand(tuning);
     895        horizontal = lnb->IsHorizontal(tuning);
     896    }
     897
     898    // check number of ports
     899    if(m_type == SWITCH_DISEQC_COMMITTED && m_num_ports > 4 ||
     900       m_type == SWITCH_DISEQC_UNCOMMITTED && m_num_ports > 16)
     901    {
     902        VERBOSE(VB_IMPORTANT, LOC_ERR +
     903                QString("Invalid number of ports for DiSEqC 1.x Switch (%1)")
     904                .arg(m_num_ports));
     905        return false;
     906    }
     907
     908    // build command
     909    unsigned int cmd;
     910    unsigned char data;
     911    if(m_type == SWITCH_DISEQC_UNCOMMITTED)
     912    {
     913        cmd = DISEQC_CMD_WRITE_N1;
     914        data = pos;
     915    }
     916    else
     917    {
     918        cmd = DISEQC_CMD_WRITE_N0;
     919        data = ((pos << 2) |
     920                (horizontal ? 2 : 0) |
     921                (high_band ? 1 : 0));
     922    }
     923    data |= 0xf0;
     924
     925    VERBOSE(VB_CHANNEL, LOC +
     926            QString("Changing to DiSEqC switch port %1/%2")
     927            .arg(pos+1)
     928            .arg(m_num_ports));
     929
     930    return m_tree.SendCommand(DISEQC_ADR_SW_ALL,
     931                              cmd,
     932                              m_repeat,
     933                              1,
     934                              &data);
     935}
     936
     937bool DVBDevSwitch::GetPosition(const DVBDevSettings& settings,
     938                               unsigned int& pos) const
     939{
     940    pos = (unsigned int)settings.GetValue(m_dtv_dev_id);
     941    if(pos >= m_num_ports)
     942    {
     943        VERBOSE(VB_IMPORTANT, LOC_ERR +
     944                QString("Port number out of range (%1 > %2)")
     945                .arg(pos+1)
     946                .arg(m_num_ports));
     947        return false;
     948    }
     949    if(m_children[pos] == NULL)
     950    {
     951        VERBOSE(VB_IMPORTANT, LOC_ERR +
     952                QString("Port has no connected devices configured (%1)")
     953                .arg(pos+1));
     954        return false;
     955    }
     956
     957    return true;
     958}
     959
     960//////////////////////////////////////// DVBDevRotor
     961
     962DVBDevRotor::DVBDevRotor(DVBDevTree& tree,
     963                         int dtv_dev_id)
     964    : DVBDevDevice(tree, dtv_dev_id),
     965      m_type(ROTOR_DISEQC_1_3), m_speed_hi(2.5), m_speed_lo(1.9),
     966      m_child(NULL), m_last_position(0.0), m_last_azimuth(0.0),
     967      m_reset(true), m_move_time(0.0), m_last_pos_known(false)
     968{
     969    Reset();
     970}
     971
     972DVBDevRotor::DVBDevRotor::~DVBDevRotor()
     973{
     974    delete m_child;
     975}
     976
     977bool DVBDevRotor::Execute(const DVBDevSettings& settings,
     978                          const DVBTuning& tuning)
     979{
     980    bool success = true;
     981
     982    double position = settings.GetValue(m_dtv_dev_id);
     983    if(m_reset || position != m_last_position)
     984    {
     985        switch(m_type)
     986        {
     987        case ROTOR_DISEQC_1_2:
     988            success = ExecuteRotor(settings, tuning, position);
     989            break;
     990        case ROTOR_DISEQC_1_3:
     991            success = ExecuteUSALS(settings, tuning, position);
     992            break;
     993        default:
     994            success = false;
     995            VERBOSE(VB_IMPORTANT, LOC_ERR +
     996                    QString("Unknown rotor type (%1)")
     997                    .arg((unsigned int)m_type));
     998            break;
     999        }
     1000       
     1001        m_last_position = position;
     1002        m_reset = false;
     1003    }
     1004
     1005    // chain to child
     1006    if(success && m_child)
     1007        success = m_child->Execute(settings, tuning);
     1008
     1009    return success;
     1010}
     1011
     1012void DVBDevRotor::Reset()
     1013{
     1014    m_reset = true;
     1015    if(m_child)
     1016        m_child->Reset();
     1017}
     1018
     1019bool DVBDevRotor::NeedsCommand(const DVBDevSettings& settings) const
     1020{
     1021    double position = settings.GetValue(m_dtv_dev_id);
     1022    if(m_reset || position != m_last_position)
     1023        return true;
     1024    else if(m_child)
     1025        return m_child->NeedsCommand(settings);
     1026    else
     1027        return false;
     1028}
     1029
     1030DVBDevDevice* DVBDevRotor::SelectedChild(const DVBDevSettings& /*settings*/) const
     1031{
     1032    return m_child;
     1033}
     1034
     1035bool DVBDevRotor::SetChild(unsigned int ordinal, DVBDevDevice* device)
     1036{
     1037    if(ordinal == 0)
     1038    {
     1039        delete m_child;
     1040        m_child = device;
     1041        if(m_child)
     1042        {
     1043            m_child->SetOrdinal(ordinal);
     1044            m_child->SetParent(this);
     1045        }
     1046        return true;
     1047    }
     1048    return false;
     1049}
     1050
     1051fe_sec_voltage DVBDevRotor::GetVoltage(const DVBDevSettings& settings,
     1052                                       const DVBTuning& tuning) const
     1053{
     1054    fe_sec_voltage voltage = SEC_VOLTAGE_18;
     1055
     1056    double position = settings.GetValue(m_dtv_dev_id);
     1057    bool moving = Progress() < 1.0 || (m_reset || position != m_last_position);
     1058
     1059    // override voltage if the last position is known and the rotor is moving
     1060    if(m_last_pos_known && moving)
     1061        VERBOSE(VB_CHANNEL, LOC +
     1062                "Overriding voltage to 18V for faster rotor movement");
     1063    else if(m_child)
     1064        voltage = m_child->GetVoltage(settings, tuning);
     1065
     1066    return voltage;
     1067}
     1068
     1069bool DVBDevRotor::Load()
     1070{
     1071    // populate switch parameters from db
     1072    {
     1073        MSqlQuery query(MSqlQuery::InitCon());
     1074        query.prepare("SELECT dtv_dev_subtype,"
     1075                      " rotor_hi_speed, rotor_lo_speed, rotor_positions"
     1076                      " FROM dtv_device_tree"
     1077                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     1078        query.bindValue(":DTV_DEV_ID", DeviceID());
     1079       
     1080        if(query.exec() && query.next())
     1081        {
     1082            m_type = RotorTypeFromString(query.value(0).toString());
     1083            m_speed_hi = query.value(1).toDouble();
     1084            m_speed_lo = query.value(2).toDouble();
     1085
     1086            // form of "angle1=index1:angle2=index2:..."
     1087            QString positions = query.value(3).toString();
     1088            QStringList pos = QStringList::split(":", positions);
     1089            for(unsigned int i=0; i < pos.count(); i++)
     1090            {
     1091                QStringList eq = QStringList::split("=", pos[i]);
     1092                if(eq.count() == 2)
     1093                    m_posmap[eq[0].toFloat()] = eq[1].toUInt();
     1094            }
     1095        }
     1096    }
     1097
     1098    // load children from db
     1099    delete m_child;
     1100    m_child = NULL;
     1101    {
     1102        MSqlQuery query(MSqlQuery::InitCon());
     1103        query.prepare("SELECT dtv_dev_id FROM dtv_device_tree "
     1104                      "WHERE parent = :DTV_DEV_ID");
     1105        query.bindValue(":DTV_DEV_ID", DeviceID());
     1106       
     1107        if(query.exec() && query.next())
     1108        {
     1109            int child_dev_id = query.value(0).toInt();
     1110            SetChild(0, CreateById(m_tree, child_dev_id));
     1111        }
     1112    }
     1113
     1114    return true;
     1115}
     1116
     1117bool DVBDevRotor::Store()
     1118{
     1119    QString type = RotorTypeToString(m_type);
     1120    QString posmap;
     1121
     1122    if(!m_posmap.empty())
     1123    {
     1124        QStringList pos;
     1125        for(INTPOSMAP::iterator i = m_posmap.begin(); i != m_posmap.end(); i++)
     1126            pos.push_back(QString("%1=%2").arg(i->first).arg(i->second));
     1127        posmap = pos.join(":");
     1128    }
     1129
     1130    MSqlQuery query(MSqlQuery::InitCon());
     1131
     1132    // insert new or update old
     1133    if(m_dtv_dev_id >= 0)
     1134    {
     1135        query.prepare("UPDATE dtv_device_tree"
     1136                      " SET parent = :PARENT,"
     1137                      " ordinal = :ORDINAL,"
     1138                      " dtv_dev_type = 'rotor',"
     1139                      " dtv_dev_descr = :DESCR,"
     1140                      " dtv_dev_subtype = :TYPE,"
     1141                      " rotor_hi_speed = :HISPEED,"
     1142                      " rotor_lo_speed = :LOSPEED,"
     1143                      " rotor_positions = :POSMAP"
     1144                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     1145    }
     1146    else
     1147    {
     1148        query.prepare("INSERT INTO dtv_device_tree"
     1149                      " (parent, ordinal, dtv_dev_type, dtv_dev_descr,"
     1150                      " dtv_dev_subtype, rotor_hi_speed,"
     1151                      " rotor_lo_speed, rotor_positions)"
     1152                      " VALUES (:PARENT, :ORDINAL, 'rotor', :DESCR,"
     1153                      " :TYPE, :HISPEED, :LOSPEED, :POSMAP)");
     1154    }
     1155    if(m_parent)
     1156        query.bindValue(":PARENT", m_parent->DeviceID());
     1157    query.bindValue(":ORDINAL", m_ordinal);
     1158    query.bindValue(":DESCR", GetDescription());
     1159    query.bindValue(":TYPE", type);
     1160    query.bindValue(":HISPEED", m_speed_hi);
     1161    query.bindValue(":LOSPEED", m_speed_lo);
     1162    query.bindValue(":POSMAP", posmap);
     1163    query.bindValue(":DTV_DEV_ID", DeviceID());
     1164
     1165    // chain to child
     1166    bool success = query.exec();
     1167    if(success)
     1168    {
     1169        if(m_dtv_dev_id < 0)
     1170            m_dtv_dev_id = query.lastInsertId().toInt();
     1171        if(m_child)
     1172            success = m_child->Store();
     1173    }
     1174
     1175    return success;
     1176}
     1177
     1178double DVBDevRotor::Progress() const
     1179{
     1180    double completed = 1.0;
     1181
     1182    if(m_move_time != 0.0)
     1183    {
     1184        // calculate duration of move
     1185        double speed =
     1186            (m_tree.GetVoltage() == SEC_VOLTAGE_18) ? m_speed_hi : m_speed_lo;
     1187        double change = fabs(m_desired_azimuth - m_last_azimuth);
     1188        double duration = change / speed;
     1189
     1190        // determine completion percentage
     1191        struct timeval curtime;
     1192        gettimeofday(&curtime, NULL);
     1193        double cursecond = curtime.tv_sec + (double)curtime.tv_usec / 1000000;
     1194        double time_since_move = cursecond - m_move_time;
     1195        completed = time_since_move / duration;
     1196
     1197        // move completed, finish up
     1198        if(completed > 1.0)
     1199        {
     1200            completed = 1.0;
     1201            m_move_time = 0.0;
     1202            m_last_pos_known = true;
     1203        }
     1204    }
     1205
     1206    return completed;
     1207}
     1208
     1209DVBDevRotor::POSMAP DVBDevRotor::GetPosMap() const
     1210{
     1211    POSMAP retposmap;
     1212    INTPOSMAP::const_iterator i;
     1213    for(i = m_posmap.begin(); i != m_posmap.end(); i++)
     1214        retposmap.insert(make_pair(i->second, i->first));
     1215    return retposmap;
     1216}
     1217
     1218void DVBDevRotor::SetPosMap(const POSMAP& posmap)
     1219{
     1220    m_posmap.clear();
     1221    POSMAP::const_iterator i;
     1222    for(i = posmap.begin(); i != posmap.end(); i++)
     1223        m_posmap.insert(make_pair(i->second, i->first));
     1224}
     1225
     1226bool DVBDevRotor::ExecuteRotor(const DVBDevSettings& /*settings*/,
     1227                               const DVBTuning& /*tuning*/,
     1228                               double angle)
     1229{
     1230    // determine stored position from position map
     1231    INTPOSMAP::const_iterator i = m_posmap.find(angle);
     1232    unsigned char index;
     1233    if(i != m_posmap.end())
     1234    {
     1235        index = i->second;
     1236        RotorMoving(CalculateAzimuth(angle));
     1237    }
     1238    else
     1239        index = (unsigned int) angle;
     1240
     1241    VERBOSE(VB_CHANNEL, LOC + QString("Rotor - Goto Stored Position %1")
     1242            .arg(index));
     1243
     1244    return m_tree.SendCommand(DISEQC_ADR_POS_AZ,
     1245                              DISEQC_CMD_GOTO_POS,
     1246                              m_repeat,
     1247                              1,
     1248                              &index);
     1249}
     1250
     1251bool DVBDevRotor::ExecuteUSALS(const DVBDevSettings& /*settings*/,
     1252                               const DVBTuning& /*tuning*/,
     1253                               double angle)
     1254{
     1255    double azimuth = CalculateAzimuth(angle);
     1256    RotorMoving(azimuth);
     1257    VERBOSE(VB_CHANNEL, LOC + QString("USALS Rotor - Goto %1 (Azimuth %2)")
     1258            .arg(angle)
     1259            .arg(azimuth));
     1260
     1261    uint az16 = (unsigned int) (abs(azimuth) * 16.0);
     1262    unsigned char cmd[2];
     1263    cmd[0] = ((azimuth > 0.0) ? 0xE0 : 0xD0) | ((az16 >> 8) & 0x0f);
     1264    cmd[1] = (az16 & 0xff);
     1265
     1266    return m_tree.SendCommand(DISEQC_ADR_POS_AZ,
     1267                              DISEQC_CMD_GOTO_X,
     1268                              m_repeat,
     1269                              2,
     1270                              cmd);
     1271}
     1272
     1273#define TO_RADS (M_PI / 180.0)
     1274#define TO_DEC  (180.0 / M_PI)
     1275
     1276double DVBDevRotor::CalculateAzimuth(double angle) const
     1277{
     1278    // Equation lifted from VDR rotor plugin by
     1279    // Thomas Bergwinkl <Thomas.Bergwinkl@t-online.de>
     1280
     1281    // Earth Station Latitude and Longitude in radians
     1282    double P  = gContext->GetSetting("Latitude",  "").toFloat() * TO_RADS;
     1283    double Ue = gContext->GetSetting("Longitude", "").toFloat() * TO_RADS;
     1284
     1285    // Satellite Longitude in radians
     1286    double Us = angle * TO_RADS;
     1287
     1288    double az      = M_PI + atan( tan(Us - Ue) / sin(P) );
     1289    double x       = acos( cos(Us - Ue) * cos(P) );
     1290    double el      = atan( (cos(x) - 0.1513) / sin(x) );
     1291    double tmp_a   = -cos(el) * sin(az);
     1292    double tmp_b   = (sin(el) * cos(P)) - (cos(el) * sin(P) * cos(az));
     1293    double azimuth = atan(tmp_a / tmp_b) * TO_DEC;
     1294
     1295    return azimuth;
     1296}
     1297
     1298double DVBDevRotor::GetApproxAzimuth()
     1299{
     1300    double approx = m_last_azimuth;
     1301
     1302    if(m_move_time != 0.0)
     1303    {
     1304        double change = m_desired_azimuth - m_last_azimuth;
     1305        double progress = Progress();
     1306        approx += (change * progress);
     1307    }
     1308
     1309    return approx;
     1310}
     1311
     1312void DVBDevRotor::RotorMoving(double azimuth)
     1313{
     1314    // set last to approximate current position (or worst case if unknown)
     1315    if(m_last_pos_known)
     1316        m_last_azimuth = GetApproxAzimuth();
     1317    else
     1318        m_last_azimuth = azimuth > 0.0 ? -75.0 : 75.0;
     1319
     1320    // save time and angle of this command
     1321    m_desired_azimuth = azimuth;
     1322    struct timeval curtime;
     1323    gettimeofday(&curtime, NULL);
     1324    m_move_time = curtime.tv_sec + (double)curtime.tv_usec / 1000000;
     1325}
     1326
     1327////////////////////////////////////////
     1328
     1329DVBDevLnb::DVBDevLnb(DVBDevTree& tree,
     1330                     int dtv_dev_id)
     1331    : DVBDevDevice(tree, dtv_dev_id),
     1332      m_type(LNB_VOLTAGE_TONE),
     1333      m_lof_switch(11700000),
     1334      m_lof_hi(10600000),
     1335      m_lof_lo(9750000)
     1336{
     1337    Reset();
     1338}
     1339
     1340bool DVBDevLnb::Execute(const DVBDevSettings& /*settings*/,
     1341                        const DVBTuning& tuning)
     1342{
     1343    // set tone for bandselect
     1344    bool high_band = IsHighBand(tuning);
     1345    if(m_type == LNB_VOLTAGE_TONE)
     1346        m_tree.SetTone(high_band);
     1347
     1348    return true;
     1349}
     1350
     1351void DVBDevLnb::Reset()
     1352{
     1353    // i.e. diseqc lnbs would need reset
     1354}
     1355
     1356bool DVBDevLnb::NeedsCommand(const DVBDevSettings& /*settings*/) const
     1357{
     1358    // i.e. diseqc lnbs would return true
     1359    return false;
     1360}
     1361
     1362fe_sec_voltage DVBDevLnb::GetVoltage(const DVBDevSettings& /*settings*/,
     1363                                     const DVBTuning& tuning) const
     1364{
     1365    fe_sec_voltage voltage = SEC_VOLTAGE_18;
     1366
     1367    if((m_type == LNB_VOLTAGE || m_type == LNB_VOLTAGE_TONE))
     1368        voltage = (IsHorizontal(tuning) ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13);
     1369
     1370    return voltage;
     1371}
     1372
     1373bool DVBDevLnb::Load()
     1374{
     1375    // populate lnb parameters from db
     1376    MSqlQuery query(MSqlQuery::InitCon());
     1377    query.prepare("SELECT dtv_dev_subtype, lnb_lof_switch,"
     1378                  " lnb_lof_hi, lnb_lof_lo"
     1379                  " FROM dtv_device_tree WHERE dtv_dev_id = :DTV_DEV_ID");
     1380    query.bindValue(":DTV_DEV_ID", DeviceID());
     1381   
     1382    if(query.exec() && query.next())
     1383    {
     1384        m_type = LnbTypeFromString(query.value(0).toString());
     1385        m_lof_switch = query.value(1).toInt();
     1386        m_lof_hi = query.value(2).toInt();
     1387        m_lof_lo = query.value(3).toInt();
     1388    }
     1389
     1390    return true;
     1391}
     1392
     1393bool DVBDevLnb::Store()
     1394{
     1395    QString type = LnbTypeToString(m_type);
     1396    MSqlQuery query(MSqlQuery::InitCon());
     1397
     1398    // insert new or update old
     1399    if(m_dtv_dev_id >= 0)
     1400    {
     1401        query.prepare("UPDATE dtv_device_tree"
     1402                      " SET parent = :PARENT,"
     1403                      " ordinal = :ORDINAL,"
     1404                      " dtv_dev_type = 'lnb',"
     1405                      " dtv_dev_descr = :DESCR,"
     1406                      " dtv_dev_subtype = :TYPE,"
     1407                      " lnb_lof_switch = :LOFSW,"
     1408                      " lnb_lof_lo = :LOFLO,"
     1409                      " lnb_lof_hi = :LOFHI"
     1410                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     1411    }
     1412    else
     1413    {
     1414        query.prepare("INSERT INTO dtv_device_tree"
     1415                      " (parent, ordinal, dtv_dev_type, dtv_dev_descr,"
     1416                      " dtv_dev_subtype, lnb_lof_switch,"
     1417                      " lnb_lof_lo, lnb_lof_hi)"
     1418                      " VALUES (:PARENT, :ORDINAL, 'lnb', :DESCR,"
     1419                      " :TYPE, :LOFSW, :LOFLO, :LOFHI)");
     1420    }
     1421    if(m_parent)
     1422        query.bindValue(":PARENT", m_parent->DeviceID());
     1423    query.bindValue(":ORDINAL", m_ordinal);
     1424    query.bindValue(":DESCR", GetDescription());
     1425    query.bindValue(":TYPE", type);
     1426    query.bindValue(":LOFSW", m_lof_switch);
     1427    query.bindValue(":LOFLO", m_lof_lo);
     1428    query.bindValue(":LOFHI", m_lof_hi);
     1429    query.bindValue(":DTV_DEV_ID", DeviceID());
     1430
     1431    // update dev_id
     1432    bool success = query.exec();
     1433    if(success && m_dtv_dev_id < 0)
     1434        m_dtv_dev_id = query.lastInsertId().toInt();
     1435
     1436    return success;
     1437}
     1438
     1439bool DVBDevLnb::IsHighBand(const DVBTuning& tuning) const
     1440{
     1441    bool high_band = false;
     1442    if(m_type == LNB_VOLTAGE_TONE)
     1443        high_band = (tuning.params.frequency > m_lof_switch);
     1444    return high_band;
     1445}
     1446   
     1447bool DVBDevLnb::IsHorizontal(const DVBTuning& tuning) const
     1448{
     1449    char pol = tuning.PolarityChar();
     1450    return (pol == 'h' || pol == 'l');
     1451}
     1452
     1453__u32 DVBDevLnb::GetIF(const DVBDevSettings& /*settings*/,
     1454                       const DVBTuning& tuning) const
     1455{
     1456    unsigned int abs_freq = tuning.params.frequency;
     1457    unsigned int lof = (IsHighBand(tuning) ? m_lof_hi : m_lof_lo);
     1458    return (lof > abs_freq ? lof - abs_freq : abs_freq - lof);
     1459}
     1460
     1461////////////////////////////////////////
     1462
     1463struct TypeTable
     1464{
     1465    const char* name;
     1466    unsigned int value;
     1467};
     1468
     1469static QString TableToString(unsigned int type, const TypeTable* table)
     1470{
     1471    QString str;
     1472    for( ; table->name != NULL; table++)
     1473    {
     1474        if(type == table->value)
     1475        {
     1476            str = table->name;
     1477            break;
     1478        }
     1479    }
     1480    return str;
     1481}
     1482
     1483static unsigned int TableFromString(const QString& type, const TypeTable*table)
     1484{
     1485    for( ; table->name != NULL; table++)
     1486        if(type == table->name)
     1487            break;
     1488    return table->value;
     1489}
     1490
     1491static TypeTable DevTypeTable[] =
     1492{
     1493    { "switch", DVBDEV_SWITCH },
     1494    { "rotor", DVBDEV_ROTOR },
     1495    { "lnb", DVBDEV_LNB },
     1496    { NULL, DVBDEV_LNB }
     1497};
     1498
     1499static TypeTable SwitchTypeTable[] =
     1500{
     1501    { "legacy_sw21", SWITCH_LEGACY_SW21 },
     1502    { "legacy_sw42", SWITCH_LEGACY_SW42 },
     1503    { "legacy_sw64", SWITCH_LEGACY_SW64 },
     1504    { "tone", SWITCH_TONE },
     1505    { "diseqc", SWITCH_DISEQC_COMMITTED },
     1506    { "diseqc_uncom", SWITCH_DISEQC_UNCOMMITTED },
     1507    { NULL, SWITCH_TONE }
     1508};
     1509
     1510static TypeTable RotorTypeTable[] =
     1511{
     1512    { "diseqc_1_2", ROTOR_DISEQC_1_2 },
     1513    { "diseqc_1_3", ROTOR_DISEQC_1_3 },
     1514    { NULL, ROTOR_DISEQC_1_3 }
     1515};
     1516
     1517static TypeTable LnbTypeTable[] =
     1518{
     1519    { "fixed", LNB_FIXED },
     1520    { "voltage", LNB_VOLTAGE },
     1521    { "voltage_tone", LNB_VOLTAGE_TONE },
     1522    { NULL, LNB_VOLTAGE_TONE }
     1523};
     1524
     1525QString DevTypeToString(dvbdev_t type)
     1526{
     1527    return TableToString((unsigned int)type, DevTypeTable);
     1528}
     1529
     1530dvbdev_t DevTypeFromString(const QString& type)
     1531{
     1532    return (dvbdev_t)TableFromString(type, DevTypeTable);
     1533}
     1534
     1535QString SwitchTypeToString(dvbdev_switch_t type)
     1536{
     1537    return TableToString((unsigned int)type, SwitchTypeTable);
     1538}
     1539
     1540dvbdev_switch_t SwitchTypeFromString(const QString& type)
     1541{
     1542    return (dvbdev_switch_t)TableFromString(type, SwitchTypeTable);
     1543}
     1544
     1545QString RotorTypeToString(dvbdev_rotor_t type)
     1546{
     1547    return TableToString((unsigned int)type, RotorTypeTable);
     1548}
     1549
     1550dvbdev_rotor_t RotorTypeFromString(const QString& type)
     1551{
     1552    return (dvbdev_rotor_t)TableFromString(type, RotorTypeTable);
     1553}
     1554
     1555QString LnbTypeToString(dvbdev_lnb_t type)
     1556{
     1557    return TableToString((unsigned int)type, LnbTypeTable);
     1558}
     1559
     1560dvbdev_lnb_t LnbTypeFromString(const QString& type)
     1561{
     1562    return (dvbdev_lnb_t)TableFromString(type, LnbTypeTable);
     1563}
     1564
     1565//////////////////////////////////////// Database Upgrade
     1566
     1567enum OLD_DISEQC_TYPES
     1568{
     1569    DISEQC_SINGLE                  = 0,
     1570    DISEQC_MINI_2                  = 1,
     1571    DISEQC_SWITCH_2_1_0            = 2,
     1572    DISEQC_SWITCH_2_1_1            = 3,
     1573    DISEQC_SWITCH_4_1_0            = 4,
     1574    DISEQC_SWITCH_4_1_1            = 5,
     1575    DISEQC_POSITIONER_1_2          = 6,
     1576    DISEQC_POSITIONER_X            = 7,
     1577    DISEQC_POSITIONER_1_2_SWITCH_2 = 8,
     1578    DISEQC_POSITIONER_X_SWITCH_2   = 9,
     1579    DISEQC_SW21                    = 10,
     1580    DISEQC_SW64                    = 11,
     1581};
     1582
     1583bool DatabaseDiseqcUpgrade()
     1584{
     1585    bool success = true;
     1586    {
     1587        MSqlQuery query(MSqlQuery::InitCon());
     1588        query.prepare("CREATE TABLE dtv_device_config ("
     1589                      "cardinputid int(11) unsigned NOT NULL,"
     1590                      "dtv_dev_id int(11) unsigned NOT NULL,"
     1591                      "value varchar(16) NOT NULL,"
     1592                      "KEY id (cardinputid) )");
     1593        success = success && query.exec();
     1594    }
     1595    {
     1596        MSqlQuery query(MSqlQuery::InitCon());
     1597        query.prepare("CREATE TABLE dtv_device_tree ("
     1598                      "dtv_dev_id int(11) unsigned NOT NULL auto_increment,"
     1599                      "parent int(11) unsigned default NULL,"
     1600                      "ordinal tinyint(3) unsigned NOT NULL,"
     1601                      "dtv_dev_type varchar(16) NOT NULL,"
     1602                      "dtv_dev_subtype varchar(16) NOT NULL,"
     1603                      "dtv_dev_descr varchar(32),"
     1604                      "switch_ports tinyint(3) unsigned default NULL,"
     1605                      "rotor_hi_speed float default NULL,"
     1606                      "rotor_lo_speed float default NULL,"
     1607                      "rotor_positions varchar(256) default NULL,"
     1608                      "lnb_lof_switch int(11) default NULL,"
     1609                      "lnb_lof_hi int(11) default NULL,"
     1610                      "lnb_lof_lo int(11) default NULL,"
     1611                      "PRIMARY KEY (dtv_dev_id),"
     1612                      "KEY parent (parent) )");
     1613        success = success && query.exec();
     1614    }
     1615    {
     1616        MSqlQuery query(MSqlQuery::InitCon());
     1617        query.prepare("ALTER TABLE capturecard"
     1618                      " ADD dtv_dev_id int(10) unsigned default NULL");
     1619        success = success && query.exec();
     1620    }
     1621
     1622    /* Deprecated fields:
     1623       ALTER TABLE capturecard DROP dvb_diseqc_type;
     1624       ALTER TABLE cardinput DROP diseqc_port;
     1625       ALTER TABLE cardinput DROP diseqc_pos;
     1626       ALTER TABLE cardinput DROP lnb_lof_switch;
     1627       ALTER TABLE cardinput DROP lnb_lof_hi;
     1628       ALTER TABLE cardinput DROP lnb_lof_lo; */
     1629
     1630    return success;
     1631}
     1632
     1633// import old diseqc configuration into tree
     1634bool DatabaseDiseqcImport()
     1635{
     1636    MSqlQuery iquery(MSqlQuery::InitCon());
     1637
     1638    iquery.prepare("SELECT cardinputid, diseqc_port, diseqc_pos,"
     1639                   " lnb_lof_switch, lnb_lof_hi, lnb_lof_lo"
     1640                   " FROM cardinput"
     1641                   " WHERE cardinput.cardid = :CARDID");
     1642
     1643    MSqlQuery cquery(MSqlQuery::InitCon());
     1644    cquery.prepare("SELECT cardid, dvb_diseqc_type FROM capturecard"
     1645                   " WHERE dvb_diseqc_type IS NOT NULL AND"
     1646                   " dtv_dev_id IS NULL");
     1647   
     1648    // iterate through cards
     1649    if(!cquery.exec())
     1650        return false;
     1651    while(cquery.next())
     1652    {
     1653        unsigned cardid = cquery.value(0).toUInt();
     1654        OLD_DISEQC_TYPES type = (OLD_DISEQC_TYPES)cquery.value(1).toUInt();
     1655
     1656        DVBDevTree tree;
     1657        DVBDevDevice* root = NULL;
     1658        unsigned int add_lnbs = 0;
     1659        dvbdev_lnb_t lnb_type = LNB_VOLTAGE_TONE;
     1660     
     1661        // create root of tree
     1662        switch(type)
     1663        {
     1664        case DISEQC_SINGLE:
     1665        {
     1666            // single LNB
     1667            root = DVBDevDevice::CreateByType(tree, DVBDEV_LNB);
     1668            break;
     1669        }
     1670       
     1671        case DISEQC_MINI_2:
     1672        {
     1673            // tone switch + 2 LNBs
     1674            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1675            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1676            sw->SetType(SWITCH_TONE);
     1677            sw->SetNumPorts(2);
     1678            add_lnbs = 2;
     1679            break;
     1680        }
     1681       
     1682        case DISEQC_SWITCH_2_1_0:
     1683        case DISEQC_SWITCH_2_1_1:
     1684        {
     1685            // 2 port diseqc + 2 LNBs
     1686            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1687            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1688            sw->SetType(SWITCH_DISEQC_COMMITTED);
     1689            sw->SetNumPorts(2);
     1690            add_lnbs = 2;
     1691            break;
     1692        }
     1693           
     1694        case DISEQC_SWITCH_4_1_0:
     1695        case DISEQC_SWITCH_4_1_1:
     1696        {
     1697            // 4 port diseqc + 4 LNBs
     1698            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1699            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1700            sw->SetType(SWITCH_DISEQC_COMMITTED);
     1701            sw->SetNumPorts(4);
     1702            add_lnbs = 4;
     1703            break;
     1704        }
     1705           
     1706        case DISEQC_POSITIONER_1_2:
     1707        {
     1708            // non-usals positioner + LNB
     1709            root = DVBDevDevice::CreateByType(tree, DVBDEV_ROTOR);
     1710            DVBDevRotor* rotor = dynamic_cast<DVBDevRotor*>(root);
     1711            rotor->SetType(ROTOR_DISEQC_1_2);
     1712            add_lnbs = 1;
     1713            break;
     1714        }
     1715           
     1716        case DISEQC_POSITIONER_X:
     1717        {
     1718            // usals positioner + LNB (diseqc_pos)
     1719            root = DVBDevDevice::CreateByType(tree, DVBDEV_ROTOR);
     1720            DVBDevRotor* rotor = dynamic_cast<DVBDevRotor*>(root);
     1721            rotor->SetType(ROTOR_DISEQC_1_3);
     1722            add_lnbs = 1;
     1723            break;
     1724        }
     1725           
     1726        case DISEQC_POSITIONER_1_2_SWITCH_2:
     1727        {
     1728            // 10 port uncommitted switch + 10 LNBs
     1729            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1730            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1731            sw->SetType(SWITCH_DISEQC_UNCOMMITTED);
     1732            sw->SetNumPorts(10);
     1733            add_lnbs = 10;
     1734            break;
     1735        }
     1736           
     1737        case DISEQC_SW21:
     1738        {
     1739            // legacy SW21 + 2 fixed lnbs
     1740            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1741            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1742            sw->SetType(SWITCH_LEGACY_SW21);
     1743            sw->SetNumPorts(2);
     1744            add_lnbs = 2;
     1745            lnb_type = LNB_FIXED;
     1746            break;
     1747        }
     1748           
     1749        case DISEQC_SW64:
     1750        {
     1751            // legacy SW64 + 3 fixed lnbs
     1752            root = DVBDevDevice::CreateByType(tree, DVBDEV_SWITCH);
     1753            DVBDevSwitch* sw = dynamic_cast<DVBDevSwitch*>(root);
     1754            sw->SetType(SWITCH_LEGACY_SW64);
     1755            sw->SetNumPorts(3);
     1756            add_lnbs = 3;
     1757            lnb_type = LNB_FIXED;
     1758            break;
     1759        }
     1760
     1761        default:
     1762            VERBOSE(VB_IMPORTANT, "Unknown DiSEqC device type, ignoring card");
     1763            break;
     1764        }
     1765        if(!root)
     1766            continue;
     1767        tree.SetRoot(root);
     1768       
     1769        // create LNBs
     1770        for(unsigned int i=0; i < add_lnbs; i++)
     1771        {
     1772            DVBDevLnb* lnb = dynamic_cast<DVBDevLnb*>
     1773                (DVBDevDevice::CreateByType(tree, DVBDEV_LNB));
     1774            lnb->SetType(lnb_type);
     1775            lnb->SetDescription(QString("LNB #%1").arg(i+1));
     1776            if(!root->SetChild(i, lnb))
     1777                delete lnb;
     1778        }
     1779       
     1780        // save the tree to get real device ids
     1781        tree.Store(cardid);
     1782       
     1783        // iterate inputs
     1784        DVBDevSettings set;
     1785        iquery.bindValue(":CARDID", cardid);
     1786        if(!iquery.exec())
     1787            return false;
     1788        while(iquery.next())
     1789        {
     1790            unsigned int inputid = iquery.value(0).toUInt();
     1791            unsigned int port = iquery.value(1).toUInt();
     1792            double pos = iquery.value(2).toDouble();
     1793            DVBDevLnb* lnb = NULL;
     1794           
     1795            // configure LNB and settings
     1796            switch(type)
     1797            {
     1798            case DISEQC_SINGLE:
     1799                lnb = dynamic_cast<DVBDevLnb*>(root);
     1800                break;
     1801               
     1802            case DISEQC_MINI_2:
     1803            case DISEQC_SWITCH_2_1_0:
     1804            case DISEQC_SWITCH_2_1_1:
     1805            case DISEQC_SWITCH_4_1_0:
     1806            case DISEQC_SWITCH_4_1_1:
     1807            case DISEQC_SW21:
     1808            case DISEQC_SW64:
     1809            case DISEQC_POSITIONER_1_2_SWITCH_2:
     1810                lnb = dynamic_cast<DVBDevLnb*>(root->GetChild(port));
     1811                set.SetValue(root->DeviceID(), port);
     1812                break;
     1813               
     1814            case DISEQC_POSITIONER_1_2:
     1815            case DISEQC_POSITIONER_X:
     1816                lnb = dynamic_cast<DVBDevLnb*>(root->GetChild(0));
     1817                set.SetValue(root->DeviceID(), pos);
     1818                break;
     1819               
     1820            default:
     1821                break;
     1822            }
     1823           
     1824            // configure lnb
     1825            if(lnb)
     1826            {
     1827                lnb->SetLOFSwitch(iquery.value(3).toUInt());
     1828                lnb->SetLOFHigh(iquery.value(4).toUInt());
     1829                lnb->SetLOFLow(iquery.value(5).toUInt());
     1830            }
     1831           
     1832            // save settings
     1833            set.Store(inputid);
     1834        }
     1835       
     1836        // save any LNB changes
     1837        tree.Store(cardid);
     1838
     1839        // invalidate cached devices
     1840        DVBDev trees;
     1841        trees.InvalidateTrees();
     1842    }
     1843
     1844    return true;
     1845}
  • libs/libmythtv/dvbdevtree.h

     
     1/*
     2 * \file dvbdevtree.h
     3 * \brief DVB-S Device Tree Control Classes.
     4 * \author Copyright (C) 2006, Yeasah Pell
     5 */
     6
     7#ifndef DVBDEVTREE_H
     8#define DVBDEVTREE_H
     9
     10// prerequisites
     11#include "dvbtypes.h"
     12#include <linux/dvb/frontend.h>
     13#include <map>
     14#include <vector>
     15
     16// DiSEqC sleep intervals per eutelsat spec
     17#define DISEQC_SHORT_WAIT     (15 * 1000)
     18#define DISEQC_LONG_WAIT      (100 * 1000)
     19
     20// Number of times to retry ioctls after receiving ETIMEDOUT before giving up
     21#define TIMEOUT_RETRIES       3
     22#define TIMEOUT_WAIT          (100 * 1000)
     23
     24// Framing byte
     25#define DISEQC_FRM            0xe0
     26#define DISEQC_FRM_REPEAT     (1 << 0)
     27#define DISEQC_FRM_REPLY_REQ  (1 << 1)
     28
     29// Address byte
     30#define DISEQC_ADR_ALL        0x00
     31#define DISEQC_ADR_SW_ALL     0x10
     32#define DISEQC_ADR_LNB        0x11
     33#define DISEQC_ADR_LNB_SW     0x12
     34#define DISEQC_ADR_SW_BLK     0x14
     35#define DISEQC_ADR_SW         0x15
     36#define DISEQC_ADR_SMATV      0x18
     37#define DISEQC_ADR_POL_ALL    0x20
     38#define DISEQC_ADR_POL_LIN    0x21
     39#define DISEQC_ADR_POS_ALL    0x30
     40#define DISEQC_ADR_POS_AZ     0x31
     41#define DISEQC_ADR_POS_EL     0x32
     42
     43// Command byte
     44#define DISEQC_CMD_RESET      0x00
     45#define DISEQC_CMD_CLR_RESET  0x01
     46#define DISEQC_CMD_WRITE_N0   0x38
     47#define DISEQC_CMD_WRITE_N1   0x39
     48#define DISEQC_CMD_WRITE_FREQ 0x58
     49#define DISEQC_CMD_HALT       0x60
     50#define DISEQC_CMD_LMT_OFF    0x63
     51#define DISEQC_CMD_LMT_E      0x66
     52#define DISEQC_CMD_LMT_W      0x67
     53#define DISEQC_CMD_DRIVE_E    0x68
     54#define DISEQC_CMD_DRIVE_W    0x69
     55#define DISEQC_CMD_STORE_POS  0x6a
     56#define DISEQC_CMD_GOTO_POS   0x6b
     57#define DISEQC_CMD_GOTO_X     0x6e
     58
     59enum dvbdev_t
     60{
     61    DVBDEV_SWITCH = 0,
     62    DVBDEV_ROTOR,
     63    DVBDEV_LNB
     64};
     65
     66QString DevTypeToString(dvbdev_t type);
     67dvbdev_t DevTypeFromString(const QString& type);
     68
     69enum dvbdev_switch_t
     70{
     71    SWITCH_TONE = 0,
     72    SWITCH_DISEQC_COMMITTED,
     73    SWITCH_DISEQC_UNCOMMITTED,
     74    SWITCH_LEGACY_SW21,
     75    SWITCH_LEGACY_SW42,
     76    SWITCH_LEGACY_SW64
     77};
     78
     79QString SwitchTypeToString(dvbdev_switch_t type);
     80dvbdev_switch_t SwitchTypeFromString(const QString& type);
     81
     82enum dvbdev_rotor_t
     83{
     84    ROTOR_DISEQC_1_2 = 0,
     85    ROTOR_DISEQC_1_3
     86};
     87
     88QString RotorTypeToString(dvbdev_rotor_t type);
     89dvbdev_rotor_t RotorTypeFromString(const QString& type);
     90
     91enum dvbdev_lnb_t
     92{
     93    LNB_FIXED = 0,
     94    LNB_VOLTAGE,
     95    LNB_VOLTAGE_TONE
     96};
     97
     98QString LnbTypeToString(dvbdev_lnb_t type);
     99dvbdev_lnb_t LnbTypeFromString(const QString& type);
     100
     101/** DVB-S device settings class. Represents a single possible configuration
     102    of a given network of DVB-S devices. */
     103class DVBDevSettings
     104{
     105  public:
     106    DVBDevSettings();
     107
     108    /** Loads configuration chain from DB for specified card input id.
     109        \param card_input_id Desired capture card input ID.
     110        \return True if successful. */
     111    bool Load(unsigned int card_input_id);
     112   
     113    /** Stores configuration chain to DB for specified card input id.
     114        \param card_input_id Desired capture card input ID.
     115        \return True if successful. */
     116    bool Store(unsigned int card_input_id) const;
     117
     118    /** Retrieves a value from this configuration chain by device id.
     119        \param dtv_dev_id Device id.
     120        \return Device scalar value. */
     121    double GetValue(int dtv_dev_id) const;
     122
     123    /** Sets a value for this configuration chain by device id.
     124        \param dtv_dev_id Device id.
     125        \param value Device scalar value. */
     126    void SetValue(int dtv_dev_id, double value);
     127   
     128  protected:
     129
     130    // map of dev tree id to configuration value
     131    typedef std::map<int, double> CONFIG;
     132    CONFIG m_config;
     133
     134    // current input id
     135    unsigned int m_input_id;
     136};
     137
     138class DVBDevTrees;
     139class DVBDevTree;
     140class DVBDevDevice;
     141class DVBDevRotor;
     142class DVBDevLnb;
     143
     144/** Main DVB-S device interface. */
     145class DVBDev
     146{
     147  public:
     148
     149    /** Retrieve device tree.
     150        \param card_id Capture card id.
     151        \param fd_frontend DVB frontend device file descriptor. */
     152    DVBDevTree* FindTree(unsigned int card_id);
     153
     154    /** Invalidate cached trees. */
     155    void InvalidateTrees();
     156   
     157  protected:
     158    static DVBDevTrees m_trees;
     159};
     160
     161/** Static-scoped locked tree list class. */
     162class DVBDevTrees
     163{
     164  public:
     165    ~DVBDevTrees();
     166
     167    DVBDevTree* FindTree(unsigned int card_id);
     168    void InvalidateTrees();
     169   
     170  protected:
     171    typedef std::map<unsigned int, DVBDevTree*> TREES;
     172    TREES m_trees;
     173    QMutex m_trees_lock;
     174};
     175
     176/** DVB-S device tree class. Represents a tree of DVB-S devices. */
     177class DVBDevTree
     178{
     179  public:
     180    /** Constructor. */
     181    DVBDevTree();
     182    ~DVBDevTree();
     183
     184    /** Loads the device tree from the database.
     185        \param card_id Capture card id.
     186        \return True if successful. */
     187    bool Load(unsigned int card_id);
     188
     189    /** Stores the device tree to the database.
     190        \param card_id Capture card id.
     191        \return True if successful. */
     192    bool Store(unsigned int card_id);
     193   
     194    /** Applies settings to the entire tree.
     195        \param settings Configuration chain to apply.
     196        \param tuning Tuning parameters.
     197        \return True if execution completed successfully. */
     198    bool Execute(const DVBDevSettings& settings,
     199                 const DVBTuning& tuning);
     200
     201    /** Reset state of nodes in tree, forcing updates on the
     202        next Execute command.
     203        \return True if reset completed successfully. */
     204    void Reset();
     205
     206    /** Returns the nth rotor device object in the tree.
     207        \param settings Configuration chain in effect.
     208        \param index 0 for first rotor, 1 for second, etc.
     209        \return Pointer to rotor object if found, NULL otherwise. */
     210    DVBDevRotor* FindRotor(const DVBDevSettings& settings,
     211                           unsigned int index = 0);
     212
     213    /** Returns the LNB device object selected by the configuration chain.
     214        \param settings Configuration chain in effect.
     215        \return Pointer to LNB object if found, NULL otherwise. */
     216    DVBDevLnb* FindLNB(const DVBDevSettings& settings);
     217
     218    /** Returns a device by ID.
     219        \param dev_id Device ID to find.
     220        \return Pointer to device, or NULL if not found in this tree. */
     221    DVBDevDevice* FindDevice(int dev_id);
     222
     223    /** Retrieves the root node in the tree.
     224        \return Pointer to device, or NULL if no root. */
     225    DVBDevDevice* Root() { return m_root; }
     226
     227    /** Changes the root node of the tree.
     228        \param root New root node (may be NULL). */
     229    void SetRoot(DVBDevDevice* root);
     230
     231    /** Sends a DiSEqC command.
     232        \param adr DiSEqC destination address.
     233        \param cmd DiSEqC command.
     234        \param repeats Number of times to repeat command.
     235        \param data_len Length of optional data.
     236        \param data Pointer to optional data. */
     237    bool SendCommand(unsigned int adr,
     238                     unsigned int cmd,
     239                     unsigned int repeats = 0,
     240                     unsigned int data_len = 0,
     241                     unsigned char* data = NULL);
     242
     243    /** Resets the DiSEqC bus.
     244        \param hard_reset If true, the bus will be power cycled.
     245        \return True if successful. */
     246    bool ResetDiseqc(bool hard_reset);
     247   
     248    // frontend fd
     249    void Open(int fd_frontend);
     250    void Close() { m_fd_frontend = -1; }
     251    int FrontendFD() const { return m_fd_frontend; }
     252
     253    // frontend operations
     254    bool SetTone(bool on);
     255    bool MiniDiseqc(fe_sec_mini_cmd cmd);
     256    bool SetVoltage(fe_sec_voltage voltage);
     257    bool SendDiseqc(const dvb_diseqc_master_cmd& cmd);
     258    fe_sec_voltage GetVoltage() const { return m_last_voltage; }
     259
     260    // tree management
     261    void AddDeferredDelete(unsigned int dev_id) { m_delete.push_back(dev_id); }
     262    int NextFakeID() { return --m_next_cnt; }
     263   
     264  protected:
     265    bool ApplyVoltage(const DVBDevSettings& settings,
     266                      const DVBTuning& tuning);
     267   
     268    int m_fd_frontend;
     269    DVBDevDevice *m_root;
     270    fe_sec_voltage m_last_voltage;
     271    mutable std::list<unsigned int> m_delete;
     272
     273    int m_next_cnt;
     274};
     275
     276/** DVB-S device class. Represents a node in a DVB-S device network. */
     277class DVBDevDevice
     278{
     279  public:
     280    /** Constructor.
     281        \param tree Parent reference to tree object.
     282        \param dtv_dev_id Device ID of this node. */
     283    DVBDevDevice(DVBDevTree& tree, int dtv_dev_id);
     284
     285    virtual ~DVBDevDevice();
     286
     287    /** Applies DiSEqC settings to this node and any children.
     288        \param settings Configuration chain to apply.
     289        \param tuning Tuning parameters.
     290        \return True if execution completed successfully. */
     291    virtual bool Execute(const DVBDevSettings& settings,
     292                         const DVBTuning& tuning) = 0;
     293
     294    /** Resets the last known settings for this device. Device
     295        will not actually have commands issued until next Execute() method. */
     296    virtual void Reset() = 0;
     297
     298    /** Determines if this device or any child will be sending a command
     299        for the given configuration chain.
     300        \param settings Configuration chain in effect.
     301        \return True if a command would be sent if Execute() were called. */
     302    virtual bool NeedsCommand(const DVBDevSettings& settings) const = 0;
     303
     304    /** Retrieves the selected child for this configuration, if any.
     305        \param settings Configuration chain in effect.
     306        \return Child node object, or NULL if none. */
     307    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const = 0;
     308
     309    /** Retrieves the proper number of children for this node.
     310        \return Number of children */
     311    virtual unsigned int NumChildren() const = 0;
     312
     313    /** Retrieves the nth child of this node.
     314        \param ordinal Child number (starting at 0).
     315        \return Pointer to device object, or NULL if no child. */
     316    virtual DVBDevDevice* GetChild(unsigned int ordinal) = 0;
     317
     318    /** Changes the nth child of this node.
     319        \param ordinal Child number (starting at 0).
     320        \param device New child device. (may be NULL)
     321        \return true if object was added to tree. */
     322    virtual bool SetChild(unsigned int ordinal,
     323                          DVBDevDevice* device) = 0;
     324
     325    /** Retrives the desired voltage for this config.
     326        \param settings Configuration chain in effect.
     327        \param tuning Tuning parameters.
     328        \return Voltage required. */
     329    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     330                                      const DVBTuning& tuning) const = 0;
     331   
     332    /** Loads this device from the database.
     333        \return True if successful. */
     334    virtual bool Load() = 0;
     335   
     336    /** Stores this device to the database.
     337        \return True if successful. */
     338    virtual bool Store() = 0;
     339   
     340    /** Returns a device by ID.
     341        \param dev_id Device ID to find.
     342        \return Pointer to device, or NULL if not found in this tree. */
     343    DVBDevDevice* FindDevice(int dev_id);
     344   
     345    static DVBDevDevice* CreateById(DVBDevTree& tree,
     346                                    int dtv_dev_id);
     347    static DVBDevDevice* CreateByType(DVBDevTree& tree,
     348                                      dvbdev_t type,
     349                                      int dev_id = -1);
     350
     351    int DeviceID() const { return m_dtv_dev_id; }
     352
     353    DVBDevDevice* GetParent() const { return m_parent; }
     354    void SetParent(DVBDevDevice* parent) { m_parent = parent; }
     355    unsigned int GetOrdinal() const { return m_ordinal; }
     356    void SetOrdinal(unsigned int ordinal) { m_ordinal = ordinal; }
     357
     358    void SetDeviceType(dvbdev_t type) { m_dev_type = type; }
     359    dvbdev_t GetDeviceType() const { return m_dev_type; }
     360
     361    QString GetDescription() const { return m_descr; }
     362    void SetDescription(const QString& descr) { m_descr = descr; }
     363   
     364  protected:
     365
     366    int m_dtv_dev_id;
     367    dvbdev_t m_dev_type;
     368    QString m_descr;
     369    DVBDevTree& m_tree;
     370    DVBDevDevice* m_parent;
     371    unsigned int m_ordinal;
     372    unsigned int m_repeat;
     373};
     374
     375/** Switch class, including tone, legacy and DiSEqC switches. */
     376class DVBDevSwitch : public DVBDevDevice
     377{
     378  public:
     379    DVBDevSwitch(DVBDevTree& tree, int dtv_dev_id);
     380    ~DVBDevSwitch();
     381
     382    virtual bool Execute(const DVBDevSettings& settings,
     383                         const DVBTuning& tuning);
     384    virtual void Reset();
     385    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     386    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const;
     387    virtual unsigned int NumChildren() const;
     388    virtual DVBDevDevice* GetChild(unsigned int ordinal);
     389    virtual bool SetChild(unsigned int ordinal, DVBDevDevice* device);
     390    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     391                                      const DVBTuning& tuning) const;
     392    virtual bool Load();
     393    virtual bool Store();
     394
     395    dvbdev_switch_t GetType() const { return m_type; }
     396    void SetType(dvbdev_switch_t type) { m_type = type; }
     397    unsigned int GetNumPorts() const { return m_num_ports; }
     398    void SetNumPorts(unsigned int num_ports);
     399   
     400  protected:
     401    bool ExecuteLegacy(const DVBDevSettings& settings,
     402                       const DVBTuning& tuning,
     403                       unsigned int pos);
     404    bool ExecuteTone(const DVBDevSettings& settings,
     405                     const DVBTuning& tuning,
     406                     unsigned int pos);
     407    bool ExecuteDiseqc(const DVBDevSettings& settings,
     408                       const DVBTuning& tuning,
     409                       unsigned int pos);
     410    bool GetPosition(const DVBDevSettings& settings,
     411                     unsigned int& pos) const;
     412   
     413  private:
     414    dvbdev_switch_t m_type;
     415    unsigned int m_num_ports;
     416    unsigned int m_last_pos;
     417
     418    typedef std::vector<DVBDevDevice*> CHILDREN;
     419    CHILDREN m_children;
     420};
     421
     422/** Rotor class. */
     423class DVBDevRotor : public DVBDevDevice
     424{
     425  public:
     426    DVBDevRotor(DVBDevTree& tree, int dtv_dev_id);
     427    ~DVBDevRotor();
     428
     429    virtual bool Execute(const DVBDevSettings& settings,
     430                         const DVBTuning& tuning);
     431    virtual void Reset();
     432    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     433    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const;
     434    virtual unsigned int NumChildren() const { return 1; }
     435    virtual DVBDevDevice* GetChild(unsigned int) { return m_child; }
     436    virtual bool SetChild(unsigned int ordinal, DVBDevDevice* device);
     437    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     438                                      const DVBTuning& tuning) const;
     439    virtual bool Load();
     440    virtual bool Store();
     441
     442    /** Returns an indication of rotor progress.
     443        \return Scale from 0.0..1.0 indicating percentage complete of current
     444        move. A value of 1.0 indicates motion is complete. */
     445    double Progress() const;
     446
     447    /** Returns true if there is reasonable confidence in the value returned
     448        by Progress() -- otherwise, Progress() returns progress toward
     449        the time when the position will be approximately known. */
     450    bool IsPositionKnown() const { return m_last_pos_known; }
     451
     452    dvbdev_rotor_t GetType() const { return m_type; }
     453    void SetType(dvbdev_rotor_t type) { m_type = type; }
     454    double GetLoSpeed() const { return m_speed_lo; }
     455    void SetLoSpeed(double speed) { m_speed_lo = speed; }
     456    double GetHiSpeed() const { return m_speed_hi; }
     457    void SetHiSpeed(double speed) { m_speed_hi = speed; }
     458
     459    typedef std::map<unsigned int, double> POSMAP;
     460    POSMAP GetPosMap() const;
     461    void SetPosMap(const POSMAP& posmap);
     462   
     463  protected:
     464    bool ExecuteRotor(const DVBDevSettings& settings,
     465                      const DVBTuning& tuning,
     466                      double angle);
     467    bool ExecuteUSALS(const DVBDevSettings& settings,
     468                      const DVBTuning& tuning,
     469                      double angle);
     470
     471    double CalculateAzimuth(double angle) const;
     472    double GetApproxAzimuth();
     473    void RotorMoving(double azimuth);
     474   
     475  private:
     476    // configuration
     477    dvbdev_rotor_t m_type;
     478    double m_speed_hi;
     479    double m_speed_lo;
     480    typedef std::map<double, unsigned int> INTPOSMAP;
     481    INTPOSMAP m_posmap;
     482    DVBDevDevice *m_child;
     483
     484    // state
     485    double m_last_position;
     486    double m_last_azimuth;
     487    double m_desired_azimuth;
     488    bool m_reset;
     489   
     490    mutable double m_move_time;
     491    mutable bool m_last_pos_known;
     492};
     493
     494/** LNB Class. */
     495class DVBDevLnb : public DVBDevDevice
     496{
     497  public:
     498    DVBDevLnb(DVBDevTree& tree, int dtv_dev_id);
     499
     500    virtual bool Execute(const DVBDevSettings& settings,
     501                         const DVBTuning& tuning);
     502    virtual void Reset();
     503    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     504
     505    // no children on LNBs
     506    virtual DVBDevDevice* SelectedChild(const DVBDevSettings&) const { return NULL; }
     507    virtual unsigned int NumChildren() const { return 0; }
     508    virtual DVBDevDevice* GetChild(unsigned int) { return NULL; }
     509    virtual bool SetChild(unsigned int, DVBDevDevice*) { return false; }
     510    fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     511                              const DVBTuning& tuning) const;
     512    virtual bool Load();   
     513    virtual bool Store();
     514   
     515    /** Determine if the high frequency band is active (for switchable LNBs).
     516        \param tuning Tuning parameters.
     517        \return True if high band is active. */
     518    bool IsHighBand(const DVBTuning& tuning) const;
     519
     520    /** Determine if horizontal polarity is active (for switchable LNBs).
     521        \param tuning Tuning parameters.
     522        \return True if polarity is horizontal. */
     523    bool IsHorizontal(const DVBTuning& tuning) const;
     524
     525    /** Calculate proper intermediate frequency for the given settings and
     526        tuning parameters.
     527        \param settings Configuration chain in effect.
     528        \param tuning Tuning parameters.
     529        \return Frequency for use with FE_SET_FRONTEND. */
     530    __u32 GetIF(const DVBDevSettings& settings,
     531                const DVBTuning& tuning) const;
     532
     533    dvbdev_lnb_t GetType() const { return m_type; }
     534    void SetType(dvbdev_lnb_t type) { m_type = type; }
     535    unsigned int GetLOFSwitch() const { return m_lof_switch; }
     536    void SetLOFSwitch(unsigned int lof_switch) { m_lof_switch = lof_switch; }
     537    unsigned int GetLOFHigh() const { return m_lof_hi; }
     538    void SetLOFHigh(unsigned int lof_hi) { m_lof_hi = lof_hi; }
     539    unsigned int GetLOFLow() const { return m_lof_lo; }
     540    void SetLOFLow(unsigned int lof_lo) { m_lof_lo = lof_lo; }
     541   
     542  private:
     543    dvbdev_lnb_t m_type;
     544   
     545    unsigned int m_lof_switch;
     546    unsigned int m_lof_hi;
     547    unsigned int m_lof_lo;
     548};
     549
     550bool DatabaseDiseqcImport();
     551bool DatabaseDiseqcUpgrade();
     552
     553#endif // DVBDISEQC_H