Ticket #1945: dvbdevtree.patch

File dvbdevtree.patch, 71.7 KB (added by yeasah@…, 18 years ago)

New code for device tree (no integration with existing code)

  • 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(DISEQC_SHORT_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(DISEQC_SHORT_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(DISEQC_SHORT_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(DISEQC_SHORT_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_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_last_pos_known || 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    }
     1003
     1004    // chain to child
     1005    if(success && m_child)
     1006        success = m_child->Execute(settings, tuning);
     1007
     1008    return success;
     1009}
     1010
     1011void DVBDevRotor::Reset()
     1012{
     1013    if(m_child)
     1014        m_child->Reset();
     1015}
     1016
     1017bool DVBDevRotor::NeedsCommand(const DVBDevSettings& settings) const
     1018{
     1019    double position = settings.GetValue(m_dtv_dev_id);
     1020    if(position != m_last_position)
     1021        return true;
     1022    else if(m_child)
     1023        return m_child->NeedsCommand(settings);
     1024    else
     1025        return false;
     1026}
     1027
     1028DVBDevDevice* DVBDevRotor::SelectedChild(const DVBDevSettings& /*settings*/) const
     1029{
     1030    return m_child;
     1031}
     1032
     1033bool DVBDevRotor::SetChild(unsigned int ordinal, DVBDevDevice* device)
     1034{
     1035    if(ordinal == 0)
     1036    {
     1037        delete m_child;
     1038        m_child = device;
     1039        if(m_child)
     1040        {
     1041            m_child->SetOrdinal(ordinal);
     1042            m_child->SetParent(this);
     1043        }
     1044        return true;
     1045    }
     1046    return false;
     1047}
     1048
     1049fe_sec_voltage DVBDevRotor::GetVoltage(const DVBDevSettings& settings,
     1050                                       const DVBTuning& tuning) const
     1051{
     1052    fe_sec_voltage voltage = SEC_VOLTAGE_18;
     1053
     1054    // override voltage if the last position is known and the rotor is moving
     1055    if(!(m_last_pos_known && Progress() < 1.0) && m_child)
     1056        voltage = m_child->GetVoltage(settings, tuning);
     1057
     1058    return voltage;
     1059}
     1060
     1061bool DVBDevRotor::Load()
     1062{
     1063    // populate switch parameters from db
     1064    {
     1065        MSqlQuery query(MSqlQuery::InitCon());
     1066        query.prepare("SELECT dtv_dev_subtype,"
     1067                      " rotor_hi_speed, rotor_lo_speed, rotor_positions"
     1068                      " FROM dtv_device_tree"
     1069                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     1070        query.bindValue(":DTV_DEV_ID", DeviceID());
     1071       
     1072        if(query.exec() && query.next())
     1073        {
     1074            m_type = RotorTypeFromString(query.value(0).toString());
     1075            m_speed_hi = query.value(1).toDouble();
     1076            m_speed_lo = query.value(2).toDouble();
     1077
     1078            // form of "angle1=index1:angle2=index2:..."
     1079            QString positions = query.value(3).toString();
     1080            QStringList pos = QStringList::split(":", positions);
     1081            for(unsigned int i=0; i < pos.count(); i++)
     1082            {
     1083                QStringList eq = QStringList::split("=", pos[i]);
     1084                if(eq.count() == 2)
     1085                    m_posmap[eq[0].toFloat()] = eq[1].toUInt();
     1086            }
     1087        }
     1088    }
     1089
     1090    // load children from db
     1091    delete m_child;
     1092    m_child = NULL;
     1093    {
     1094        MSqlQuery query(MSqlQuery::InitCon());
     1095        query.prepare("SELECT dtv_dev_id FROM dtv_device_tree "
     1096                      "WHERE parent = :DTV_DEV_ID");
     1097        query.bindValue(":DTV_DEV_ID", DeviceID());
     1098       
     1099        if(query.exec() && query.next())
     1100        {
     1101            int child_dev_id = query.value(0).toInt();
     1102            SetChild(0, CreateById(m_tree, child_dev_id));
     1103        }
     1104    }
     1105
     1106    return true;
     1107}
     1108
     1109bool DVBDevRotor::Store()
     1110{
     1111    QString type = RotorTypeToString(m_type);
     1112    QString posmap;
     1113
     1114    if(!m_posmap.empty())
     1115    {
     1116        QStringList pos;
     1117        for(INTPOSMAP::iterator i = m_posmap.begin(); i != m_posmap.end(); i++)
     1118            pos.push_back(QString("%1=%2").arg(i->first).arg(i->second));
     1119        posmap = pos.join(":");
     1120    }
     1121
     1122    MSqlQuery query(MSqlQuery::InitCon());
     1123
     1124    // insert new or update old
     1125    if(m_dtv_dev_id >= 0)
     1126    {
     1127        query.prepare("UPDATE dtv_device_tree"
     1128                      " SET parent = :PARENT,"
     1129                      " ordinal = :ORDINAL,"
     1130                      " dtv_dev_type = 'rotor',"
     1131                      " dtv_dev_descr = :DESCR,"
     1132                      " dtv_dev_subtype = :TYPE,"
     1133                      " rotor_hi_speed = :HISPEED,"
     1134                      " rotor_lo_speed = :LOSPEED,"
     1135                      " rotor_positions = :POSMAP"
     1136                      " WHERE dtv_dev_id = :DTV_DEV_ID");
     1137    }
     1138    else
     1139    {
     1140        query.prepare("INSERT INTO dtv_device_tree"
     1141                      " (parent, ordinal, dtv_dev_type, dtv_dev_descr,"
     1142                      " dtv_dev_subtype, rotor_hi_speed,"
     1143                      " rotor_lo_speed, rotor_positions)"
     1144                      " VALUES (:PARENT, :ORDINAL, 'rotor', :DESCR,"
     1145                      " :TYPE, :HISPEED, :LOSPEED, :POSMAP)");
     1146    }
     1147    if(m_parent)
     1148        query.bindValue(":PARENT", m_parent->DeviceID());
     1149    query.bindValue(":ORDINAL", m_ordinal);
     1150    query.bindValue(":DESCR", GetDescription());
     1151    query.bindValue(":TYPE", type);
     1152    query.bindValue(":HISPEED", m_speed_hi);
     1153    query.bindValue(":LOSPEED", m_speed_lo);
     1154    query.bindValue(":POSMAP", posmap);
     1155    query.bindValue(":DTV_DEV_ID", DeviceID());
     1156
     1157    // chain to child
     1158    bool success = query.exec();
     1159    if(success)
     1160    {
     1161        if(m_dtv_dev_id < 0)
     1162            m_dtv_dev_id = query.lastInsertId().toInt();
     1163        if(m_child)
     1164            success = m_child->Store();
     1165    }
     1166
     1167    return success;
     1168}
     1169
     1170double DVBDevRotor::Progress() const
     1171{
     1172    double completed = 1.0;
     1173
     1174    if(m_move_time != 0.0)
     1175    {
     1176        // calculate duration of move
     1177        double speed =
     1178            (m_tree.GetVoltage() == SEC_VOLTAGE_18) ? m_speed_hi : m_speed_lo;
     1179        double change = fabs(m_desired_azimuth - m_last_azimuth);
     1180        double duration = change / speed;
     1181
     1182        // determine completion percentage
     1183        struct timeval curtime;
     1184        gettimeofday(&curtime, NULL);
     1185        double cursecond = curtime.tv_sec + (double)curtime.tv_usec / 1000000;
     1186        double time_since_move = cursecond - m_move_time;
     1187        completed = time_since_move / duration;
     1188
     1189        // move completed, finish up
     1190        if(completed > 1.0)
     1191            completed = 1.0;
     1192    }
     1193
     1194    return completed;
     1195}
     1196
     1197DVBDevRotor::POSMAP DVBDevRotor::GetPosMap() const
     1198{
     1199    POSMAP retposmap;
     1200    INTPOSMAP::const_iterator i;
     1201    for(i = m_posmap.begin(); i != m_posmap.end(); i++)
     1202        retposmap.insert(make_pair(i->second, i->first));
     1203    return retposmap;
     1204}
     1205
     1206void DVBDevRotor::SetPosMap(const POSMAP& posmap)
     1207{
     1208    m_posmap.clear();
     1209    POSMAP::const_iterator i;
     1210    for(i = posmap.begin(); i != posmap.end(); i++)
     1211        m_posmap.insert(make_pair(i->second, i->first));
     1212}
     1213
     1214bool DVBDevRotor::ExecuteRotor(const DVBDevSettings& /*settings*/,
     1215                               const DVBTuning& /*tuning*/,
     1216                               double angle)
     1217{
     1218    // determine stored position from position map
     1219    INTPOSMAP::const_iterator i = m_posmap.find(angle);
     1220    unsigned char index;
     1221    if(i != m_posmap.end())
     1222    {
     1223        index = i->second;
     1224        RotorMoving(CalculateAzimuth(angle));
     1225    }
     1226    else
     1227        index = (unsigned int) angle;
     1228
     1229    VERBOSE(VB_CHANNEL, LOC + QString("Rotor - Goto Stored Position %1")
     1230            .arg(index));
     1231
     1232    return m_tree.SendCommand(DISEQC_ADR_POS_AZ,
     1233                              DISEQC_CMD_GOTO_POS,
     1234                              m_repeat,
     1235                              1,
     1236                              &index);
     1237}
     1238
     1239bool DVBDevRotor::ExecuteUSALS(const DVBDevSettings& /*settings*/,
     1240                               const DVBTuning& /*tuning*/,
     1241                               double angle)
     1242{
     1243    double azimuth = CalculateAzimuth(angle);
     1244    RotorMoving(azimuth);
     1245    VERBOSE(VB_CHANNEL, LOC + QString("USALS Rotor - Goto %1 (Azimuth %2)")
     1246            .arg(angle)
     1247            .arg(azimuth));
     1248
     1249    uint az16 = (unsigned int) (abs(azimuth) * 16.0);
     1250    unsigned char cmd[2];
     1251    cmd[0] = ((azimuth > 0.0) ? 0xE0 : 0xD0) | ((az16 >> 8) & 0x0f);
     1252    cmd[1] = (az16 & 0xff);
     1253
     1254    return m_tree.SendCommand(DISEQC_ADR_POS_AZ,
     1255                              DISEQC_CMD_GOTO_X,
     1256                              m_repeat,
     1257                              2,
     1258                              cmd);
     1259}
     1260
     1261#define TO_RADS (M_PI / 180.0)
     1262#define TO_DEC  (180.0 / M_PI)
     1263
     1264double DVBDevRotor::CalculateAzimuth(double angle) const
     1265{
     1266    // Equation lifted from VDR rotor plugin by
     1267    // Thomas Bergwinkl <Thomas.Bergwinkl@t-online.de>
     1268
     1269    // Earth Station Latitude and Longitude in radians
     1270    double P  = gContext->GetSetting("Latitude",  "").toFloat() * TO_RADS;
     1271    double Ue = gContext->GetSetting("Longitude", "").toFloat() * TO_RADS;
     1272
     1273    // Satellite Longitude in radians
     1274    double Us = angle * TO_RADS;
     1275
     1276    double az      = M_PI + atan( tan(Us - Ue) / sin(P) );
     1277    double x       = acos( cos(Us - Ue) * cos(P) );
     1278    double el      = atan( (cos(x) - 0.1513) / sin(x) );
     1279    double tmp_a   = -cos(el) * sin(az);
     1280    double tmp_b   = (sin(el) * cos(P)) - (cos(el) * sin(P) * cos(az));
     1281    double azimuth = atan(tmp_a / tmp_b) * TO_DEC;
     1282
     1283    return azimuth;
     1284}
     1285
     1286double DVBDevRotor::GetApproxAzimuth()
     1287{
     1288    double approx = m_last_azimuth;
     1289
     1290    if(m_move_time != 0.0)
     1291    {
     1292        double change = m_desired_azimuth - m_last_azimuth;
     1293        double progress = Progress();
     1294        approx += (change * progress);
     1295        if(progress >= 1.0)
     1296            m_move_time = 0.0;
     1297    }
     1298
     1299    return approx;
     1300}
     1301
     1302void DVBDevRotor::RotorMoving(double azimuth)
     1303{
     1304    // set last to approximate current position (or worst case if unknown)
     1305    if(m_last_pos_known)
     1306        m_last_azimuth = GetApproxAzimuth();
     1307    else
     1308        m_last_azimuth = azimuth > 0.0 ? -75.0 : 75.0;
     1309
     1310    // save time and angle of this command
     1311    m_desired_azimuth = azimuth;
     1312    struct timeval curtime;
     1313    gettimeofday(&curtime, NULL);
     1314    m_move_time = curtime.tv_sec + (double)curtime.tv_usec / 1000000;
     1315
     1316    m_last_pos_known = true;
     1317}
     1318
     1319////////////////////////////////////////
     1320
     1321DVBDevLnb::DVBDevLnb(DVBDevTree& tree,
     1322                     int dtv_dev_id)
     1323    : DVBDevDevice(tree, dtv_dev_id),
     1324      m_type(LNB_VOLTAGE_TONE),
     1325      m_lof_switch(11700000),
     1326      m_lof_hi(10600000),
     1327      m_lof_lo(9750000)
     1328{
     1329    Reset();
     1330}
     1331
     1332bool DVBDevLnb::Execute(const DVBDevSettings& /*settings*/,
     1333                        const DVBTuning& tuning)
     1334{
     1335    // set voltage for polarization
     1336    if((m_type == LNB_VOLTAGE || m_type == LNB_VOLTAGE_TONE))
     1337    {
     1338        bool horiz = IsHorizontal(tuning);
     1339        fe_sec_voltage voltage = (horiz ? SEC_VOLTAGE_18 : SEC_VOLTAGE_13);
     1340        m_tree.SetVoltage(voltage);
     1341    }
     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
     23// Framing byte
     24#define DISEQC_FRM            0xe0
     25#define DISEQC_FRM_REPEAT     (1 << 0)
     26#define DISEQC_FRM_REPLY_REQ  (1 << 1)
     27
     28// Address byte
     29#define DISEQC_ADR_ALL        0x00
     30#define DISEQC_ADR_SW_ALL     0x10
     31#define DISEQC_ADR_LNB        0x11
     32#define DISEQC_ADR_LNB_SW     0x12
     33#define DISEQC_ADR_SW_BLK     0x14
     34#define DISEQC_ADR_SW         0x15
     35#define DISEQC_ADR_SMATV      0x18
     36#define DISEQC_ADR_POL_ALL    0x20
     37#define DISEQC_ADR_POL_LIN    0x21
     38#define DISEQC_ADR_POS_ALL    0x30
     39#define DISEQC_ADR_POS_AZ     0x31
     40#define DISEQC_ADR_POS_EL     0x32
     41
     42// Command byte
     43#define DISEQC_CMD_RESET      0x00
     44#define DISEQC_CMD_CLR_RESET  0x01
     45#define DISEQC_CMD_WRITE_N0   0x38
     46#define DISEQC_CMD_WRITE_N1   0x39
     47#define DISEQC_CMD_WRITE_FREQ 0x58
     48#define DISEQC_CMD_HALT       0x60
     49#define DISEQC_CMD_LMT_OFF    0x63
     50#define DISEQC_CMD_LMT_E      0x66
     51#define DISEQC_CMD_LMT_W      0x67
     52#define DISEQC_CMD_DRIVE_E    0x68
     53#define DISEQC_CMD_DRIVE_W    0x69
     54#define DISEQC_CMD_STORE_POS  0x6a
     55#define DISEQC_CMD_GOTO_POS   0x6b
     56#define DISEQC_CMD_GOTO_X     0x6e
     57
     58enum dvbdev_t
     59{
     60    DVBDEV_SWITCH = 0,
     61    DVBDEV_ROTOR,
     62    DVBDEV_LNB
     63};
     64
     65QString DevTypeToString(dvbdev_t type);
     66dvbdev_t DevTypeFromString(const QString& type);
     67
     68enum dvbdev_switch_t
     69{
     70    SWITCH_TONE = 0,
     71    SWITCH_DISEQC_COMMITTED,
     72    SWITCH_DISEQC_UNCOMMITTED,
     73    SWITCH_LEGACY_SW21,
     74    SWITCH_LEGACY_SW42,
     75    SWITCH_LEGACY_SW64
     76};
     77
     78QString SwitchTypeToString(dvbdev_switch_t type);
     79dvbdev_switch_t SwitchTypeFromString(const QString& type);
     80
     81enum dvbdev_rotor_t
     82{
     83    ROTOR_DISEQC_1_2 = 0,
     84    ROTOR_DISEQC_1_3
     85};
     86
     87QString RotorTypeToString(dvbdev_rotor_t type);
     88dvbdev_rotor_t RotorTypeFromString(const QString& type);
     89
     90enum dvbdev_lnb_t
     91{
     92    LNB_FIXED = 0,
     93    LNB_VOLTAGE,
     94    LNB_VOLTAGE_TONE
     95};
     96
     97QString LnbTypeToString(dvbdev_lnb_t type);
     98dvbdev_lnb_t LnbTypeFromString(const QString& type);
     99
     100/** DVB-S device settings class. Represents a single possible configuration
     101    of a given network of DVB-S devices. */
     102class DVBDevSettings
     103{
     104  public:
     105    DVBDevSettings();
     106
     107    /** Loads configuration chain from DB for specified card input id.
     108        \param card_input_id Desired capture card input ID.
     109        \return True if successful. */
     110    bool Load(unsigned int card_input_id);
     111   
     112    /** Stores configuration chain to DB for specified card input id.
     113        \param card_input_id Desired capture card input ID.
     114        \return True if successful. */
     115    bool Store(unsigned int card_input_id) const;
     116
     117    /** Retrieves a value from this configuration chain by device id.
     118        \param dtv_dev_id Device id.
     119        \return Device scalar value. */
     120    double GetValue(int dtv_dev_id) const;
     121
     122    /** Sets a value for this configuration chain by device id.
     123        \param dtv_dev_id Device id.
     124        \param value Device scalar value. */
     125    void SetValue(int dtv_dev_id, double value);
     126   
     127  protected:
     128
     129    // map of dev tree id to configuration value
     130    typedef std::map<int, double> CONFIG;
     131    CONFIG m_config;
     132
     133    // current input id
     134    unsigned int m_input_id;
     135};
     136
     137class DVBDevTrees;
     138class DVBDevTree;
     139class DVBDevDevice;
     140class DVBDevRotor;
     141class DVBDevLnb;
     142
     143/** Main DVB-S device interface. */
     144class DVBDev
     145{
     146  public:
     147
     148    /** Retrieve device tree.
     149        \param card_id Capture card id.
     150        \param fd_frontend DVB frontend device file descriptor. */
     151    DVBDevTree* FindTree(unsigned int card_id);
     152
     153    /** Invalidate cached trees. */
     154    void InvalidateTrees();
     155   
     156  protected:
     157    static DVBDevTrees m_trees;
     158};
     159
     160/** Static-scoped locked tree list class. */
     161class DVBDevTrees
     162{
     163  public:
     164    ~DVBDevTrees();
     165
     166    DVBDevTree* FindTree(unsigned int card_id);
     167    void InvalidateTrees();
     168   
     169  protected:
     170    typedef std::map<unsigned int, DVBDevTree*> TREES;
     171    TREES m_trees;
     172    QMutex m_trees_lock;
     173};
     174
     175/** DVB-S device tree class. Represents a tree of DVB-S devices. */
     176class DVBDevTree
     177{
     178  public:
     179    /** Constructor. */
     180    DVBDevTree();
     181    ~DVBDevTree();
     182
     183    /** Loads the device tree from the database.
     184        \param card_id Capture card id.
     185        \return True if successful. */
     186    bool Load(unsigned int card_id);
     187
     188    /** Stores the device tree to the database.
     189        \param card_id Capture card id.
     190        \return True if successful. */
     191    bool Store(unsigned int card_id);
     192   
     193    /** Applies settings to the entire tree.
     194        \param settings Configuration chain to apply.
     195        \param tuning Tuning parameters.
     196        \return True if execution completed successfully. */
     197    bool Execute(const DVBDevSettings& settings,
     198                 const DVBTuning& tuning);
     199
     200    /** Reset state of nodes in tree, forcing updates on the
     201        next Execute command.
     202        \return True if reset completed successfully. */
     203    void Reset();
     204
     205    /** Returns the nth rotor device object in the tree.
     206        \param settings Configuration chain in effect.
     207        \param index 0 for first rotor, 1 for second, etc.
     208        \return Pointer to rotor object if found, NULL otherwise. */
     209    DVBDevRotor* FindRotor(const DVBDevSettings& settings,
     210                           unsigned int index = 0);
     211
     212    /** Returns the LNB device object selected by the configuration chain.
     213        \param settings Configuration chain in effect.
     214        \return Pointer to LNB object if found, NULL otherwise. */
     215    DVBDevLnb* FindLNB(const DVBDevSettings& settings);
     216
     217    /** Returns a device by ID.
     218        \param dev_id Device ID to find.
     219        \return Pointer to device, or NULL if not found in this tree. */
     220    DVBDevDevice* FindDevice(int dev_id);
     221
     222    /** Retrieves the root node in the tree.
     223        \return Pointer to device, or NULL if no root. */
     224    DVBDevDevice* Root() { return m_root; }
     225
     226    /** Changes the root node of the tree.
     227        \param root New root node (may be NULL). */
     228    void SetRoot(DVBDevDevice* root);
     229
     230    /** Sends a DiSEqC command.
     231        \param adr DiSEqC destination address.
     232        \param cmd DiSEqC command.
     233        \param repeats Number of times to repeat command.
     234        \param data_len Length of optional data.
     235        \param data Pointer to optional data. */
     236    bool SendCommand(unsigned int adr,
     237                     unsigned int cmd,
     238                     unsigned int repeats = 0,
     239                     unsigned int data_len = 0,
     240                     unsigned char* data = NULL);
     241
     242    /** Resets the DiSEqC bus.
     243        \param hard_reset If true, the bus will be power cycled.
     244        \return True if successful. */
     245    bool ResetDiseqc(bool hard_reset);
     246   
     247    // frontend fd
     248    void Open(int fd_frontend);
     249    void Close() { m_fd_frontend = -1; }
     250    int FrontendFD() const { return m_fd_frontend; }
     251
     252    // frontend operations
     253    bool SetTone(bool on);
     254    bool MiniDiseqc(fe_sec_mini_cmd cmd);
     255    bool SetVoltage(fe_sec_voltage voltage);
     256    bool SendDiseqc(const dvb_diseqc_master_cmd& cmd);
     257    fe_sec_voltage GetVoltage() const { return m_last_voltage; }
     258
     259    // tree management
     260    void AddDeferredDelete(unsigned int dev_id) { m_delete.push_back(dev_id); }
     261    int NextFakeID() { return --m_next_cnt; }
     262   
     263  protected:
     264    bool ApplyVoltage(const DVBDevSettings& settings,
     265                      const DVBTuning& tuning);
     266   
     267    int m_fd_frontend;
     268    DVBDevDevice *m_root;
     269    fe_sec_voltage m_last_voltage;
     270    mutable std::list<unsigned int> m_delete;
     271
     272    int m_next_cnt;
     273};
     274
     275/** DVB-S device class. Represents a node in a DVB-S device network. */
     276class DVBDevDevice
     277{
     278  public:
     279    /** Constructor.
     280        \param tree Parent reference to tree object.
     281        \param dtv_dev_id Device ID of this node. */
     282    DVBDevDevice(DVBDevTree& tree, int dtv_dev_id);
     283
     284    virtual ~DVBDevDevice();
     285
     286    /** Applies DiSEqC settings to this node and any children.
     287        \param settings Configuration chain to apply.
     288        \param tuning Tuning parameters.
     289        \return True if execution completed successfully. */
     290    virtual bool Execute(const DVBDevSettings& settings,
     291                         const DVBTuning& tuning) = 0;
     292
     293    /** Resets the last known settings for this device. Device
     294        will not actually have commands issued until next Execute() method. */
     295    virtual void Reset() = 0;
     296
     297    /** Determines if this device or any child will be sending a command
     298        for the given configuration chain.
     299        \param settings Configuration chain in effect.
     300        \return True if a command would be sent if Execute() were called. */
     301    virtual bool NeedsCommand(const DVBDevSettings& settings) const = 0;
     302
     303    /** Retrieves the selected child for this configuration, if any.
     304        \param settings Configuration chain in effect.
     305        \return Child node object, or NULL if none. */
     306    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const = 0;
     307
     308    /** Retrieves the proper number of children for this node.
     309        \return Number of children */
     310    virtual unsigned int NumChildren() const = 0;
     311
     312    /** Retrieves the nth child of this node.
     313        \param ordinal Child number (starting at 0).
     314        \return Pointer to device object, or NULL if no child. */
     315    virtual DVBDevDevice* GetChild(unsigned int ordinal) = 0;
     316
     317    /** Changes the nth child of this node.
     318        \param ordinal Child number (starting at 0).
     319        \param device New child device. (may be NULL)
     320        \return true if object was added to tree. */
     321    virtual bool SetChild(unsigned int ordinal,
     322                          DVBDevDevice* device) = 0;
     323
     324    /** Retrives the desired voltage for this config.
     325        \param settings Configuration chain in effect.
     326        \param tuning Tuning parameters.
     327        \return Voltage required. */
     328    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     329                                      const DVBTuning& tuning) const = 0;
     330   
     331    /** Loads this device from the database.
     332        \return True if successful. */
     333    virtual bool Load() = 0;
     334   
     335    /** Stores this device to the database.
     336        \return True if successful. */
     337    virtual bool Store() = 0;
     338   
     339    /** Returns a device by ID.
     340        \param dev_id Device ID to find.
     341        \return Pointer to device, or NULL if not found in this tree. */
     342    DVBDevDevice* FindDevice(int dev_id);
     343   
     344    static DVBDevDevice* CreateById(DVBDevTree& tree,
     345                                    int dtv_dev_id);
     346    static DVBDevDevice* CreateByType(DVBDevTree& tree,
     347                                      dvbdev_t type,
     348                                      int dev_id = -1);
     349
     350    int DeviceID() const { return m_dtv_dev_id; }
     351
     352    DVBDevDevice* GetParent() const { return m_parent; }
     353    void SetParent(DVBDevDevice* parent) { m_parent = parent; }
     354    unsigned int GetOrdinal() const { return m_ordinal; }
     355    void SetOrdinal(unsigned int ordinal) { m_ordinal = ordinal; }
     356
     357    void SetDeviceType(dvbdev_t type) { m_dev_type = type; }
     358    dvbdev_t GetDeviceType() const { return m_dev_type; }
     359
     360    QString GetDescription() const { return m_descr; }
     361    void SetDescription(const QString& descr) { m_descr = descr; }
     362   
     363  protected:
     364
     365    int m_dtv_dev_id;
     366    dvbdev_t m_dev_type;
     367    QString m_descr;
     368    DVBDevTree& m_tree;
     369    DVBDevDevice* m_parent;
     370    unsigned int m_ordinal;
     371    unsigned int m_repeat;
     372};
     373
     374/** Switch class, including tone, legacy and DiSEqC switches. */
     375class DVBDevSwitch : public DVBDevDevice
     376{
     377  public:
     378    DVBDevSwitch(DVBDevTree& tree, int dtv_dev_id);
     379    ~DVBDevSwitch();
     380
     381    virtual bool Execute(const DVBDevSettings& settings,
     382                         const DVBTuning& tuning);
     383    virtual void Reset();
     384    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     385    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const;
     386    virtual unsigned int NumChildren() const;
     387    virtual DVBDevDevice* GetChild(unsigned int ordinal);
     388    virtual bool SetChild(unsigned int ordinal, DVBDevDevice* device);
     389    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     390                                      const DVBTuning& tuning) const;
     391    virtual bool Load();
     392    virtual bool Store();
     393
     394    dvbdev_switch_t GetType() const { return m_type; }
     395    void SetType(dvbdev_switch_t type) { m_type = type; }
     396    unsigned int GetNumPorts() const { return m_num_ports; }
     397    void SetNumPorts(unsigned int num_ports);
     398   
     399  protected:
     400    bool ExecuteLegacy(const DVBDevSettings& settings,
     401                       const DVBTuning& tuning,
     402                       unsigned int pos);
     403    bool ExecuteTone(const DVBDevSettings& settings,
     404                     const DVBTuning& tuning,
     405                     unsigned int pos);
     406    bool ExecuteDiseqc(const DVBDevSettings& settings,
     407                       const DVBTuning& tuning,
     408                       unsigned int pos);
     409    bool GetPosition(const DVBDevSettings& settings,
     410                     unsigned int& pos) const;
     411   
     412  private:
     413    dvbdev_switch_t m_type;
     414    unsigned int m_num_ports;
     415    unsigned int m_last_pos;
     416
     417    typedef std::vector<DVBDevDevice*> CHILDREN;
     418    CHILDREN m_children;
     419};
     420
     421/** Rotor class. */
     422class DVBDevRotor : public DVBDevDevice
     423{
     424  public:
     425    DVBDevRotor(DVBDevTree& tree, int dtv_dev_id);
     426    ~DVBDevRotor();
     427
     428    virtual bool Execute(const DVBDevSettings& settings,
     429                         const DVBTuning& tuning);
     430    virtual void Reset();
     431    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     432    virtual DVBDevDevice* SelectedChild(const DVBDevSettings& settings) const;
     433    virtual unsigned int NumChildren() const { return 1; }
     434    virtual DVBDevDevice* GetChild(unsigned int) { return m_child; }
     435    virtual bool SetChild(unsigned int ordinal, DVBDevDevice* device);
     436    virtual fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     437                                      const DVBTuning& tuning) const;
     438    virtual bool Load();
     439    virtual bool Store();
     440
     441    /** Returns an indication of rotor progress.
     442        \return Scale from 0.0..1.0 indicating percentage complete of current
     443        move. A value of 1.0 indicates motion is complete. */
     444    double Progress() const;
     445
     446    /** Returns true if there is reasonable confidence in the value returned
     447        by Progress() -- otherwise, Progress() returns progress toward
     448        the time when the position will be approximately known. */
     449    bool IsPositionKnown() const { return m_last_pos_known; }
     450
     451    dvbdev_rotor_t GetType() const { return m_type; }
     452    void SetType(dvbdev_rotor_t type) { m_type = type; }
     453    double GetLoSpeed() const { return m_speed_lo; }
     454    void SetLoSpeed(double speed) { m_speed_lo = speed; }
     455    double GetHiSpeed() const { return m_speed_hi; }
     456    void SetHiSpeed(double speed) { m_speed_hi = speed; }
     457
     458    typedef std::map<unsigned int, double> POSMAP;
     459    POSMAP GetPosMap() const;
     460    void SetPosMap(const POSMAP& posmap);
     461   
     462  protected:
     463    bool ExecuteRotor(const DVBDevSettings& settings,
     464                      const DVBTuning& tuning,
     465                      double angle);
     466    bool ExecuteUSALS(const DVBDevSettings& settings,
     467                      const DVBTuning& tuning,
     468                      double angle);
     469
     470    double CalculateAzimuth(double angle) const;
     471    double GetApproxAzimuth();
     472    void RotorMoving(double azimuth);
     473   
     474  private:
     475    // configuration
     476    dvbdev_rotor_t m_type;
     477    double m_speed_hi;
     478    double m_speed_lo;
     479    typedef std::map<double, unsigned int> INTPOSMAP;
     480    INTPOSMAP m_posmap;
     481    DVBDevDevice *m_child;
     482
     483    // state
     484    double m_last_position;
     485    double m_last_azimuth;
     486    double m_desired_azimuth;
     487    double m_move_time;
     488    bool m_last_pos_known;
     489};
     490
     491/** LNB Class. */
     492class DVBDevLnb : public DVBDevDevice
     493{
     494  public:
     495    DVBDevLnb(DVBDevTree& tree, int dtv_dev_id);
     496
     497    virtual bool Execute(const DVBDevSettings& settings,
     498                         const DVBTuning& tuning);
     499    virtual void Reset();
     500    virtual bool NeedsCommand(const DVBDevSettings& settings) const;
     501
     502    // no children on LNBs
     503    virtual DVBDevDevice* SelectedChild(const DVBDevSettings&) const { return NULL; }
     504    virtual unsigned int NumChildren() const { return 0; }
     505    virtual DVBDevDevice* GetChild(unsigned int) { return NULL; }
     506    virtual bool SetChild(unsigned int, DVBDevDevice*) { return false; }
     507    fe_sec_voltage GetVoltage(const DVBDevSettings& settings,
     508                              const DVBTuning& tuning) const;
     509    virtual bool Load();   
     510    virtual bool Store();
     511   
     512    /** Determine if the high frequency band is active (for switchable LNBs).
     513        \param tuning Tuning parameters.
     514        \return True if high band is active. */
     515    bool IsHighBand(const DVBTuning& tuning) const;
     516
     517    /** Determine if horizontal polarity is active (for switchable LNBs).
     518        \param tuning Tuning parameters.
     519        \return True if polarity is horizontal. */
     520    bool IsHorizontal(const DVBTuning& tuning) const;
     521
     522    /** Calculate proper intermediate frequency for the given settings and
     523        tuning parameters.
     524        \param settings Configuration chain in effect.
     525        \param tuning Tuning parameters.
     526        \return Frequency for use with FE_SET_FRONTEND. */
     527    __u32 GetIF(const DVBDevSettings& settings,
     528                const DVBTuning& tuning) const;
     529
     530    dvbdev_lnb_t GetType() const { return m_type; }
     531    void SetType(dvbdev_lnb_t type) { m_type = type; }
     532    unsigned int GetLOFSwitch() const { return m_lof_switch; }
     533    void SetLOFSwitch(unsigned int lof_switch) { m_lof_switch = lof_switch; }
     534    unsigned int GetLOFHigh() const { return m_lof_hi; }
     535    void SetLOFHigh(unsigned int lof_hi) { m_lof_hi = lof_hi; }
     536    unsigned int GetLOFLow() const { return m_lof_lo; }
     537    void SetLOFLow(unsigned int lof_lo) { m_lof_lo = lof_lo; }
     538   
     539  private:
     540    dvbdev_lnb_t m_type;
     541   
     542    unsigned int m_lof_switch;
     543    unsigned int m_lof_hi;
     544    unsigned int m_lof_lo;
     545};
     546
     547bool DatabaseDiseqcImport();
     548bool DatabaseDiseqcUpgrade();
     549
     550#endif // DVBDISEQC_H