add lockState and syncState to getFrontendInfo call
[enigma2.git] / lib / dvb / frontend.cpp
1 #include <lib/dvb/dvb.h>
2 #include <lib/base/eerror.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/ioctl.h>
7
8 #ifndef I2C_SLAVE_FORCE
9 #define I2C_SLAVE_FORCE 0x0706
10 #endif
11
12 #if HAVE_DVB_API_VERSION < 3
13 #include <ost/frontend.h>
14 #include <ost/sec.h>
15 #define QAM_AUTO                                (Modulation)6
16 #define TRANSMISSION_MODE_AUTO  (TransmitMode)2
17 #define BANDWIDTH_AUTO                  (BandWidth)3
18 #define GUARD_INTERVAL_AUTO             (GuardInterval)4
19 #define HIERARCHY_AUTO                  (Hierarchy)4
20 #define parm.frequency parm.Frequency
21 #define parm.u.qam.symbol_rate parm.u.qam.SymbolRate
22 #define parm.u.qam.fec_inner parm.u.qam.FEC_inner
23 #define parm.u.qam.modulation parm.u.qam.QAM
24 #define parm.u.ofdm.bandwidth parm.u.ofdm.bandWidth
25 #define parm.u.ofdm.code_rate_LP parm.u.ofdm.LP_CodeRate
26 #define parm.u.ofdm.code_rate_HP parm.u.ofdm.HP_CodeRate
27 #define parm.u.ofdm.constellation parm.u.ofdm.Constellation
28 #define parm.u.ofdm.transmission_mode parm.u.ofdm.TransmissionMode
29 #define parm.u.ofdm.guard_interval parm.u.ofdm.guardInterval
30 #define parm.u.ofdm.hierarchy_information parm.u.ofdm.HierarchyInformation
31 #define parm.inversion parm.Inversion
32 #else
33 #include <linux/dvb/frontend.h>
34 #endif
35
36 #include <dvbsi++/satellite_delivery_system_descriptor.h>
37 #include <dvbsi++/cable_delivery_system_descriptor.h>
38 #include <dvbsi++/terrestrial_delivery_system_descriptor.h>
39
40 void eDVBFrontendParametersSatellite::set(const SatelliteDeliverySystemDescriptor &descriptor)
41 {
42         frequency    = descriptor.getFrequency() * 10;
43         symbol_rate  = descriptor.getSymbolRate() * 100;
44         polarisation = descriptor.getPolarization();
45         fec = descriptor.getFecInner();
46         if ( fec == 0xF )
47                 fec = FEC::fNone;
48         inversion = Inversion::Unknown;
49         orbital_position  = ((descriptor.getOrbitalPosition() >> 12) & 0xF) * 1000;
50         orbital_position += ((descriptor.getOrbitalPosition() >> 8) & 0xF) * 100;
51         orbital_position += ((descriptor.getOrbitalPosition() >> 4) & 0xF) * 10;
52         orbital_position += ((descriptor.getOrbitalPosition()) & 0xF);
53         if (orbital_position && (!descriptor.getWestEastFlag()))
54                 orbital_position = 3600 - orbital_position;
55         eDebug("SAT freq %d, %s, pos %d, sr %d, fec %d",
56                 frequency,
57                 polarisation ? "hor" : "vert",
58                 orbital_position,
59                 symbol_rate, fec);
60 }
61
62 void eDVBFrontendParametersCable::set(const CableDeliverySystemDescriptor &descriptor)
63 {
64         frequency = descriptor.getFrequency() / 10;
65         symbol_rate = descriptor.getSymbolRate() * 100;
66         fec_inner = descriptor.getFecInner();
67         if ( fec_inner == 0xF )
68                 fec_inner = FEC::fNone;
69         modulation = descriptor.getModulation();
70         if ( modulation > 0x5 )
71                 modulation = Modulation::Auto;
72         inversion = Inversion::Unknown;
73         eDebug("Cable freq %d, mod %d, sr %d, fec %d",
74                 frequency,
75                 modulation, symbol_rate, fec_inner);
76 }
77
78 void eDVBFrontendParametersTerrestrial::set(const TerrestrialDeliverySystemDescriptor &descriptor)
79 {
80         frequency = descriptor.getCentreFrequency() * 10;
81         bandwidth = descriptor.getBandwidth();
82         if ( bandwidth > 2 ) // 5Mhz forced to auto
83                 bandwidth = Bandwidth::BwAuto;
84         code_rate_HP = descriptor.getCodeRateHpStream();
85         if (code_rate_HP > 4)
86                 code_rate_HP = FEC::fAuto;
87         code_rate_LP = descriptor.getCodeRateLpStream();
88         if (code_rate_LP > 4)
89                 code_rate_LP = FEC::fAuto;
90         transmission_mode = descriptor.getTransmissionMode();
91         if (transmission_mode > 2)
92                 transmission_mode = TransmissionMode::TMAuto;
93         guard_interval = descriptor.getGuardInterval();
94         if (guard_interval > 3)
95                 guard_interval = GuardInterval::GI_Auto;
96         hierarchy = descriptor.getHierarchyInformation()&3;
97         modulation = descriptor.getConstellation();
98         if (modulation > 2)
99                 modulation = Modulation::Auto;
100         inversion = Inversion::Unknown;
101         eDebug("Terr freq %d, bw %d, cr_hp %d, cr_lp %d, tm_mode %d, guard %d, hierarchy %d, const %d",
102                 frequency, bandwidth, code_rate_HP, code_rate_LP, transmission_mode,
103                 guard_interval, hierarchy, modulation);
104 }
105
106 eDVBFrontendParameters::eDVBFrontendParameters(): m_type(-1)
107 {
108 }
109
110 DEFINE_REF(eDVBFrontendParameters);
111
112 RESULT eDVBFrontendParameters::getSystem(int &t) const
113 {
114         if (m_type == -1)
115                 return -1;
116         t = m_type;
117         return 0;
118 }
119
120 RESULT eDVBFrontendParameters::getDVBS(eDVBFrontendParametersSatellite &p) const
121 {
122         if (m_type != iDVBFrontend::feSatellite)
123                 return -1;
124         p = sat;
125         return 0;
126 }
127
128 RESULT eDVBFrontendParameters::getDVBC(eDVBFrontendParametersCable &p) const
129 {
130         if (m_type != iDVBFrontend::feCable)
131                 return -1;
132         p = cable;
133         return 0;
134 }
135
136 RESULT eDVBFrontendParameters::getDVBT(eDVBFrontendParametersTerrestrial &p) const
137 {
138         if (m_type != iDVBFrontend::feTerrestrial)
139                 return -1;
140         p = terrestrial;
141         return 0;
142 }
143
144 RESULT eDVBFrontendParameters::setDVBS(const eDVBFrontendParametersSatellite &p)
145 {
146         sat = p;
147         m_type = iDVBFrontend::feSatellite;
148         return 0;
149 }
150
151 RESULT eDVBFrontendParameters::setDVBC(const eDVBFrontendParametersCable &p)
152 {
153         cable = p;
154         m_type = iDVBFrontend::feCable;
155         return 0;
156 }
157
158 RESULT eDVBFrontendParameters::setDVBT(const eDVBFrontendParametersTerrestrial &p)
159 {
160         terrestrial = p;
161         m_type = iDVBFrontend::feTerrestrial;
162         return 0;
163 }
164
165 RESULT eDVBFrontendParameters::calculateDifference(const iDVBFrontendParameters *parm, int &diff) const
166 {
167         if (!parm)
168                 return -1;
169         int type;
170         if (parm->getSystem(type))
171                 return -1;
172         if (type != m_type)
173         {
174                 diff = 1<<30; // big difference
175                 return 0;
176         }
177         
178         switch (type)
179         {
180         case iDVBFrontend::feSatellite:
181         {
182                 eDVBFrontendParametersSatellite osat;
183                 if (parm->getDVBS(osat))
184                         return -2;
185                 
186                 if (sat.orbital_position != osat.orbital_position)
187                         diff = 1<<29;
188                 else if (sat.polarisation != osat.polarisation)
189                         diff = 1<<28;
190                 else
191                 {
192                         diff = abs(sat.frequency - osat.frequency);
193                         diff += abs(sat.symbol_rate - osat.symbol_rate);
194                 }
195                 return 0;
196         }
197         case iDVBFrontend::feCable:
198                 eDVBFrontendParametersCable ocable;
199                 if (parm->getDVBC(ocable))
200                         return -2;
201                 
202                 if (cable.modulation != ocable.modulation && cable.modulation != eDVBFrontendParametersCable::Modulation::Auto && ocable.modulation != eDVBFrontendParametersCable::Modulation::Auto)
203                         diff = 1 << 29;
204                 else if (cable.inversion != ocable.inversion && cable.inversion != eDVBFrontendParametersCable::Inversion::Unknown && ocable.inversion != eDVBFrontendParametersCable::Inversion::Unknown)
205                         diff = 1 << 28;
206                 else
207                 {
208                         diff = abs(cable.frequency - ocable.frequency);
209                         diff += abs(cable.symbol_rate - ocable.symbol_rate);
210                 }
211                 
212                 return 0;
213         case iDVBFrontend::feTerrestrial:
214                 eDVBFrontendParametersTerrestrial oterrestrial;
215                 if (parm->getDVBT(oterrestrial))
216                         return -2;
217                 
218                 diff = abs(terrestrial.frequency - oterrestrial.frequency);
219
220                 return 0;
221         default:
222                 return -1;
223         }
224         return 0;
225 }
226
227 RESULT eDVBFrontendParameters::getHash(unsigned long &hash) const 
228 {
229         switch (m_type)
230         {
231         case iDVBFrontend::feSatellite:
232         {
233                 hash = (sat.orbital_position << 16);
234                 hash |= ((sat.frequency/1000)&0xFFFF)|((sat.polarisation&1) << 15);
235                 return 0;
236         }
237         case iDVBFrontend::feCable:
238         case iDVBFrontend::feTerrestrial:
239         default:
240                 return -1;
241         }
242 }
243
244 DEFINE_REF(eDVBFrontend);
245
246 eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok)
247         :m_type(-1), m_fe(fe), m_fd(-1), m_timeout(0), m_tuneTimer(0)
248 #if HAVE_DVB_API_VERSION < 3
249         ,m_secfd(-1)
250 #endif
251 {
252 #if HAVE_DVB_API_VERSION < 3
253         sprintf(m_filename, "/dev/dvb/card%d/frontend%d", adap, fe);
254         sprintf(m_sec_filename, "/dev/dvb/card%d/sec%d", adap, fe);
255 #else
256         sprintf(m_filename, "/dev/dvb/adapter%d/frontend%d", adap, fe);
257 #endif
258         m_timeout = new eTimer(eApp);
259         CONNECT(m_timeout->timeout, eDVBFrontend::timeout);
260
261         m_tuneTimer = new eTimer(eApp);
262         CONNECT(m_tuneTimer->timeout, eDVBFrontend::tuneLoop);
263
264         int entries = sizeof(m_data) / sizeof(int);
265         for (int i=0; i<entries; ++i)
266                 m_data[i] = -1;
267
268         m_idleInputpower[0]=m_idleInputpower[1]=0;
269
270         ok = !openFrontend();
271         closeFrontend();
272 }
273
274 int eDVBFrontend::openFrontend()
275 {
276         if (m_fd >= 0)
277                 return -1;  // already opened
278
279         m_state=0;
280         m_tuning=0;
281
282 #if HAVE_DVB_API_VERSION < 3
283         m_secfd = ::open(m_sec_filename, O_RDWR);
284         if (m_secfd < 0)
285         {
286                 eWarning("failed! (%s) %m", m_sec_filename);
287                 return -1;
288         }
289         FrontendInfo fe_info;
290 #else
291         dvb_frontend_info fe_info;
292 #endif
293         eDebug("opening frontend %d", m_fe);
294         m_fd = ::open(m_filename, O_RDWR|O_NONBLOCK);
295         if (m_fd < 0)
296         {
297                 eWarning("failed! (%s) %m", m_filename);
298 #if HAVE_DVB_API_VERSION < 3
299                 ::close(m_secfd);
300                 m_secfd=-1;
301 #endif
302                 return -1;
303         }
304
305         if (m_type == -1)
306         {
307                 if (::ioctl(m_fd, FE_GET_INFO, &fe_info) < 0)
308                 {
309                         eWarning("ioctl FE_GET_INFO failed");
310                         ::close(m_fd);
311                         m_fd = -1;
312 #if HAVE_DVB_API_VERSION < 3
313                         ::close(m_secfd);
314                         m_secfd=-1;
315 #endif
316                         return -1;
317                 }
318
319                 switch (fe_info.type)
320                 {
321                 case FE_QPSK:
322                         m_type = iDVBFrontend::feSatellite;
323                         break;
324                 case FE_QAM:
325                         m_type = iDVBFrontend::feCable;
326                         break;
327                 case FE_OFDM:
328                         m_type = iDVBFrontend::feTerrestrial;
329                         break;
330                 default:
331                         eWarning("unknown frontend type.");
332                         ::close(m_fd);
333                         m_fd = -1;
334 #if HAVE_DVB_API_VERSION < 3
335                         ::close(m_secfd);
336                         m_secfd=-1;
337 #endif
338                         return -1;
339                 }
340                 eDebug("detected %s frontend", "satellite\0cable\0    terrestrial"+fe_info.type*10);
341         }
342
343         setTone(iDVBFrontend::toneOff);
344         setVoltage(iDVBFrontend::voltageOff);
345
346         m_sn = new eSocketNotifier(eApp, m_fd, eSocketNotifier::Read);
347         CONNECT(m_sn->activated, eDVBFrontend::feEvent);
348         m_sn->start();
349
350         return 0;
351 }
352
353 int eDVBFrontend::closeFrontend()
354 {
355         if (!m_fe && m_data[7] != -1)
356         {
357                 // try to close the first frontend.. but the second is linked to the first
358                 eDVBRegisteredFrontend *linked_fe = (eDVBRegisteredFrontend*)m_data[7];
359                 if (linked_fe->m_inuse)
360                 {
361                         eDebug("dont close frontend %d until the linked frontend %d is still in use",
362                                 m_fe, linked_fe->m_frontend->getID());
363                         return -1;
364                 }
365         }
366         if (m_fd >= 0)
367         {
368                 eDebug("close frontend %d", m_fe);
369                 setTone(iDVBFrontend::toneOff);
370                 setVoltage(iDVBFrontend::voltageOff);
371                 ::close(m_fd);
372                 m_fd=-1;
373                 m_data[0] = m_data[1] = m_data[2] = -1;
374         }
375 #if HAVE_DVB_API_VERSION < 3
376         if (m_secfd >= 0)
377         {
378                 ::close(m_secfd);
379                 m_secfd=-1;
380         }
381 #endif
382         delete m_sn;
383         m_sn=0;
384
385         return 0;
386 }
387
388 eDVBFrontend::~eDVBFrontend()
389 {
390         closeFrontend();
391         delete m_timeout;
392         delete m_tuneTimer;
393 }
394
395 void eDVBFrontend::feEvent(int w)
396 {
397         while (1)
398         {
399 #if HAVE_DVB_API_VERSION < 3
400                 FrontendEvent event;
401 #else
402                 dvb_frontend_event event;
403 #endif
404                 int res;
405                 int state;
406                 res = ::ioctl(m_fd, FE_GET_EVENT, &event);
407                 
408                 if (res && (errno == EAGAIN))
409                         break;
410
411                 if (res)
412                 {
413                         eWarning("FE_GET_EVENT failed! %m");
414                         return;
415                 }
416                 
417                 if (w < 0)
418                         continue;
419
420 #if HAVE_DVB_API_VERSION < 3
421                 if (event.type == FE_COMPLETION_EV)
422 #else
423                 eDebug("(%d)fe event: status %x, inversion %s", m_fe, event.status, (event.parameters.inversion == INVERSION_ON) ? "on" : "off");
424                 if (event.status & FE_HAS_LOCK)
425 #endif
426                 {
427                         state = stateLock;
428                 } else
429                 {
430                         if (m_tuning)
431                                 state = stateTuning;
432                         else
433                         {
434                                 state = stateLostLock;
435                                 m_data[0] = m_data[1] = m_data[2] = -1; // reset diseqc
436                         }
437                 }
438                 if (m_state != state)
439                 {
440                         m_state = state;
441                         m_stateChanged(this);
442                 }
443         }
444 }
445
446 void eDVBFrontend::timeout()
447 {
448         m_tuning = 0;
449         if (m_state == stateTuning)
450         {
451                 m_state = stateFailed;
452                 m_stateChanged(this);
453         }
454 }
455
456 int eDVBFrontend::readFrontendData(int type)
457 {
458         switch(type)
459         {
460                 case bitErrorRate:
461                 {
462                         uint32_t ber=0;
463                         if (ioctl(m_fd, FE_READ_BER, &ber) < 0 && errno != ERANGE)
464                                 eDebug("FE_READ_BER failed (%m)");
465                         return ber;
466                 }
467                 case signalPower:
468                 {
469                         uint16_t snr=0;
470                         if (ioctl(m_fd, FE_READ_SNR, &snr) < 0 && errno != ERANGE)
471                                 eDebug("FE_READ_SNR failed (%m)");
472                         return snr;
473                 }
474                 case signalQuality:
475                 {
476                         uint16_t strength=0;
477                         if (ioctl(m_fd, FE_READ_SIGNAL_STRENGTH, &strength) < 0 && errno != ERANGE)
478                                 eDebug("FE_READ_SIGNAL_STRENGTH failed (%m)");
479                         return strength;
480                 }
481                 case Locked:
482                 {
483 #if HAVE_DVB_API_VERSION < 3
484                         FrontendStatus status=0;
485 #else
486                         fe_status_t status;
487 #endif
488                         if ( ioctl(m_fd, FE_READ_STATUS, &status) < 0 && errno != ERANGE )
489                                 eDebug("FE_READ_STATUS failed (%m)");
490                         return !!(status&FE_HAS_LOCK);
491                 }
492                 case Synced:
493                 {
494 #if HAVE_DVB_API_VERSION < 3
495                         FrontendStatus status=0;
496 #else
497                         fe_status_t status;
498 #endif
499                         if ( ioctl(m_fd, FE_READ_STATUS, &status) < 0 && errno != ERANGE )
500                                 eDebug("FE_READ_STATUS failed (%m)");
501                         return !!(status&FE_HAS_SYNC);
502                 }
503         }
504         return 0;
505 }
506
507 PyObject *eDVBFrontend::readTransponderData()
508 {
509         Py_INCREF(Py_None);
510         return Py_None;
511 }
512
513 #ifndef FP_IOCTL_GET_ID
514 #define FP_IOCTL_GET_ID 0
515 #endif
516 int eDVBFrontend::readInputpower()
517 {
518         int power=m_fe;  // this is needed for read inputpower from the correct tuner !
519
520         // open front prozessor
521         int fp=::open("/dev/dbox/fp0", O_RDWR);
522         if (fp < 0)
523         {
524                 eDebug("couldn't open fp");
525                 return -1;
526         }
527         static bool old_fp = (::ioctl(fp, FP_IOCTL_GET_ID) < 0);
528         if ( ioctl( fp, old_fp ? 9 : 0x100, &power ) < 0 )
529         {
530                 eDebug("FP_IOCTL_GET_LNB_CURRENT failed (%m)");
531                 return -1;
532         }
533         ::close(fp);
534
535         return power;
536 }
537
538 bool eDVBFrontend::setSecSequencePos(int steps)
539 {
540         eDebug("set sequence pos %d", steps);
541         if (!steps)
542                 return false;
543         while( steps > 0 )
544         {
545                 if (m_sec_sequence.current() != m_sec_sequence.end())
546                         ++m_sec_sequence.current();
547                 --steps;
548         }
549         while( steps < 0 )
550         {
551                 if (m_sec_sequence.current() != m_sec_sequence.begin() && m_sec_sequence.current() != m_sec_sequence.end())
552                         --m_sec_sequence.current();
553                 ++steps;
554         }
555         return true;
556 }
557
558 void eDVBFrontend::tuneLoop()  // called by m_tuneTimer
559 {
560         int delay=0;
561         if ( m_sec_sequence && m_sec_sequence.current() != m_sec_sequence.end() )
562         {
563 //              eDebug("tuneLoop %d\n", m_sec_sequence.current()->cmd);
564                 switch (m_sec_sequence.current()->cmd)
565                 {
566                         case eSecCommand::SLEEP:
567                                 delay = m_sec_sequence.current()++->msec;
568                                 eDebug("[SEC] sleep %dms", delay);
569                                 break;
570                         case eSecCommand::GOTO:
571                                 if ( !setSecSequencePos(m_sec_sequence.current()->steps) )
572                                         ++m_sec_sequence.current();
573                                 break;
574                         case eSecCommand::SET_VOLTAGE:
575                         {
576                                 int voltage = m_sec_sequence.current()++->voltage;
577                                 eDebug("[SEC] setVoltage %d", voltage);
578                                 setVoltage(voltage);
579                                 break;
580                         }
581                         case eSecCommand::IF_VOLTAGE_GOTO:
582                         {
583                                 eSecCommand::pair &compare = m_sec_sequence.current()->compare;
584                                 if ( compare.voltage == m_curVoltage && setSecSequencePos(compare.steps) )
585                                         break;
586                                 ++m_sec_sequence.current();
587                                 break;
588                         }
589                         case eSecCommand::IF_NOT_VOLTAGE_GOTO:
590                         {
591                                 eSecCommand::pair &compare = m_sec_sequence.current()->compare;
592                                 if ( compare.voltage != m_curVoltage && setSecSequencePos(compare.steps) )
593                                         break;
594                                 ++m_sec_sequence.current();
595                                 break;
596                         }
597                         case eSecCommand::SET_TONE:
598                                 eDebug("[SEC] setTone %d", m_sec_sequence.current()->tone);
599                                 setTone(m_sec_sequence.current()++->tone);
600                                 break;
601                         case eSecCommand::SEND_DISEQC:
602                                 sendDiseqc(m_sec_sequence.current()->diseqc);
603                                 eDebugNoNewLine("[SEC] sendDiseqc: ");
604                                 for (int i=0; i < m_sec_sequence.current()->diseqc.len; ++i)
605                                     eDebugNoNewLine("%02x", m_sec_sequence.current()->diseqc.data[i]);
606                                 eDebug("");
607                                 ++m_sec_sequence.current();
608                                 break;
609                         case eSecCommand::SEND_TONEBURST:
610                                 eDebug("[SEC] sendToneburst: %d", m_sec_sequence.current()->toneburst);
611                                 sendToneburst(m_sec_sequence.current()++->toneburst);
612                                 break;
613                         case eSecCommand::SET_FRONTEND:
614                                 eDebug("[SEC] setFrontend");
615                                 setFrontend();
616                                 ++m_sec_sequence.current();
617                                 break;
618                         case eSecCommand::START_TUNE_TIMEOUT:
619                                 m_timeout->start(5000, 1); // 5 sec timeout. TODO: symbolrate dependent
620                                 ++m_sec_sequence.current();
621                                 break;
622                         case eSecCommand::SET_TIMEOUT:
623                                 m_timeoutCount = m_sec_sequence.current()++->val;
624                                 eDebug("[SEC] set timeout %d", m_timeoutCount);
625                                 break;
626                         case eSecCommand::IF_TIMEOUT_GOTO:
627                                 if (!m_timeoutCount)
628                                 {
629                                         eDebug("[SEC] rotor timout");
630                                         m_sec->setRotorMoving(false);
631                                         setSecSequencePos(m_sec_sequence.current()->steps);
632                                 }
633                                 else
634                                         ++m_sec_sequence.current();
635                                 break;
636                         case eSecCommand::MEASURE_IDLE_INPUTPOWER:
637                         {
638                                 int idx = m_sec_sequence.current()++->val;
639                                 if ( idx == 0 || idx == 1 )
640                                 {
641                                         m_idleInputpower[idx] = readInputpower();
642                                         eDebug("[SEC] idleInputpower[%d] is %d", idx, m_idleInputpower[idx]);
643                                 }
644                                 else
645                                         eDebug("[SEC] idleInputpower measure index(%d) out of bound !!!", idx);
646                                 break;
647                         }
648                         case eSecCommand::IF_MEASURE_IDLE_WAS_NOT_OK_GOTO:
649                         {
650                                 eSecCommand::pair &compare = m_sec_sequence.current()->compare;
651                                 int idx = compare.voltage;
652                                 if ( idx == 0 || idx == 1 )
653                                 {
654                                         int idle = readInputpower();
655                                         int diff = abs(idle-m_idleInputpower[idx]);
656                                         if ( diff > 0)
657                                         {
658                                                 eDebug("measure idle(%d) was not okay.. (%d - %d = %d) retry", idx, m_idleInputpower[idx], idle, diff);
659                                                 setSecSequencePos(compare.steps);
660                                                 break;
661                                         }
662                                 }
663                                 ++m_sec_sequence.current();
664                                 break;
665                         }
666                         case eSecCommand::IF_TUNER_LOCKED_GOTO:
667                         {
668                                 eSecCommand::rotor &cmd = m_sec_sequence.current()->measure;
669                                 if (readFrontendData(Locked))
670                                 {
671                                         eDebug("[SEC] locked step %d ok", cmd.okcount);
672                                         ++cmd.okcount;
673                                         if (cmd.okcount > 12)
674                                         {
675                                                 eDebug("ok > 12 .. goto %d\n",m_sec_sequence.current()->steps);
676                                                 setSecSequencePos(cmd.steps);
677                                                 break;
678                                         }
679                                 }
680                                 else
681                                 {
682                                         eDebug("[SEC] rotor locked step %d failed", cmd.okcount);
683                                         --m_timeoutCount;
684                                         if (!m_timeoutCount && m_retryCount > 0)
685                                                 --m_retryCount;
686                                         cmd.okcount=0;
687                                 }
688                                 ++m_sec_sequence.current();
689                                 break;
690                         }
691                         case eSecCommand::MEASURE_RUNNING_INPUTPOWER:
692                                 m_runningInputpower = readInputpower();
693                                 eDebug("[SEC] runningInputpower is %d", m_runningInputpower);
694                                 ++m_sec_sequence.current();
695                                 break;
696                         case eSecCommand::IF_INPUTPOWER_DELTA_GOTO:
697                         {
698                                 int idleInputpower = m_idleInputpower[ (m_curVoltage&1) ? 0 : 1];
699                                 eSecCommand::rotor &cmd = m_sec_sequence.current()->measure;
700                                 const char *txt = cmd.direction ? "running" : "stopped";
701                                 eDebug("[SEC] waiting for rotor %s %d, idle %d, delta %d",
702                                         txt,
703                                         m_runningInputpower,
704                                         idleInputpower,
705                                         cmd.deltaA);
706                                 if ( (cmd.direction && abs(m_runningInputpower - idleInputpower) >= cmd.deltaA)
707                                         || (!cmd.direction && abs(m_runningInputpower - idleInputpower) <= cmd.deltaA) )
708                                 {
709                                         ++cmd.okcount;
710                                         eDebug("[SEC] rotor %s step %d ok", txt, cmd.okcount);
711                                         if ( cmd.okcount > 6 )
712                                         {
713                                                 m_sec->setRotorMoving(cmd.direction);
714                                                 eDebug("[SEC] rotor is %s", txt);
715                                                 if (setSecSequencePos(cmd.steps))
716                                                         break;
717                                         }
718                                 }
719                                 else
720                                 {
721                                         eDebug("[SEC] rotor not %s... reset counter.. increase timeout", txt);
722                                         --m_timeoutCount;
723                                         if (!m_timeoutCount && m_retryCount > 0)
724                                                 --m_retryCount;
725                                         cmd.okcount=0;
726                                 }
727                                 ++m_sec_sequence.current();
728                                 break;
729                         }
730                         case eSecCommand::IF_ROTORPOS_VALID_GOTO:
731                                 if (m_data[5] != -1 && m_data[6] != -1)
732                                         setSecSequencePos(m_sec_sequence.current()->steps);
733                                 else
734                                         ++m_sec_sequence.current();
735                                 break;
736                         case eSecCommand::INVALIDATE_CURRENT_ROTORPARMS:
737                                 m_data[5] = m_data[6] = -1;
738                                 eDebug("[SEC] invalidate current rotorparams");
739                                 ++m_sec_sequence.current();
740                                 break;
741                         case eSecCommand::UPDATE_CURRENT_ROTORPARAMS:
742                                 m_data[5] = m_data[3];
743                                 m_data[6] = m_data[4];
744                                 eDebug("[SEC] update current rotorparams %d %04x %d", m_timeoutCount, m_data[5], m_data[6]);
745                                 ++m_sec_sequence.current();
746                                 break;
747                         case eSecCommand::SET_ROTOR_DISEQC_RETRYS:
748                                 m_retryCount = m_sec_sequence.current()++->val;
749                                 eDebug("[SEC] set rotor retries %d", m_retryCount);
750                                 break;
751                         case eSecCommand::IF_NO_MORE_ROTOR_DISEQC_RETRYS_GOTO:
752                                 if (!m_retryCount)
753                                 {
754                                         eDebug("[SEC] no more rotor retrys");
755                                         setSecSequencePos(m_sec_sequence.current()->steps);
756                                 }
757                                 else
758                                         ++m_sec_sequence.current();
759                                 break;
760                         case eSecCommand::SET_POWER_LIMITING_MODE:
761                         {
762                                 int fd = m_fe ?
763                                         ::open("/dev/i2c/1", O_RDWR) :
764                                         ::open("/dev/i2c/0", O_RDWR);
765
766                                 unsigned char data[2];
767                                 ::ioctl(fd, I2C_SLAVE_FORCE, 0x10 >> 1);
768                                 if(::read(fd, data, 1) != 1)
769                                         eDebug("[SEC] error read lnbp (%m)");
770                                 if ( m_sec_sequence.current()->mode == eSecCommand::modeStatic )
771                                 {
772                                         data[0] |= 0x80;  // enable static current limiting
773                                         eDebug("[SEC] set static current limiting");
774                                 }
775                                 else
776                                 {
777                                         data[0] &= ~0x80;  // enable dynamic current limiting
778                                         eDebug("[SEC] set dynamic current limiting");
779                                 }
780                                 if(::write(fd, data, 1) != 1)
781                                         eDebug("[SEC] error write lnbp (%m)");
782                                 ::close(fd);
783                                 ++m_sec_sequence.current();
784                                 break;
785                         }
786                         default:
787                                 ++m_sec_sequence.current();
788                                 eDebug("[SEC] unhandled sec command");
789                 }
790                 m_tuneTimer->start(delay,true);
791         }
792 }
793
794 void eDVBFrontend::setFrontend()
795 {
796         eDebug("setting frontend %d", m_fe);
797         if (ioctl(m_fd, FE_SET_FRONTEND, &parm) == -1)
798         {
799                 perror("FE_SET_FRONTEND failed");
800                 return;
801         }
802 }
803
804 RESULT eDVBFrontend::getFrontendType(int &t)
805 {
806         if (m_type == -1)
807                 return -ENODEV;
808         t = m_type;
809         return 0;
810 }
811
812 RESULT eDVBFrontend::prepare_sat(const eDVBFrontendParametersSatellite &feparm)
813 {
814         int res;
815         if (!m_sec)
816         {
817                 eWarning("no SEC module active!");
818                 return -ENOENT;
819         }
820         res = m_sec->prepare(*this, parm, feparm, 1 << m_fe);
821         if (!res)
822                 eDebug("tuning to %d mhz", parm.frequency/1000);
823         return res;
824 }
825
826 RESULT eDVBFrontend::prepare_cable(const eDVBFrontendParametersCable &feparm)
827 {
828         parm.frequency = feparm.frequency * 1000;
829         parm.u.qam.symbol_rate = feparm.symbol_rate;
830         switch (feparm.modulation)
831         {
832         case eDVBFrontendParametersCable::Modulation::QAM16:
833                 parm.u.qam.modulation = QAM_16;
834                 break;
835         case eDVBFrontendParametersCable::Modulation::QAM32:
836                 parm.u.qam.modulation = QAM_32;
837                 break;
838         case eDVBFrontendParametersCable::Modulation::QAM64:
839                 parm.u.qam.modulation = QAM_64;
840                 break;
841         case eDVBFrontendParametersCable::Modulation::QAM128:
842                 parm.u.qam.modulation = QAM_128;
843                 break;
844         case eDVBFrontendParametersCable::Modulation::QAM256:
845                 parm.u.qam.modulation = QAM_256;
846                 break;
847         default:
848         case eDVBFrontendParametersCable::Modulation::Auto:
849                 parm.u.qam.modulation = QAM_AUTO;
850                 break;
851         }
852         switch (feparm.inversion)
853         {
854         case eDVBFrontendParametersCable::Inversion::On:
855                 parm.inversion = INVERSION_ON;
856                 break;
857         case eDVBFrontendParametersCable::Inversion::Off:
858                 parm.inversion = INVERSION_OFF;
859                 break;
860         default:
861         case eDVBFrontendParametersCable::Inversion::Unknown:
862                 parm.inversion = INVERSION_AUTO;
863                 break;
864         }
865         switch (feparm.fec_inner)
866         {
867         case eDVBFrontendParametersCable::FEC::fNone:
868                 parm.u.qam.fec_inner = FEC_NONE;
869                 break;
870         case eDVBFrontendParametersCable::FEC::f1_2:
871                 parm.u.qam.fec_inner = FEC_1_2;
872                 break;
873         case eDVBFrontendParametersCable::FEC::f2_3:
874                 parm.u.qam.fec_inner = FEC_2_3;
875                 break;
876         case eDVBFrontendParametersCable::FEC::f3_4:
877                 parm.u.qam.fec_inner = FEC_3_4;
878                 break;
879         case eDVBFrontendParametersCable::FEC::f5_6:
880                 parm.u.qam.fec_inner = FEC_5_6;
881                 break;
882         case eDVBFrontendParametersCable::FEC::f7_8:
883                 parm.u.qam.fec_inner = FEC_7_8;
884                 break;
885         case eDVBFrontendParametersCable::FEC::f8_9:
886                 parm.u.qam.fec_inner = FEC_8_9;
887                 break;
888         default:
889         case eDVBFrontendParametersCable::FEC::fAuto:
890                 parm.u.qam.fec_inner = FEC_AUTO;
891                 break;
892         }
893         return 0;
894 }
895
896 RESULT eDVBFrontend::prepare_terrestrial(const eDVBFrontendParametersTerrestrial &feparm)
897 {
898         parm.frequency = feparm.frequency;
899
900         switch (feparm.bandwidth)
901         {
902         case eDVBFrontendParametersTerrestrial::Bandwidth::Bw8MHz:
903                 parm.u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
904                 break;
905         case eDVBFrontendParametersTerrestrial::Bandwidth::Bw7MHz:
906                 parm.u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
907                 break;
908         case eDVBFrontendParametersTerrestrial::Bandwidth::Bw6MHz:
909                 parm.u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
910                 break;
911         default:
912         case eDVBFrontendParametersTerrestrial::Bandwidth::BwAuto:
913                 parm.u.ofdm.bandwidth = BANDWIDTH_AUTO;
914                 break;
915         }
916         switch (feparm.code_rate_LP)
917         {
918         case eDVBFrontendParametersCable::FEC::f1_2:
919                 parm.u.ofdm.code_rate_LP = FEC_1_2;
920                 break;
921         case eDVBFrontendParametersCable::FEC::f2_3:
922                 parm.u.ofdm.code_rate_LP = FEC_2_3;
923                 break;
924         case eDVBFrontendParametersCable::FEC::f3_4:
925                 parm.u.ofdm.code_rate_LP = FEC_3_4;
926                 break;
927         case eDVBFrontendParametersCable::FEC::f5_6:
928                 parm.u.ofdm.code_rate_LP = FEC_5_6;
929                 break;
930         case eDVBFrontendParametersCable::FEC::f7_8:
931                 parm.u.ofdm.code_rate_LP = FEC_7_8;
932                 break;
933         default:
934         case eDVBFrontendParametersCable::FEC::fAuto:
935         case eDVBFrontendParametersCable::FEC::fNone:
936                 parm.u.ofdm.code_rate_LP = FEC_AUTO;
937                 break;
938         }
939         switch (feparm.code_rate_HP)
940         {
941         case eDVBFrontendParametersCable::FEC::f1_2:
942                 parm.u.ofdm.code_rate_HP = FEC_1_2;
943                 break;
944         case eDVBFrontendParametersCable::FEC::f2_3:
945                 parm.u.ofdm.code_rate_HP = FEC_2_3;
946                 break;
947         case eDVBFrontendParametersCable::FEC::f3_4:
948                 parm.u.ofdm.code_rate_HP = FEC_3_4;
949                 break;
950         case eDVBFrontendParametersCable::FEC::f5_6:
951                 parm.u.ofdm.code_rate_HP = FEC_5_6;
952                 break;
953         case eDVBFrontendParametersCable::FEC::f7_8:
954                 parm.u.ofdm.code_rate_HP = FEC_7_8;
955                 break;
956         default:
957         case eDVBFrontendParametersCable::FEC::fAuto:
958         case eDVBFrontendParametersCable::FEC::fNone:
959                 parm.u.ofdm.code_rate_HP = FEC_AUTO;
960                 break;
961         }
962         switch (feparm.modulation)
963         {
964         case eDVBFrontendParametersTerrestrial::Modulation::QPSK:
965                 parm.u.ofdm.constellation = QPSK;
966                 break;
967         case eDVBFrontendParametersTerrestrial::Modulation::QAM16:
968                 parm.u.ofdm.constellation = QAM_16;
969                 break;
970         default:
971         case eDVBFrontendParametersTerrestrial::Modulation::Auto:
972                 parm.u.ofdm.constellation = QAM_AUTO;
973                 break;
974         }
975         switch (feparm.transmission_mode)
976         {
977         case eDVBFrontendParametersTerrestrial::TransmissionMode::TM2k:
978                 parm.u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
979                 break;
980         case eDVBFrontendParametersTerrestrial::TransmissionMode::TM8k:
981                 parm.u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
982                 break;
983         default:
984         case eDVBFrontendParametersTerrestrial::TransmissionMode::TMAuto:
985                 parm.u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
986                 break;
987         }
988         switch (feparm.guard_interval)
989         {
990                 case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_32:
991                         parm.u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
992                         break;
993                 case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_16:
994                         parm.u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
995                         break;
996                 case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_8:
997                         parm.u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
998                         break;
999                 case eDVBFrontendParametersTerrestrial::GuardInterval::GI_1_4:
1000                         parm.u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
1001                         break;
1002                 default:
1003                 case eDVBFrontendParametersTerrestrial::GuardInterval::GI_Auto:
1004                         parm.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
1005                         break;
1006         }
1007         switch (feparm.hierarchy)
1008         {
1009                 case eDVBFrontendParametersTerrestrial::Hierarchy::H1:
1010                         parm.u.ofdm.hierarchy_information = HIERARCHY_1;
1011                         break;
1012                 case eDVBFrontendParametersTerrestrial::Hierarchy::H2:
1013                         parm.u.ofdm.hierarchy_information = HIERARCHY_2;
1014                         break;
1015                 case eDVBFrontendParametersTerrestrial::Hierarchy::H4:
1016                         parm.u.ofdm.hierarchy_information = HIERARCHY_4;
1017                         break;
1018                 default:
1019                 case eDVBFrontendParametersTerrestrial::Hierarchy::HAuto:
1020                         parm.u.ofdm.hierarchy_information = HIERARCHY_AUTO;
1021                         break;
1022         }
1023         return 0;
1024 }
1025
1026 RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
1027 {
1028         eDebug("(%d)tune", m_fe);
1029
1030         int res=0;
1031
1032         if (m_type == -1)
1033                 return -ENODEV;
1034
1035         feEvent(-1);
1036
1037         m_sec_sequence.clear();
1038
1039         switch (m_type)
1040         {
1041         case feSatellite:
1042         {
1043                 eDVBFrontendParametersSatellite feparm;
1044                 if (where.getDVBS(feparm))
1045                 {
1046                         eDebug("no dvbs data!");
1047                         return -EINVAL;
1048                 }
1049                 res=prepare_sat(feparm);
1050                 break;
1051         }
1052         case feCable:
1053         {
1054                 eDVBFrontendParametersCable feparm;
1055                 if (where.getDVBC(feparm))
1056                         return -EINVAL;
1057                 res=prepare_cable(feparm);
1058                 if (!res)
1059                         m_sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND) );
1060                 break;
1061         }
1062         case feTerrestrial:
1063         {
1064                 eDVBFrontendParametersTerrestrial feparm;
1065                 if (where.getDVBT(feparm))
1066                 {
1067                         eDebug("no -T data");
1068                         return -EINVAL;
1069                 }
1070                 res=prepare_terrestrial(feparm);
1071                 if (!res)
1072                         m_sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND) );
1073                 break;
1074         }
1075         }
1076
1077         if (!res)  // prepare ok
1078         {
1079                 m_tuneTimer->start(0,true);
1080                 m_timeout->stop();
1081                 m_sec_sequence.current() = m_sec_sequence.begin();
1082
1083                 if (m_state != stateTuning)
1084                 {
1085                         m_tuning = 1;
1086                         m_state = stateTuning;
1087                         m_stateChanged(this);
1088                 }
1089         }
1090
1091         return res;
1092 }
1093
1094 RESULT eDVBFrontend::connectStateChange(const Slot1<void,iDVBFrontend*> &stateChange, ePtr<eConnection> &connection)
1095 {
1096         connection = new eConnection(this, m_stateChanged.connect(stateChange));
1097         return 0;
1098 }
1099
1100 RESULT eDVBFrontend::setVoltage(int voltage)
1101 {
1102         if (m_type != feSatellite)
1103                 return -1;
1104 #if HAVE_DVB_API_VERSION < 3
1105         secVoltage vlt;
1106 #else
1107         bool increased=false;
1108         fe_sec_voltage_t vlt;
1109 #endif
1110         m_curVoltage=voltage;
1111         switch (voltage)
1112         {
1113         case voltageOff:
1114                 for (int i=0; i < 3; ++i)  // reset diseqc
1115                         m_data[i]=-1;
1116                 vlt = SEC_VOLTAGE_OFF;
1117                 break;
1118         case voltage13_5:
1119 #if HAVE_DVB_API_VERSION < 3
1120                 vlt = SEC_VOLTAGE_13_5;
1121                 break;
1122 #else
1123                 increased = true;
1124 #endif
1125         case voltage13:
1126                 vlt = SEC_VOLTAGE_13;
1127                 break;
1128         case voltage18_5:
1129 #if HAVE_DVB_API_VERSION < 3
1130                 vlt = SEC_VOLTAGE_18_5;
1131                 break;
1132 #else
1133                 increased = true;
1134 #endif
1135         case voltage18:
1136                 vlt = SEC_VOLTAGE_18;
1137                 break;
1138         default:
1139                 return -ENODEV;
1140         }
1141 #if HAVE_DVB_API_VERSION < 3
1142         return ::ioctl(m_secfd, SEC_SET_VOLTAGE, vlt);
1143 #else
1144         if (::ioctl(m_fd, FE_ENABLE_HIGH_LNB_VOLTAGE, increased) < 0)
1145                 perror("FE_ENABLE_HIGH_LNB_VOLTAGE");
1146         return ::ioctl(m_fd, FE_SET_VOLTAGE, vlt);
1147 #endif
1148 }
1149
1150 RESULT eDVBFrontend::getState(int &state)
1151 {
1152         state = m_state;
1153         return 0;
1154 }
1155
1156 RESULT eDVBFrontend::setTone(int t)
1157 {
1158         if (m_type != feSatellite)
1159                 return -1;
1160 #if HAVE_DVB_API_VERSION < 3
1161         secToneMode_t tone;
1162 #else
1163         fe_sec_tone_mode_t tone;
1164 #endif
1165
1166         switch (t)
1167         {
1168         case toneOn:
1169                 tone = SEC_TONE_ON;
1170                 break;
1171         case toneOff:
1172                 tone = SEC_TONE_OFF;
1173                 break;
1174         default:
1175                 return -ENODEV;
1176         }
1177 #if HAVE_DVB_API_VERSION < 3    
1178         return ::ioctl(m_secfd, SEC_SET_TONE, tone);
1179 #else   
1180         return ::ioctl(m_fd, FE_SET_TONE, tone);
1181 #endif
1182 }
1183
1184 #if HAVE_DVB_API_VERSION < 3 && !defined(SEC_DISEQC_SEND_MASTER_CMD)
1185         #define SEC_DISEQC_SEND_MASTER_CMD _IOW('o', 97, struct secCommand *)
1186 #endif
1187
1188 RESULT eDVBFrontend::sendDiseqc(const eDVBDiseqcCommand &diseqc)
1189 {
1190 #if HAVE_DVB_API_VERSION < 3
1191         struct secCommand cmd;
1192         cmd.type = SEC_CMDTYPE_DISEQC_RAW;
1193         cmd.u.diseqc.cmdtype = diseqc.data[0];
1194         cmd.u.diseqc.addr = diseqc.data[1];
1195         cmd.u.diseqc.cmd = diseqc.data[2];
1196         cmd.u.diseqc.numParams = diseqc.len-3;
1197         memcpy(cmd.u.diseqc.params, diseqc.data+3, diseqc.len-3);
1198         if (::ioctl(m_secfd, SEC_DISEQC_SEND_MASTER_CMD, &cmd))
1199 #else
1200         struct dvb_diseqc_master_cmd cmd;
1201         memcpy(cmd.msg, diseqc.data, diseqc.len);
1202         cmd.msg_len = diseqc.len;
1203         if (::ioctl(m_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd))
1204 #endif
1205                 return -EINVAL;
1206         return 0;
1207 }
1208
1209 #if HAVE_DVB_API_VERSION < 3 && !defined(SEC_DISEQC_SEND_BURST)
1210         #define SEC_DISEQC_SEND_BURST _IO('o', 96)
1211 #endif
1212 RESULT eDVBFrontend::sendToneburst(int burst)
1213 {
1214 #if HAVE_DVB_API_VERSION < 3
1215         secMiniCmd cmd = SEC_MINI_NONE;
1216 #else
1217         fe_sec_mini_cmd_t cmd = SEC_MINI_A;
1218 #endif
1219         if ( burst == eDVBSatelliteDiseqcParameters::A )
1220                 cmd = SEC_MINI_A;
1221         else if ( burst == eDVBSatelliteDiseqcParameters::B )
1222                 cmd = SEC_MINI_B;
1223 #if HAVE_DVB_API_VERSION < 3
1224         if (::ioctl(m_secfd, SEC_DISEQC_SEND_BURST, cmd))
1225                 return -EINVAL;
1226 #else
1227         if (::ioctl(m_fd, FE_DISEQC_SEND_BURST, cmd))
1228                 return -EINVAL;
1229 #endif
1230         return 0;
1231 }
1232
1233 RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec)
1234 {
1235         m_sec = sec;
1236         return 0;
1237 }
1238
1239 RESULT eDVBFrontend::setSecSequence(const eSecCommandList &list)
1240 {
1241         m_sec_sequence = list;
1242         return 0;
1243 }
1244
1245 RESULT eDVBFrontend::getData(int num, int &data)
1246 {
1247         if ( num < (int)(sizeof(m_data)/sizeof(int)) )
1248         {
1249                 data = m_data[num];
1250                 return 0;
1251         }
1252         return -EINVAL;
1253 }
1254
1255 RESULT eDVBFrontend::setData(int num, int val)
1256 {
1257         if ( num < (int)(sizeof(m_data)/sizeof(int)) )
1258         {
1259                 m_data[num] = val;
1260                 return 0;
1261         }
1262         return -EINVAL;
1263 }
1264
1265 int eDVBFrontend::isCompatibleWith(ePtr<iDVBFrontendParameters> &feparm)
1266 {
1267         int type;
1268         if (feparm->getSystem(type) || type != m_type)
1269                 return 0;
1270
1271         if (m_type == eDVBFrontend::feSatellite)
1272         {
1273                 ASSERT(m_sec);
1274                 eDVBFrontendParametersSatellite sat_parm;
1275                 ASSERT(!feparm->getDVBS(sat_parm));
1276                 return m_sec->canTune(sat_parm, this, 1 << m_fe);
1277         }
1278         return 1;
1279 }