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