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