6e8bcaa7fd1b5afc58ce1a1a59b741efe1042c16
[enigma2.git] / lib / dvb / frontend.cpp
1 #include <config.h>
2 #include <lib/dvb/dvb.h>
3 #include <lib/base/eerror.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8
9 #if HAVE_DVB_API_VERSION < 3
10 #include <ost/frontend.h>
11 #include <ost/sec.h>
12 #define QAM_AUTO                                (Modulation)6
13 #define TRANSMISSION_MODE_AUTO  (TransmitMode)2
14 #define BANDWIDTH_AUTO                  (BandWidth)3
15 #define GUARD_INTERVAL_AUTO             (GuardInterval)4
16 #define HIERARCHY_AUTO                  (Hierarchy)4
17 #define constellation Constellation
18 #define guard_interval guardInterval
19 #define hierarchy_information HierarchyInformation
20 #define code_rate_HP HP_CodeRate
21 #define code_rate_LP LP_CodeRate
22 #else
23 #include <linux/dvb/frontend.h>
24 #endif
25
26 #include <lib/dvb_si/satellite_delivery_system_descriptor.h>
27 #include <lib/dvb_si/cable_delivery_system_descriptor.h>
28 #include <lib/dvb_si/terrestrial_delivery_system_descriptor.h>
29
30 void eDVBFrontendParametersSatellite::set(const SatelliteDeliverySystemDescriptor &descriptor)
31 {
32         frequency    = descriptor.getFrequency() * 10;
33         symbol_rate  = descriptor.getSymbolRate() * 100;
34         switch (descriptor.getPolarization())
35         {
36         case 0:
37                 polarisation = Polarisation::Horizontal;
38                 break;
39         case 1:
40                 polarisation = Polarisation::Vertical;
41                 break;
42         case 2:
43                 polarisation = Polarisation::CircularLeft;
44                 break;
45         case 3:
46                 polarisation = Polarisation::CircularRight;
47                 break;
48         }
49         switch (descriptor.getFecInner())
50         {
51         case 1:
52                 fec = FEC::f1_2;
53                 break;
54         case 2:
55                 fec = FEC::f2_3;
56                 break;
57         case 3:
58                 fec = FEC::f3_4;
59                 break;
60         case 4:
61                 fec = FEC::f5_6;
62                 break;
63         case 5:
64                 fec = FEC::f7_8;
65                 break;
66         case 0xF:
67                 fec = FEC::fNone;
68                 break;
69         default:
70                 fec = FEC::fAuto;
71                 break;
72         }
73         inversion = Inversion::Unknown;
74         orbital_position  = ((descriptor.getOrbitalPosition() >> 12) & 0xF) * 1000;
75         orbital_position += ((descriptor.getOrbitalPosition() >> 8) & 0xF) * 100;
76         orbital_position += ((descriptor.getOrbitalPosition() >> 4) & 0xF) * 10;
77         orbital_position += ((descriptor.getOrbitalPosition()) & 0xF);
78         if (orbital_position && (!descriptor.getWestEastFlag()))
79                 orbital_position = 3600 - orbital_position;
80 }
81
82 void eDVBFrontendParametersCable::set(const CableDeliverySystemDescriptor &descriptor)
83 {
84         eFatal("nyi");
85 }
86
87 void eDVBFrontendParametersTerrestrial::set(const TerrestrialDeliverySystemDescriptor  &)
88 {
89         eFatal("nyi");
90 }
91
92 eDVBFrontendParameters::eDVBFrontendParameters(): m_type(-1)
93 {
94 }
95
96 DEFINE_REF(eDVBFrontendParameters);
97
98 RESULT eDVBFrontendParameters::getSystem(int &t) const
99 {
100         if (m_type == -1)
101                 return -1;
102         t = m_type;
103         return 0;
104 }
105
106 RESULT eDVBFrontendParameters::getDVBS(eDVBFrontendParametersSatellite &p) const
107 {
108         if (m_type != iDVBFrontend::feSatellite)
109                 return -1;
110         p = sat;
111         return 0;
112 }
113
114 RESULT eDVBFrontendParameters::getDVBC(eDVBFrontendParametersCable &p) const
115 {
116         if (m_type != iDVBFrontend::feCable)
117                 return -1;
118         p = cable;
119         return 0;
120 }
121
122 RESULT eDVBFrontendParameters::getDVBT(eDVBFrontendParametersTerrestrial &p) const
123 {
124         if (m_type != iDVBFrontend::feTerrestrial)
125                 return -1;
126         p = terrestrial;
127         return 0;
128 }
129
130 RESULT eDVBFrontendParameters::setDVBS(eDVBFrontendParametersSatellite &p)
131 {
132         sat = p;
133         m_type = iDVBFrontend::feSatellite;
134         return 0;
135 }
136
137 RESULT eDVBFrontendParameters::setDVBC(eDVBFrontendParametersCable &p)
138 {
139         cable = p;
140         m_type = iDVBFrontend::feCable;
141         return 0;
142 }
143
144 RESULT eDVBFrontendParameters::setDVBT(eDVBFrontendParametersTerrestrial &p)
145 {
146         terrestrial = p;
147         m_type = iDVBFrontend::feTerrestrial;
148         return 0;
149 }
150
151 RESULT eDVBFrontendParameters::calculateDifference(const iDVBFrontendParameters *parm, int &diff) const
152 {
153         if (!parm)
154                 return -1;
155         int type;
156         if (parm->getSystem(type))
157                 return -1;
158         if (type != m_type)
159         {
160                 diff = 1<<30; // big difference
161                 return 0;
162         }
163         
164         switch (type)
165         {
166         case iDVBFrontend::feSatellite:
167         {
168                 eDVBFrontendParametersSatellite osat;
169                 if (parm->getDVBS(osat))
170                         return -2;
171                 
172                 if (sat.orbital_position != osat.orbital_position)
173                         diff = 1<<29;
174                 else if (sat.polarisation != osat.polarisation)
175                         diff = 1<<28;
176                 else 
177                         diff = abs(sat.frequency - osat.frequency);
178                 return 0;
179         }
180         case iDVBFrontend::feCable:
181         case iDVBFrontend::feTerrestrial:
182         default:
183                 return -1;
184         }
185         return 0;
186 }
187
188 RESULT eDVBFrontendParameters::getHash(unsigned long &hash) const 
189 {
190         switch (m_type)
191         {
192         case iDVBFrontend::feSatellite:
193         {
194                 hash  = sat.frequency & 0xFFFF;
195                 hash |= sat.orbital_position << 16;
196                 return 0;
197         }
198         case iDVBFrontend::feCable:
199         case iDVBFrontend::feTerrestrial:
200         default:
201                 return -1;
202         }
203 }
204
205 DEFINE_REF(eDVBFrontend);
206
207 eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): m_type(-1)
208 {
209 #if HAVE_DVB_API_VERSION < 3
210         char sec_filename[128];
211 #endif
212         char filename[128];
213
214         int result;
215
216         m_sn = 0;
217         m_timeout = 0;
218
219 #if HAVE_DVB_API_VERSION < 3
220         sprintf(sec_filename, "/dev/dvb/card%d/sec%d", adap, fe);
221         m_secfd = ::open(sec_filename, O_RDWR);
222         if (m_secfd < 0)
223         {
224                 eWarning("failed! (%s) %m", sec_filename);
225                 ok = 0;
226                 return;
227         }
228         else
229                 eDebug("m_secfd is %d", m_secfd);
230
231         FrontendInfo fe_info;
232         sprintf(filename, "/dev/dvb/card%d/frontend%d", adap, fe);
233 #else
234         dvb_frontend_info fe_info;      
235         sprintf(filename, "/dev/dvb/adapter%d/frontend%d", adap, fe);
236 #endif
237         eDebug("opening frontend.");
238         m_fd = ::open(filename, O_RDWR|O_NONBLOCK);
239         if (m_fd < 0)
240         {
241                 eWarning("failed! (%s) %m", filename);
242                 ok = 0;
243                 return;
244         }
245
246         result = ::ioctl(m_fd, FE_GET_INFO, &fe_info);
247         
248         if (result < 0) {
249                 eWarning("ioctl FE_GET_INFO failed");
250                 ::close(m_fd);
251                 m_fd = -1;
252                 ok = 0;
253                 return;
254         }
255
256         switch (fe_info.type) 
257         {
258         case FE_QPSK:
259                 m_type = feSatellite;
260                 break;
261         case FE_QAM:
262                 m_type = feCable;
263                 break;
264         case FE_OFDM:
265                 m_type = feTerrestrial;
266                 break;
267         default:
268                 eWarning("unknown frontend type.");
269                 ::close(m_fd);
270                 m_fd = -1;
271                 ok = 0;
272                 return;
273         }
274         eDebug("detected %s frontend", "satellite\0cable\0    terrestrial"+fe_info.type*9);
275         ok = 1;
276
277         m_sn = new eSocketNotifier(eApp, m_fd, eSocketNotifier::Read);
278         CONNECT(m_sn->activated, eDVBFrontend::feEvent);
279         m_sn->start();
280         
281         m_timeout = new eTimer(eApp);
282         CONNECT(m_timeout->timeout, eDVBFrontend::timeout);
283         
284         return;
285 }
286
287 eDVBFrontend::~eDVBFrontend()
288 {
289         if (m_fd >= 0)
290                 ::close(m_fd);
291         if (m_sn)
292                 delete m_sn;
293         if (m_timeout)
294                 delete m_timeout;
295 }
296
297 void eDVBFrontend::feEvent(int w)
298 {
299         while (1)
300         {
301 #if HAVE_DVB_API_VERSION < 3
302                 FrontendEvent event;
303 #else
304                 dvb_frontend_event event;
305 #endif
306                 int res;
307                 int state;
308                 res = ::ioctl(m_fd, FE_GET_EVENT, &event);
309                 
310                 if (res && (errno == EAGAIN))
311                         break;
312
313                 if (res)
314                 {
315                         eWarning("FE_GET_EVENT failed! %m");
316                         return;
317                 }
318                 
319                 if (w < 0)
320                         continue;
321
322 #if HAVE_DVB_API_VERSION < 3
323                 if (event.type == FE_COMPLETION_EV)
324 #else
325                 eDebug("fe event: status %x, inversion %s", event.status, (event.parameters.inversion == INVERSION_ON) ? "on" : "off");
326                 if (event.status & FE_HAS_LOCK)
327 #endif
328                 {
329                         state = stateLock;
330                 } else
331                 {
332                         if (m_tuning)
333                                 state = stateTuning;
334                         else
335                                 state = stateFailed;
336                 }
337                 if (m_state != state)
338                 {
339                         m_state = state;
340                         m_stateChanged(this);
341                 }
342         }
343 }
344
345 void eDVBFrontend::timeout()
346 {
347         int state;
348         if (m_state == stateTuning)
349         {
350                 state = stateFailed;
351                 eDebug("DVBFrontend: timeout");
352                 if (m_state != state)
353                 {
354                         m_state = state;
355                         m_stateChanged(this);
356                 }
357         } else
358                 m_tuning = 0;
359 }
360
361 RESULT eDVBFrontend::getFrontendType(int &t)
362 {
363         if (m_type == -1)
364                 return -ENODEV;
365         t = m_type;
366         return 0;
367 }
368
369 RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
370 {
371         if (m_type == -1)
372                 return -ENODEV;
373
374         FRONTENDPARAMETERS parm;
375
376         feEvent(-1);
377         
378         eDebug("eDVBFrontend::tune. type: %d", m_type);
379         
380         switch (m_type)
381         {
382         case feSatellite:
383         {
384                 int res;
385                 eDVBFrontendParametersSatellite feparm;
386                 if (where.getDVBS(feparm))
387                 {
388                         eDebug("no dvbs data!");
389                         return -EINVAL;
390                 }
391                 if (!m_sec)
392                 {
393                         eWarning("no SEC module active!");
394                         return -ENOENT;
395                 }
396                 
397                 res = m_sec->prepare(*this, parm, feparm);
398                 if (res)
399                         return res;
400 #if HAVE_DVB_API_VERSION < 3
401                 eDebug("tuning to %d mhz", parm.Frequency/1000);
402 #else
403                 eDebug("tuning to %d mhz", parm.frequency/1000);
404 #endif
405                 break;
406         }
407         case feCable:
408         {
409                 eDVBFrontendParametersCable feparm;
410                 if (where.getDVBC(feparm))
411                         return -EINVAL;
412                 eFatal("cable tuning nyi");
413         }
414         case feTerrestrial:
415         {
416                 eDVBFrontendParametersTerrestrial feparm;
417                 if (where.getDVBT(feparm))
418                 {
419                         eDebug("no -T data");
420                         return -EINVAL;
421                 }
422 #if HAVE_DVB_API_VERSION < 3
423                 parm.Frequency = feparm.frequency;
424 #else
425                 parm.frequency = feparm.frequency;
426 #endif
427
428                 switch (feparm.bandwidth)
429                 {
430                 case eDVBFrontendParametersTerrestrial::Bandwidth::Bw8MHz:
431 #if HAVE_DVB_API_VERSION < 3
432                         parm.u.ofdm.bandWidth =
433 #else
434                         parm.u.ofdm.bandwidth =
435 #endif
436                                 BANDWIDTH_8_MHZ;
437                         break;
438                 case eDVBFrontendParametersTerrestrial::Bandwidth::Bw7MHz:
439 #if HAVE_DVB_API_VERSION < 3
440                         parm.u.ofdm.bandWidth =
441 #else
442                         parm.u.ofdm.bandwidth =
443 #endif
444                                 BANDWIDTH_7_MHZ;
445                         break;
446                 case eDVBFrontendParametersTerrestrial::Bandwidth::Bw6MHz:
447 #if HAVE_DVB_API_VERSION < 3
448                         parm.u.ofdm.bandWidth =
449 #else
450                         parm.u.ofdm.bandwidth =
451 #endif
452                                 BANDWIDTH_6_MHZ;
453                         break;
454                 case eDVBFrontendParametersTerrestrial::Bandwidth::BwAuto:
455 #if HAVE_DVB_API_VERSION < 3
456                         parm.u.ofdm.bandWidth =
457 #else
458                         parm.u.ofdm.bandwidth =
459 #endif
460                                 BANDWIDTH_AUTO;
461                         break;
462                 default:
463                         eWarning("invalid OFDM bandwith");
464                         return -EINVAL;
465                 }
466                 
467                 parm.u.ofdm.code_rate_HP = FEC_AUTO;
468                 parm.u.ofdm.code_rate_LP = FEC_AUTO;
469                 
470                 switch (feparm.modulation)
471                 {
472                 case eDVBFrontendParametersTerrestrial::Modulation::QPSK:
473                         parm.u.ofdm.constellation = QPSK;
474                         break;
475                 case eDVBFrontendParametersTerrestrial::Modulation::QAM16:
476                         parm.u.ofdm.constellation = QAM_16;
477                         break;
478                 case eDVBFrontendParametersTerrestrial::Modulation::Auto:
479                         parm.u.ofdm.constellation = QAM_AUTO;
480                         break;
481                 }
482                 
483                 switch (feparm.transmission_mode)
484                 {
485                 case eDVBFrontendParametersTerrestrial::TransmissionMode::TM2k:
486 #if HAVE_DVB_API_VERSION < 3
487                         parm.u.ofdm.TransmissionMode =
488 #else
489                         parm.u.ofdm.transmission_mode =
490 #endif
491                                 TRANSMISSION_MODE_2K;
492                         break;
493                 case eDVBFrontendParametersTerrestrial::TransmissionMode::TM8k:
494 #if HAVE_DVB_API_VERSION < 3
495                         parm.u.ofdm.TransmissionMode =
496 #else
497                         parm.u.ofdm.transmission_mode =
498 #endif
499                                 TRANSMISSION_MODE_8K;
500                         break;
501                 case eDVBFrontendParametersTerrestrial::TransmissionMode::TMAuto:
502 #if HAVE_DVB_API_VERSION < 3
503                         parm.u.ofdm.TransmissionMode =
504 #else
505                         parm.u.ofdm.transmission_mode =
506 #endif
507                                 TRANSMISSION_MODE_AUTO;
508                         break;
509                 }
510                 
511                 parm.u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
512                 parm.u.ofdm.hierarchy_information = HIERARCHY_AUTO;
513 #if HAVE_DVB_API_VERSION < 3
514                 parm.Inversion =
515 #else
516                 parm.inversion =
517 #endif
518                         INVERSION_AUTO;
519                 break;
520         }
521         }
522         
523         eDebug("setting frontend..\n");
524         
525         if (ioctl(m_fd, FE_SET_FRONTEND, &parm) == -1)
526         {
527                 perror("FE_SET_FRONTEND failed");
528                 return errno;
529         }
530         
531         if (m_state != stateTuning)
532         {
533                 m_tuning = 1;
534                 m_state = stateTuning;
535                 m_stateChanged(this);
536         }
537         
538         m_timeout->start(5000, 1); // 5 sec timeout. TODO: symbolrate dependent
539
540         return 0;
541 }
542
543 RESULT eDVBFrontend::connectStateChange(const Slot1<void,iDVBFrontend*> &stateChange, ePtr<eConnection> &connection)
544 {
545         connection = new eConnection(this, m_stateChanged.connect(stateChange));
546         return 0;
547 }
548
549 RESULT eDVBFrontend::setVoltage(int voltage)
550 {
551 #if HAVE_DVB_API_VERSION < 3
552         secVoltage vlt;
553 #else
554         fe_sec_voltage_t vlt;
555 #endif
556
557         switch (voltage)
558         {
559         case voltageOff:
560                 vlt = SEC_VOLTAGE_OFF;
561                 break;
562         case voltage13:
563                 vlt = SEC_VOLTAGE_13;
564                 break;
565         case voltage18:
566                 vlt = SEC_VOLTAGE_18;
567                 break;
568         default:
569                 return -ENODEV;
570         }
571 #if HAVE_DVB_API_VERSION < 3
572         return ::ioctl(m_secfd, SEC_SET_VOLTAGE, vlt);
573 #else
574         return ::ioctl(m_fd, FE_SET_VOLTAGE, vlt);
575 #endif
576 }
577
578 RESULT eDVBFrontend::getState(int &state)
579 {
580         state = m_state;
581         return 0;
582 }
583
584 RESULT eDVBFrontend::setTone(int t)
585 {
586 #if HAVE_DVB_API_VERSION < 3
587         secToneMode_t tone;
588 #else
589         fe_sec_tone_mode_t tone;
590 #endif
591
592         switch (t)
593         {
594         case toneOn:
595                 tone = SEC_TONE_ON;
596                 break;
597         case toneOff:
598                 tone = SEC_TONE_OFF;
599                 break;
600         default:
601                 return -ENODEV;
602         }
603 #if HAVE_DVB_API_VERSION < 3    
604         return ::ioctl(m_secfd, SEC_SET_TONE, tone);
605 #else   
606         return ::ioctl(m_fd, FE_SET_TONE, tone);
607 #endif
608 }
609
610 RESULT eDVBFrontend::sendDiseqc(const eDVBDiseqcCommand &diseqc)
611 {
612 #if HAVE_DVB_API_VERSION < 3
613         secCmdSequence seq;
614         secCommand cmd;
615
616         if ( len > 3 )
617         {
618                 seq.numCommands=1;
619                 cmd.type = SEC_CMDTYPE_DISEQC_RAW;
620                 cmd.u.diseqc.cmdtype = diseqc.data[0];
621                 eDebug("cmdtype is %02x", diseqc.data[0]);
622                 cmd.u.diseqc.addr = diseqc.data[1];
623                 eDebug("cmdaddr is %02x", diseqc.data[1]);
624                 cmd.u.diseqc.cmd = diseqc.data[2];
625                 eDebug("cmd is %02x", diseqc.data[2]);
626                 cmd.u.diseqc.numParams = diseqc.len-3;
627                 eDebug("numparams %d", diseqc.len-3);
628
629                 memcpy(cmd.u.diseqc.params, diseqc.data+3, diseqc.len-3);
630                 for (int i=0; i < diseqc.len-3; ++i )
631                         eDebugNoNewLine("%02x ", diseqc.data[3+i]);
632                 eDebug("");
633         }
634         else
635                 seq.numCommands=0;
636
637         seq.continuousTone = diseqc.tone == toneOn ? SEC_TONE_ON : SEC_TONE_OFF;
638         switch ( diseqc.voltage )
639         {
640                 case voltageOff:
641                         seq.voltage = SEC_VOLTAGE_OFF;
642                         break;
643                 case voltage13:
644                         seq.voltage = SEC_VOLTAGE_13;
645                         break;
646                 case voltage18:
647                         seq.voltage = SEC_VOLTAGE_18;
648                         break;
649         }
650         seq.miniCommand = SEC_MINI_NONE;
651         seq.commands=&cmd;
652
653         if ( ioctl(m_secfd, SEC_SEND_SEQUENCE, &seq) < 0 )
654         {
655                 eDebug("SEC_SEND_SEQUENCE failed ( %m )");
656                 return -EINVAL;
657         }
658         return 0;
659 #else
660         if ( !diseqc.len )
661                 return 0;
662         struct dvb_diseqc_master_cmd cmd;
663         if (::ioctl(m_fd, FE_SET_TONE, SEC_TONE_OFF))
664                 return -EINVAL;
665         usleep(15 * 1000);
666         memcpy(cmd.msg, diseqc.data, diseqc.len);
667         cmd.msg_len = diseqc.len;
668         
669         if (::ioctl(m_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd))
670                 return -EINVAL;
671         usleep(15 * 1000);
672 #endif
673         eDebug("diseqc ok");
674         return 0;
675 }
676
677 RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec)
678 {
679         m_sec = sec;
680         return 0;
681 }