c51eeea7193054cc9dd8bc8de2054d85462d0ad1
[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 #include <linux/dvb/frontend.h>
9
10 #include <lib/dvb_si/satellite_delivery_system_descriptor.h>
11 #include <lib/dvb_si/cable_delivery_system_descriptor.h>
12 #include <lib/dvb_si/terrestrial_delivery_system_descriptor.h>
13
14 void eDVBFrontendParametersSatellite::set(const SatelliteDeliverySystemDescriptor &descriptor)
15 {
16         frequency    = descriptor.getFrequency() * 10;
17         symbol_rate  = descriptor.getSymbolRate() * 100;
18         switch (descriptor.getPolarization())
19         {
20         case 0:
21                 polarisation = Polarisation::Horizontal;
22                 break;
23         case 1:
24                 polarisation = Polarisation::Vertical;
25                 break;
26         case 2:
27                 polarisation = Polarisation::CircularLeft;
28                 break;
29         case 3:
30                 polarisation = Polarisation::CircularRight;
31                 break;
32         }
33         switch (descriptor.getFecInner())
34         {
35         case 1:
36                 fec = FEC::f1_2;
37                 break;
38         case 2:
39                 fec = FEC::f2_3;
40                 break;
41         case 3:
42                 fec = FEC::f3_4;
43                 break;
44         case 4:
45                 fec = FEC::f5_6;
46                 break;
47         case 5:
48                 fec = FEC::f7_8;
49                 break;
50         case 0xF:
51                 fec = FEC::fNone;
52                 break;
53         default:
54                 fec = FEC::fAuto;
55                 break;
56         }
57         inversion = Inversion::Unknown;
58         orbital_position  = ((descriptor.getOrbitalPosition() >> 12) & 0xF) * 1000;
59         orbital_position += ((descriptor.getOrbitalPosition() >> 8) & 0xF) * 100;
60         orbital_position += ((descriptor.getOrbitalPosition() >> 4) & 0xF) * 10;
61         orbital_position += ((descriptor.getOrbitalPosition()) & 0xF);
62         if (orbital_position && (!descriptor.getWestEastFlag()))
63                 orbital_position = 3600 - orbital_position;
64 }
65
66 void eDVBFrontendParametersCable::set(const CableDeliverySystemDescriptor &descriptor)
67 {
68         eFatal("nyi");
69 }
70
71 void eDVBFrontendParametersTerrestrial::set(const TerrestrialDeliverySystemDescriptor  &)
72 {
73         eFatal("nyi");
74 }
75
76 eDVBFrontendParameters::eDVBFrontendParameters(): ref(0), m_type(-1)
77 {
78 }
79
80 DEFINE_REF(eDVBFrontendParameters);
81
82 RESULT eDVBFrontendParameters::getSystem(int &t) const
83 {
84         if (m_type == -1)
85                 return -1;
86         t = m_type;
87         return 0;
88 }
89
90 RESULT eDVBFrontendParameters::getDVBS(eDVBFrontendParametersSatellite &p) const
91 {
92         if (m_type != iDVBFrontend::feSatellite)
93                 return -1;
94         p = sat;
95         return 0;
96 }
97
98 RESULT eDVBFrontendParameters::getDVBC(eDVBFrontendParametersCable &p) const
99 {
100         if (m_type != iDVBFrontend::feCable)
101                 return -1;
102         p = cable;
103         return 0;
104 }
105
106 RESULT eDVBFrontendParameters::getDVBT(eDVBFrontendParametersTerrestrial &p) const
107 {
108         if (m_type != iDVBFrontend::feTerrestrial)
109                 return -1;
110         p = terrestrial;
111         return 0;
112 }
113
114 RESULT eDVBFrontendParameters::setDVBS(eDVBFrontendParametersSatellite &p)
115 {
116         sat = p;
117         m_type = iDVBFrontend::feSatellite;
118         return 0;
119 }
120
121 RESULT eDVBFrontendParameters::setDVBC(eDVBFrontendParametersCable &p)
122 {
123         cable = p;
124         m_type = iDVBFrontend::feCable;
125         return 0;
126 }
127
128 RESULT eDVBFrontendParameters::setDVBT(eDVBFrontendParametersTerrestrial &p)
129 {
130         terrestrial = p;
131         m_type = iDVBFrontend::feTerrestrial;
132         return 0;
133 }
134
135 RESULT eDVBFrontendParameters::calculateDifference(const iDVBFrontendParameters *parm, int &diff) const
136 {
137         if (!parm)
138                 return -1;
139         int type;
140         if (parm->getSystem(type))
141                 return -1;
142         if (type != m_type)
143         {
144                 diff = 1<<30; // big difference
145                 return 0;
146         }
147         
148         switch (type)
149         {
150         case iDVBFrontend::feSatellite:
151         {
152                 eDVBFrontendParametersSatellite osat;
153                 if (parm->getDVBS(osat))
154                         return -2;
155                 
156                 if (sat.orbital_position != osat.orbital_position)
157                         diff = 1<<29;
158                 else if (sat.polarisation != osat.polarisation)
159                         diff = 1<<28;
160                 else 
161                         diff = abs(sat.frequency - osat.frequency);
162                 return 0;
163         }
164         case iDVBFrontend::feCable:
165         case iDVBFrontend::feTerrestrial:
166         default:
167                 return -1;
168         }
169         return 0;
170 }
171
172 RESULT eDVBFrontendParameters::getHash(unsigned long &hash) const 
173 {
174         switch (m_type)
175         {
176         case iDVBFrontend::feSatellite:
177         {
178                 hash  = sat.frequency & 0xFFFF;
179                 hash |= sat.orbital_position << 16;
180                 return 0;
181         }
182         case iDVBFrontend::feCable:
183         case iDVBFrontend::feTerrestrial:
184         default:
185                 return -1;
186         }
187 }
188
189 DEFINE_REF(eDVBFrontend);
190
191 eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok): ref(0), m_type(-1)
192 {
193         char filename[128];
194         int result;
195         dvb_frontend_info fe_info;
196         
197         m_sn = 0;
198         m_timeout = 0;
199         
200         sprintf(filename, "/dev/dvb/adapter%d/frontend%d", adap, fe);
201         eDebug("opening frontend.");
202         m_fd = ::open(filename, O_RDWR|O_NONBLOCK);
203         if (m_fd < 0)
204         {
205                 eWarning("failed! (%s) %m", filename);
206                 ok = 0;
207                 return;
208         }
209         
210         result = ::ioctl(m_fd, FE_GET_INFO, &fe_info);
211         
212         if (result < 0) {
213                 eWarning("ioctl FE_GET_INFO failed");
214                 ::close(m_fd);
215                 m_fd = -1;
216                 ok = 0;
217                 return;
218         }
219
220         switch (fe_info.type) 
221         {
222         case FE_QPSK:
223                 m_type = feSatellite;
224                 break;
225         case FE_QAM:
226                 m_type = feCable;
227                 break;
228         case FE_OFDM:
229                 m_type = feTerrestrial;
230                 break;
231         default:
232                 eWarning("unknown frontend type.");
233                 ::close(m_fd);
234                 m_fd = -1;
235                 ok = 0;
236                 return;
237         }
238         eDebug("detected %s frontend", "satellite\0cable\0    terrestrial"+feSatellite*9);
239         ok = 1;
240
241         m_sn = new eSocketNotifier(eApp, m_fd, eSocketNotifier::Read);
242         CONNECT(m_sn->activated, eDVBFrontend::feEvent);
243         m_sn->start();
244         
245         m_timeout = new eTimer(eApp);
246         CONNECT(m_timeout->timeout, eDVBFrontend::timeout);
247         
248         return;
249 }
250
251 eDVBFrontend::~eDVBFrontend()
252 {
253         if (m_fd >= 0)
254                 ::close(m_fd);
255         if (m_sn)
256                 delete m_sn;
257         if (m_timeout)
258                 delete m_timeout;
259 }
260
261 void eDVBFrontend::feEvent(int w)
262 {
263         while (1)
264         {
265                 dvb_frontend_event event;
266                 int res;
267                 int state;
268                 res = ::ioctl(m_fd, FE_GET_EVENT, &event);
269                 
270                 if (res && (errno == EAGAIN))
271                         break;
272
273                 if (res)
274                 {
275                         eWarning("FE_GET_EVENT failed! %m");
276                         return;
277                 }
278                 
279                 if (w < 0)
280                         continue;
281                 
282                 eDebug("fe event: status %x, inversion %s", event.status, (event.parameters.inversion == INVERSION_ON) ? "on" : "off");
283                 if (event.status & FE_HAS_LOCK)
284                 {
285                         state = stateLock;
286                 } else
287                 {
288                         if (m_tuning)
289                                 state = stateTuning;
290                         else
291                                 state = stateFailed;
292                 }
293                 if (m_state != state)
294                 {
295                         m_state = state;
296                         m_stateChanged(this);
297                 }
298         }
299 }
300
301 void eDVBFrontend::timeout()
302 {
303         int state;
304         if (m_state == stateTuning)
305         {
306                 state = stateFailed;
307                 eDebug("DVBFrontend: timeout");
308                 if (m_state != state)
309                 {
310                         m_state = state;
311                         m_stateChanged(this);
312                 }
313         } else
314                 m_tuning = 0;
315 }
316
317 RESULT eDVBFrontend::getFrontendType(int &t)
318 {
319         if (m_type == -1)
320                 return -ENODEV;
321         t = m_type;
322         return 0;
323 }
324
325 RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where)
326 {
327         if (m_type == -1)
328                 return -ENODEV;
329
330         dvb_frontend_parameters parm;
331         
332         feEvent(-1);
333         
334         switch (m_type)
335         {
336         case feSatellite:
337         {
338                 int res;
339                 eDVBFrontendParametersSatellite feparm;
340                 if (where.getDVBS(feparm))
341                 {
342                         eDebug("no dvbs data!");
343                         return -EINVAL;
344                 }
345                 if (!m_sec)
346                 {
347                         eWarning("no SEC module active!");
348                         return -ENOENT;
349                 }
350                 
351                 res = m_sec->prepare(*this, parm, feparm);
352                 if (res)
353                         return res;
354                 
355                 eDebug("tuning to %d mhz", parm.frequency/1000);
356                 break;
357         }
358         case feCable:
359         {
360                 eDVBFrontendParametersCable feparm;
361                 if (where.getDVBC(feparm))
362                         return -EINVAL;
363                 eFatal("cable tuning nyi");
364         }
365         case feTerrestrial:
366         {
367                 eDVBFrontendParametersTerrestrial feparm;
368                 if (where.getDVBT(feparm))
369                         return -EINVAL;
370                 eFatal("terrestrial tuning nyi");
371         }
372         }
373         
374         if (ioctl(m_fd, FE_SET_FRONTEND, &parm) == -1)
375         {
376                 perror("FE_SET_FRONTEND failed");
377                 return errno;
378         }
379         
380         if (m_state != stateTuning)
381         {
382                 m_tuning = 1;
383                 m_state = stateTuning;
384                 m_stateChanged(this);
385         }
386         
387         m_timeout->start(5000, 1); // 5 sec timeout. TODO: symbolrate dependent
388
389         return 0;
390 }
391
392 RESULT eDVBFrontend::connectStateChange(const Slot1<void,iDVBFrontend*> &stateChange, ePtr<eConnection> &connection)
393 {
394         connection = new eConnection(m_stateChanged.connect(stateChange));
395         return 0;
396 }
397
398 RESULT eDVBFrontend::setVoltage(int voltage)
399 {
400         fe_sec_voltage_t vlt;
401         
402         switch (voltage)
403         {
404         case voltageOff:
405                 vlt = SEC_VOLTAGE_OFF;
406                 break;
407         case voltage13:
408                 vlt = SEC_VOLTAGE_13;
409                 break;
410         case voltage18:
411                 vlt = SEC_VOLTAGE_18;
412                 break;
413         default:
414                 return -ENODEV;
415         }
416         
417         return ::ioctl(m_fd, FE_SET_VOLTAGE, vlt);
418 }
419
420 RESULT eDVBFrontend::getState(int &state)
421 {
422         state = m_state;
423         return 0;
424 }
425
426 RESULT eDVBFrontend::setTone(int t)
427 {
428         fe_sec_tone_mode_t tone;
429         
430         switch (t)
431         {
432         case toneOn:
433                 tone = SEC_TONE_ON;
434                 break;
435         case toneOff:
436                 tone = SEC_TONE_OFF;
437                 break;
438         default:
439                 return -ENODEV;
440         }
441         
442         return ::ioctl(m_fd, FE_SET_TONE, tone);        
443 }
444
445 RESULT eDVBFrontend::sendDiseqc(const eDVBDiseqcCommand &diseqc)
446 {
447         struct dvb_diseqc_master_cmd cmd;
448         if (::ioctl(m_fd, FE_SET_TONE, SEC_TONE_OFF))
449                 return -EINVAL;
450         usleep(15 * 1000);
451         memcpy(cmd.msg, diseqc.data, diseqc.len);
452         cmd.msg_len = diseqc.len;
453         
454         if (::ioctl(m_fd, FE_DISEQC_SEND_MASTER_CMD, &cmd))
455                 return -EINVAL;
456         usleep(15 * 1000);
457         eDebug("diseqc ok");
458         return 0;
459 }
460
461 RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec)
462 {
463         m_sec = sec;
464         return 0;
465 }