add a blinking point to the infobar
[enigma2.git] / lib / dvb / frontend.cpp
index d4704a9d0305347ff01835bc0841b02458ce02f8..a05963cd2bd892df8fc173edbfb3ba8a39cb73d6 100644 (file)
@@ -6,6 +6,10 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 
+#ifndef I2C_SLAVE_FORCE
+#define I2C_SLAVE_FORCE        0x0706
+#endif
+
 #if HAVE_DVB_API_VERSION < 3
 #include <ost/frontend.h>
 #include <ost/sec.h>
@@ -23,9 +27,9 @@
 #include <linux/dvb/frontend.h>
 #endif
 
-#include <lib/dvb_si/satellite_delivery_system_descriptor.h>
-#include <lib/dvb_si/cable_delivery_system_descriptor.h>
-#include <lib/dvb_si/terrestrial_delivery_system_descriptor.h>
+#include <dvbsi++/satellite_delivery_system_descriptor.h>
+#include <dvbsi++/cable_delivery_system_descriptor.h>
+#include <dvbsi++/terrestrial_delivery_system_descriptor.h>
 
 void eDVBFrontendParametersSatellite::set(const SatelliteDeliverySystemDescriptor &descriptor)
 {
@@ -127,21 +131,21 @@ RESULT eDVBFrontendParameters::getDVBT(eDVBFrontendParametersTerrestrial &p) con
        return 0;
 }
 
-RESULT eDVBFrontendParameters::setDVBS(eDVBFrontendParametersSatellite &p)
+RESULT eDVBFrontendParameters::setDVBS(const eDVBFrontendParametersSatellite &p)
 {
        sat = p;
        m_type = iDVBFrontend::feSatellite;
        return 0;
 }
 
-RESULT eDVBFrontendParameters::setDVBC(eDVBFrontendParametersCable &p)
+RESULT eDVBFrontendParameters::setDVBC(const eDVBFrontendParametersCable &p)
 {
        cable = p;
        m_type = iDVBFrontend::feCable;
        return 0;
 }
 
-RESULT eDVBFrontendParameters::setDVBT(eDVBFrontendParametersTerrestrial &p)
+RESULT eDVBFrontendParameters::setDVBT(const eDVBFrontendParametersTerrestrial &p)
 {
        terrestrial = p;
        m_type = iDVBFrontend::feTerrestrial;
@@ -204,7 +208,7 @@ RESULT eDVBFrontendParameters::getHash(unsigned long &hash) const
 
 DEFINE_REF(eDVBFrontend);
 
-eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): m_type(-1)
+eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): m_type(-1), m_fe(fe), m_curVoltage(-1)
 {
 #if HAVE_DVB_API_VERSION < 3
        char sec_filename[128];
@@ -271,7 +275,7 @@ eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): m_type(-1)
                ok = 0;
                return;
        }
-       eDebug("detected %s frontend", "satellite\0cable\0    terrestrial"+fe_info.type*9);
+       eDebug("detected %s frontend", "satellite\0cable\0    terrestrial"+fe_info.type*10);
        ok = 1;
 
        m_sn = new eSocketNotifier(eApp, m_fd, eSocketNotifier::Read);
@@ -284,7 +288,9 @@ eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): m_type(-1)
        m_tuneTimer = new eTimer(eApp);
        CONNECT(m_tuneTimer->timeout, eDVBFrontend::tuneLoop);
 
-       memset(m_data, 0xFFFF, sizeof(m_data));
+       int entries = sizeof(m_data) / sizeof(int);
+       for (int i=0; i<entries; ++i)
+               m_data[i] = -1;
 
        return;
 }
@@ -337,7 +343,12 @@ void eDVBFrontend::feEvent(int w)
                        if (m_tuning)
                                state = stateTuning;
                        else
-                               state = stateFailed;
+                       {
+                               state = stateLostLock;
+
+                               if (m_state != stateLostLock)
+                                       eDebug("FIXME: we lost lock, so we might have to retune.");
+                       }
                }
                if (m_state != state)
                {
@@ -359,6 +370,7 @@ void eDVBFrontend::timeout()
                        m_state = state;
                        m_stateChanged(this);
                }
+               m_tuning = 0;
        } else
                m_tuning = 0;
 }
@@ -435,8 +447,9 @@ void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
                                        ++m_sec_sequence.current();
                                break;
                        case eSecCommand::SET_VOLTAGE:
-                               eDebug("[SEC] setVoltage %d", m_sec_sequence.current()->voltage);
-                               setVoltage(m_sec_sequence.current()++->voltage);
+                               int voltage = m_sec_sequence.current()++->voltage;
+                               eDebug("[SEC] setVoltage %d", voltage);
+                               setVoltage(voltage);
                                break;
                        case eSecCommand::SET_TONE:
                                eDebug("[SEC] setTone %d", m_sec_sequence.current()->tone);
@@ -444,10 +457,10 @@ void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
                                break;
                        case eSecCommand::SEND_DISEQC:
                                sendDiseqc(m_sec_sequence.current()->diseqc);
-                               eDebugNoNewLine("sendDiseqc: ");
+                               eDebugNoNewLine("[SEC] sendDiseqc: ");
                                for (int i=0; i < m_sec_sequence.current()->diseqc.len; ++i)
                                    eDebugNoNewLine("%02x", m_sec_sequence.current()->diseqc.data[i]);
-                               eDebug("[SEC] ");
+                               eDebug("");
                                ++m_sec_sequence.current();
                                break;
                        case eSecCommand::SEND_TONEBURST:
@@ -460,10 +473,17 @@ void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
                                ++m_sec_sequence.current();
                                break;
                        case eSecCommand::MEASURE_IDLE_INPUTPOWER:
-                               m_idleInputpower = readInputpower();
-                               eDebug("[SEC] idleInputpower is %d", m_idleInputpower);
-                               ++m_sec_sequence.current();
+                       {
+                               int idx = m_sec_sequence.current()++->val;
+                               if ( idx == 0 || idx == 1 )
+                               {
+                                       m_idleInputpower[idx] = readInputpower();
+                                       eDebug("[SEC] idleInputpower[%d] is %d", idx, m_idleInputpower[idx]);
+                               }
+                               else
+                                       eDebug("[SEC] idleInputpower measure index(%d) out of bound !!!", idx);
                                break;
+                       }
                        case eSecCommand::MEASURE_RUNNING_INPUTPOWER:
                                m_runningInputpower = readInputpower();
                                eDebug("[SEC] runningInputpower is %d", m_runningInputpower);
@@ -481,26 +501,57 @@ void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
                                break;
                        case eSecCommand::IF_TIMEOUT_GOTO:
                                if (!m_timeoutCount)
+                               {
+                                       eDebug("[SEC] rotor timout");
                                        setSecSequencePos(m_sec_sequence.current()->steps);
+                               }
                                else
                                        ++m_sec_sequence.current();
                                break;
+                       case eSecCommand::SET_POWER_LIMITING_MODE:
+                       {
+                               int fd=::open("/dev/i2c/0", O_RDWR);
+                               unsigned char data[2];
+                               ::ioctl(fd, I2C_SLAVE_FORCE, 0x10 >> 1);
+                               if(::read(fd, data, 1) != 1)
+                                       eDebug("[SEC] error read lnbp (%m)");
+                               if ( m_sec_sequence.current()->mode == eSecCommand::modeStatic )
+                               {
+                                       data[0] |= 0x90;  // enable static current limiting
+                                       eDebug("[SEC] set static current limiting");
+                               }
+                               else
+                               {
+                                       data[0] &= ~0x90;  // enable dynamic current limiting
+                                       eDebug("[SEC] set dynamic current limiting");
+                               }
+                               if(::write(fd, data, 1) != 1)
+                                       eDebug("[SEC] error write lnbp (%m)");
+                               ::close(fd);
+                               ++m_sec_sequence.current();
+                               break;
+                       }
+                       case eSecCommand::IF_IDLE_INPUTPOWER_AVAIL_GOTO:
+                               if (m_idleInputpower[0] && m_idleInputpower[1] && setSecSequencePos(m_sec_sequence.current()->steps))
+                                       break;
+                               ++m_sec_sequence.current();
+                               break;
                        case eSecCommand::IF_INPUTPOWER_DELTA_GOTO:
                        {
+                               int idleInputpower = m_idleInputpower[m_curVoltage == iDVBFrontend::voltage13 ? 0 : 1];
                                eSecCommand::rotor &cmd = m_sec_sequence.current()->measure;
                                const char *txt = cmd.direction ? "running" : "stopped";
-                               eDebug("[SEC] waiting for rotor %s", txt);
-                               eDebug("[SEC] %s %d, idle %d, delta %d",
+                               eDebug("[SEC] waiting for rotor %s %d, idle %d, delta %d",
                                        txt,
                                        m_runningInputpower,
-                                       m_idleInputpower,
+                                       idleInputpower,
                                        cmd.deltaA);
-                               if ( (cmd.direction && abs(m_runningInputpower - m_idleInputpower) >= cmd.deltaA)
-                                       || (!cmd.direction && abs(m_runningInputpower - m_idleInputpower) <= cmd.deltaA) )
+                               if ( (cmd.direction && abs(m_runningInputpower - idleInputpower) >= cmd.deltaA)
+                                       || (!cmd.direction && abs(m_runningInputpower - idleInputpower) <= cmd.deltaA) )
                                {
                                        ++cmd.okcount;
                                        eDebug("[SEC] rotor %s step %d ok", txt, cmd.okcount);
-                                       if ( cmd.okcount > 1 )
+                                       if ( cmd.okcount > 6 )
                                        {
                                                eDebug("[SEC] rotor is %s", txt);
                                                if (setSecSequencePos(cmd.steps))
@@ -516,6 +567,14 @@ void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
                                ++m_sec_sequence.current();
                                break;
                        }
+                       case eSecCommand::IF_VOLTAGE_GOTO:
+                       {
+                               eSecCommand::pair &compare = m_sec_sequence.current()->compare;
+                               if ( compare.voltage == m_curVoltage && setSecSequencePos(compare.steps) )
+                                       break;
+                               ++m_sec_sequence.current();
+                               break;
+                       }
                        default:
                                ++m_sec_sequence.current();
                                eDebug("[SEC] unhandled sec command");
@@ -559,8 +618,6 @@ RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
 
        m_sec_sequence.clear();
 
-       eDebug("eDVBFrontend::tune. type: %d", m_type);
-
        switch (m_type)
        {
        case feSatellite:
@@ -578,7 +635,7 @@ RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
                        return -ENOENT;
                }
                
-               res = m_sec->prepare(*this, parm, feparm);
+               res = m_sec->prepare(*this, parm, feparm, 1 << m_fe);
                if (res)
                        return res;
 #if HAVE_DVB_API_VERSION < 3
@@ -590,10 +647,115 @@ RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
        }
        case feCable:
        {
+#if HAVE_DVB_API_VERSION >= 3
                eDVBFrontendParametersCable feparm;
                if (where.getDVBC(feparm))
                        return -EINVAL;
-               eFatal("cable tuning nyi");
+#if HAVE_DVB_API_VERSION < 3
+               parm.Frequency = feparm.frequency * 1000;
+               parm.u.qam.SymbolRate = feparm.symbol_rate;
+#else
+               parm.frequency = feparm.frequency * 1000;
+               parm.u.qam.symbol_rate = feparm.symbol_rate;
+#endif
+               fe_modulation_t mod;
+               switch (feparm.modulation)
+               {
+               case eDVBFrontendParametersCable::Modulation::QAM16:
+                       mod = QAM_16;
+                       break;
+               case eDVBFrontendParametersCable::Modulation::QAM32:
+                       mod = QAM_32;
+                       break;
+               case eDVBFrontendParametersCable::Modulation::QAM64:
+                       mod = QAM_64;
+                       break;
+               case eDVBFrontendParametersCable::Modulation::QAM128:
+                       mod = QAM_128;
+                       break;
+               case eDVBFrontendParametersCable::Modulation::QAM256:
+                       mod = QAM_256;
+                       break;                  
+               case eDVBFrontendParametersCable::Modulation::Auto:
+                       mod = QAM_AUTO;
+                       break;                  
+               }
+#if HAVE_DVB_API_VERSION < 3
+               parm.u.qam.QAM = mod;
+#else
+               parm.u.qam.modulation = mod;
+#endif
+               switch (feparm.inversion)
+               {               
+               case eDVBFrontendParametersCable::Inversion::On:
+                       #if HAVE_DVB_API_VERSION < 3
+                       parm.Inversion =
+                       #else
+                       parm.inversion =
+                       #endif
+                               INVERSION_ON;
+                       break;
+               case eDVBFrontendParametersCable::Inversion::Off:
+                       #if HAVE_DVB_API_VERSION < 3
+                       parm.Inversion =
+                       #else
+                       parm.inversion =
+                       #endif
+                               INVERSION_OFF;
+                       break;
+               case eDVBFrontendParametersCable::Inversion::Unknown:
+                       #if HAVE_DVB_API_VERSION < 3
+                       parm.Inversion =
+                       #else
+                       parm.inversion =
+                       #endif
+                               INVERSION_AUTO;
+                       break;
+               }
+               
+               fe_code_rate_t fec_inner;
+               switch (feparm.fec_inner)
+               {               
+               case eDVBFrontendParametersCable::FEC::fNone:
+                       fec_inner = FEC_NONE;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f1_2:
+                       fec_inner = FEC_1_2;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f2_3:
+                       fec_inner = FEC_2_3;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f3_4:
+                       fec_inner = FEC_3_4;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f4_5:
+                       fec_inner = FEC_4_5;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f5_6:
+                       fec_inner = FEC_5_6;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f6_7:
+                       fec_inner = FEC_6_7;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f7_8:
+                       fec_inner = FEC_7_8;
+                       break;
+               case eDVBFrontendParametersCable::FEC::f8_9:
+                       fec_inner = FEC_8_9;
+                       break;
+               case eDVBFrontendParametersCable::FEC::fAuto:
+                       fec_inner = FEC_AUTO;
+                       break;
+               }
+#if HAVE_DVB_API_VERSION < 3
+               parm.u.qam.FEC_inner = fec_inner;
+#else
+               parm.u.qam.fec_inner = fec_inner;
+#endif
+#else
+               eFatal("Old API not fully supported");
+#endif // old api
+               break;
        }
        case feTerrestrial:
        {
@@ -725,6 +887,7 @@ RESULT eDVBFrontend::setVoltage(int voltage)
        fe_sec_voltage_t vlt;
 #endif
 
+       m_curVoltage=voltage;
        switch (voltage)
        {
        case voltageOff:
@@ -834,7 +997,7 @@ RESULT eDVBFrontend::setSecSequence(const eSecCommandList &list)
 
 RESULT eDVBFrontend::getData(int num, int &data)
 {
-       if ( num < 7 )
+       if ( num < (int)(sizeof(m_data)/sizeof(int)) )
        {
                data = m_data[num];
                return 0;
@@ -844,7 +1007,7 @@ RESULT eDVBFrontend::getData(int num, int &data)
 
 RESULT eDVBFrontend::setData(int num, int val)
 {
-       if ( num < 7 )
+       if ( num < (int)(sizeof(m_data)/sizeof(int)) )
        {
                m_data[num] = val;
                return 0;
@@ -852,3 +1015,18 @@ RESULT eDVBFrontend::setData(int num, int val)
        return -EINVAL;
 }
 
+int eDVBFrontend::isCompatibleWith(ePtr<iDVBFrontendParameters> &feparm)
+{
+       int type;
+       if (feparm->getSystem(type) || type != m_type)
+               return 0;
+
+       if (m_type == eDVBFrontend::feSatellite)
+       {
+               ASSERT(m_sec);
+               eDVBFrontendParametersSatellite sat_parm;
+               ASSERT(!feparm->getDVBS(sat_parm));
+               return m_sec->canTune(sat_parm, this, 1 << m_fe);
+       }
+       return 1;
+}