update python
[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"+feSatellite*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 #if HAVE_DVB_API_VERSION < 3
365         FrontendParameters parm;
366 #else
367         dvb_frontend_parameters parm;
368 #endif
369
370         feEvent(-1);
371         
372         switch (m_type)
373         {
374         case feSatellite:
375         {
376                 int res;
377                 eDVBFrontendParametersSatellite feparm;
378                 if (where.getDVBS(feparm))
379                 {
380                         eDebug("no dvbs data!");
381                         return -EINVAL;
382                 }
383                 if (!m_sec)
384                 {
385                         eWarning("no SEC module active!");
386                         return -ENOENT;
387                 }
388                 
389                 res = m_sec->prepare(*this, parm, feparm);
390                 if (res)
391                         return res;
392 #if HAVE_DVB_API_VERSION < 3
393                 eDebug("tuning to %d mhz", parm.Frequency/1000);
394 #else
395                 eDebug("tuning to %d mhz", parm.frequency/1000);
396 #endif
397                 break;
398         }
399         case feCable:
400         {
401                 eDVBFrontendParametersCable feparm;
402                 if (where.getDVBC(feparm))
403                         return -EINVAL;
404                 eFatal("cable tuning nyi");
405         }
406         case feTerrestrial:
407         {
408                 eDVBFrontendParametersTerrestrial feparm;
409                 if (where.getDVBT(feparm))
410                         return -EINVAL;
411                 eFatal("terrestrial tuning nyi");
412         }
413         }
414         
415         if (ioctl(m_fd, FE_SET_FRONTEND, &parm) == -1)
416         {
417                 perror("FE_SET_FRONTEND failed");
418                 return errno;
419         }
420         
421         if (m_state != stateTuning)
422         {
423                 m_tuning = 1;
424                 m_state = stateTuning;
425                 m_stateChanged(this);
426         }
427         
428         m_timeout->start(5000, 1); // 5 sec timeout. TODO: symbolrate dependent
429
430         return 0;
431 }
432
433 RESULT eDVBFrontend::connectStateChange(const Slot1<void,iDVBFrontend*> &stateChange, ePtr<eConnection> &connection)
434 {
435         connection = new eConnection(this, m_stateChanged.connect(stateChange));
436         return 0;
437 }
438
439 RESULT eDVBFrontend::setVoltage(int voltage)
440 {
441 #if HAVE_DVB_API_VERSION < 3
442         secVoltage vlt;
443 #else
444         fe_sec_voltage_t vlt;
445 #endif
446
447         switch (voltage)
448         {
449         case voltageOff:
450                 vlt = SEC_VOLTAGE_OFF;
451                 break;
452         case voltage13:
453                 vlt = SEC_VOLTAGE_13;
454                 break;
455         case voltage18:
456                 vlt = SEC_VOLTAGE_18;
457                 break;
458         default:
459                 return -ENODEV;
460         }
461 #if HAVE_DVB_API_VERSION < 3
462         return ::ioctl(m_secfd, SEC_SET_VOLTAGE, vlt);
463 #else
464         return ::ioctl(m_fd, FE_SET_VOLTAGE, vlt);
465 #endif
466 }
467
468 RESULT eDVBFrontend::getState(int &state)
469 {
470         state = m_state;
471         return 0;
472 }
473
474 RESULT eDVBFrontend::setTone(int t)
475 {
476 #if HAVE_DVB_API_VERSION < 3
477         secToneMode_t tone;
478 #else
479         fe_sec_tone_mode_t tone;
480 #endif
481
482         switch (t)
483         {
484         case toneOn:
485                 tone = SEC_TONE_ON;
486                 break;
487         case toneOff:
488                 tone = SEC_TONE_OFF;
489                 break;
490         default:
491                 return -ENODEV;
492         }
493 #if HAVE_DVB_API_VERSION < 3    
494         return ::ioctl(m_secfd, SEC_SET_TONE, tone);
495 #else   
496         return ::ioctl(m_fd, FE_SET_TONE, tone);
497 #endif
498 }
499
500 RESULT eDVBFrontend::sendDiseqc(const eDVBDiseqcCommand &diseqc)
501 {
502 #if HAVE_DVB_API_VERSION < 3
503         secCmdSequence seq;
504         secCommand cmd;
505
506         cmd.type = SEC_CMDTYPE_DISEQC_RAW;
507         cmd.u.diseqc.cmdtype = diseqc.data[0];
508         eDebug("cmdtype is %02x", diseqc.data[0]);
509         cmd.u.diseqc.addr = diseqc.data[1];
510         eDebug("cmdaddr is %02x", diseqc.data[1]);
511         cmd.u.diseqc.cmd = diseqc.data[2];
512         eDebug("cmd is %02x", diseqc.data[2]);
513         cmd.u.diseqc.numParams = diseqc.len-3;
514         eDebug("numparams %d", diseqc.len-3);
515
516         memcpy(cmd.u.diseqc.params, diseqc.data+3, diseqc.len-3);
517         for (int i=0; i < diseqc.len-3; ++i )
518                 eDebugNoNewLine("%02x ", diseqc.data[3+i]);
519         eDebug("");
520
521         seq.continuousTone = SEC_TONE_OFF;
522         seq.voltage = SEC_VOLTAGE_13;
523         seq.miniCommand = SEC_MINI_NONE;
524         seq.commands=&cmd;
525         seq.numCommands=1;
526
527
528         if ( ioctl(m_secfd, SEC_SEND_SEQUENCE, &seq) < 0 )
529         {
530                 eDebug("SEC_SEND_SEQUENCE failed ( %m )");
531                 return -EINVAL;
532         }
533         return 0;
534 #else
535         struct dvb_diseqc_master_cmd cmd;
536         if (::ioctl(m_fd, FE_SET_TONE, SEC_TONE_OFF))
537                 return -EINVAL;
538         usleep(15 * 1000);
539         memcpy(cmd.msg, diseqc.data, diseqc.len);
540         cmd.msg_len = diseqc.len;
541         
542         if (::ioctl(m_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd))
543                 return -EINVAL;
544         usleep(15 * 1000);
545 #endif
546         eDebug("diseqc ok");
547         return 0;
548 }
549
550 RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec)
551 {
552         m_sec = sec;
553         return 0;
554 }