File: | dvbdevice.c |
Location: | line 234, column 16 |
Description: | Value stored to 'q' is never read |
1 | /* |
2 | * dvbdevice.c: The DVB device tuner interface |
3 | * |
4 | * See the main source file 'vdr.c' for copyright information and |
5 | * how to reach the author. |
6 | * |
7 | * $Id: dvbdevice.c 3.10 2014/01/20 11:46:26 kls Exp $ |
8 | */ |
9 | |
10 | #include "dvbdevice.h" |
11 | #include <ctype.h> |
12 | #include <errno(*__errno_location ()).h> |
13 | #include <limits.h> |
14 | #include <linux1/dvb/dmx.h> |
15 | #include <linux1/dvb/frontend.h> |
16 | #include <sys/ioctl.h> |
17 | #include <sys/mman.h> |
18 | #include "channels.h" |
19 | #include "diseqc.h" |
20 | #include "dvbci.h" |
21 | #include "menuitems.h" |
22 | #include "sourceparams.h" |
23 | |
24 | static int DvbApiVersion = 0x0000; // the version of the DVB driver actually in use (will be determined by the first device created) |
25 | |
26 | #define DVBS_TUNE_TIMEOUT9000 9000 //ms |
27 | #define DVBS_LOCK_TIMEOUT2000 2000 //ms |
28 | #define DVBC_TUNE_TIMEOUT9000 9000 //ms |
29 | #define DVBC_LOCK_TIMEOUT2000 2000 //ms |
30 | #define DVBT_TUNE_TIMEOUT9000 9000 //ms |
31 | #define DVBT_LOCK_TIMEOUT2000 2000 //ms |
32 | #define ATSC_TUNE_TIMEOUT9000 9000 //ms |
33 | #define ATSC_LOCK_TIMEOUT2000 2000 //ms |
34 | |
35 | #define SCR_RANDOM_TIMEOUT500 500 // ms (add random value up to this when tuning SCR device to avoid lockups) |
36 | |
37 | // --- DVB Parameter Maps ---------------------------------------------------- |
38 | |
39 | const tDvbParameterMap InversionValues[] = { |
40 | { 0, INVERSION_OFF, trNOOP("off")("off") }, |
41 | { 1, INVERSION_ON, trNOOP("on")("on") }, |
42 | { 999, INVERSION_AUTO, trNOOP("auto")("auto") }, |
43 | { -1, 0, NULL__null } |
44 | }; |
45 | |
46 | const tDvbParameterMap BandwidthValues[] = { |
47 | { 5, 5000000, "5 MHz" }, |
48 | { 6, 6000000, "6 MHz" }, |
49 | { 7, 7000000, "7 MHz" }, |
50 | { 8, 8000000, "8 MHz" }, |
51 | { 10, 10000000, "10 MHz" }, |
52 | { 1712, 1712000, "1.712 MHz" }, |
53 | { -1, 0, NULL__null } |
54 | }; |
55 | |
56 | const tDvbParameterMap CoderateValues[] = { |
57 | { 0, FEC_NONE, trNOOP("none")("none") }, |
58 | { 12, FEC_1_2, "1/2" }, |
59 | { 23, FEC_2_3, "2/3" }, |
60 | { 34, FEC_3_4, "3/4" }, |
61 | { 35, FEC_3_5, "3/5" }, |
62 | { 45, FEC_4_5, "4/5" }, |
63 | { 56, FEC_5_6, "5/6" }, |
64 | { 67, FEC_6_7, "6/7" }, |
65 | { 78, FEC_7_8, "7/8" }, |
66 | { 89, FEC_8_9, "8/9" }, |
67 | { 910, FEC_9_10, "9/10" }, |
68 | { 999, FEC_AUTO, trNOOP("auto")("auto") }, |
69 | { -1, 0, NULL__null } |
70 | }; |
71 | |
72 | const tDvbParameterMap ModulationValues[] = { |
73 | { 16, QAM_16, "QAM16" }, |
74 | { 32, QAM_32, "QAM32" }, |
75 | { 64, QAM_64, "QAM64" }, |
76 | { 128, QAM_128, "QAM128" }, |
77 | { 256, QAM_256, "QAM256" }, |
78 | { 2, QPSK, "QPSK" }, |
79 | { 5, PSK_8, "8PSK" }, |
80 | { 6, APSK_16, "16APSK" }, |
81 | { 7, APSK_32, "32APSK" }, |
82 | { 10, VSB_8, "VSB8" }, |
83 | { 11, VSB_16, "VSB16" }, |
84 | { 12, DQPSK, "DQPSK" }, |
85 | { 999, QAM_AUTO, trNOOP("auto")("auto") }, |
86 | { -1, 0, NULL__null } |
87 | }; |
88 | |
89 | #define DVB_SYSTEM_10 0 // see also nit.c |
90 | #define DVB_SYSTEM_21 1 |
91 | |
92 | const tDvbParameterMap SystemValuesSat[] = { |
93 | { 0, DVB_SYSTEM_10, "DVB-S" }, |
94 | { 1, DVB_SYSTEM_21, "DVB-S2" }, |
95 | { -1, 0, NULL__null } |
96 | }; |
97 | |
98 | const tDvbParameterMap SystemValuesTerr[] = { |
99 | { 0, DVB_SYSTEM_10, "DVB-T" }, |
100 | { 1, DVB_SYSTEM_21, "DVB-T2" }, |
101 | { -1, 0, NULL__null } |
102 | }; |
103 | |
104 | const tDvbParameterMap TransmissionValues[] = { |
105 | { 1, TRANSMISSION_MODE_1K, "1K" }, |
106 | { 2, TRANSMISSION_MODE_2K, "2K" }, |
107 | { 4, TRANSMISSION_MODE_4K, "4K" }, |
108 | { 8, TRANSMISSION_MODE_8K, "8K" }, |
109 | { 16, TRANSMISSION_MODE_16K, "16K" }, |
110 | { 32, TRANSMISSION_MODE_32K, "32K" }, |
111 | { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto")("auto") }, |
112 | { -1, 0, NULL__null } |
113 | }; |
114 | |
115 | const tDvbParameterMap GuardValues[] = { |
116 | { 4, GUARD_INTERVAL_1_4, "1/4" }, |
117 | { 8, GUARD_INTERVAL_1_8, "1/8" }, |
118 | { 16, GUARD_INTERVAL_1_16, "1/16" }, |
119 | { 32, GUARD_INTERVAL_1_32, "1/32" }, |
120 | { 128, GUARD_INTERVAL_1_128, "1/128" }, |
121 | { 19128, GUARD_INTERVAL_19_128, "19/128" }, |
122 | { 19256, GUARD_INTERVAL_19_256, "19/256" }, |
123 | { 999, GUARD_INTERVAL_AUTO, trNOOP("auto")("auto") }, |
124 | { -1, 0, NULL__null } |
125 | }; |
126 | |
127 | const tDvbParameterMap HierarchyValues[] = { |
128 | { 0, HIERARCHY_NONE, trNOOP("none")("none") }, |
129 | { 1, HIERARCHY_1, "1" }, |
130 | { 2, HIERARCHY_2, "2" }, |
131 | { 4, HIERARCHY_4, "4" }, |
132 | { 999, HIERARCHY_AUTO, trNOOP("auto")("auto") }, |
133 | { -1, 0, NULL__null } |
134 | }; |
135 | |
136 | const tDvbParameterMap RollOffValues[] = { |
137 | { 0, ROLLOFF_AUTO, trNOOP("auto")("auto") }, |
138 | { 20, ROLLOFF_20, "0.20" }, |
139 | { 25, ROLLOFF_25, "0.25" }, |
140 | { 35, ROLLOFF_35, "0.35" }, |
141 | { -1, 0, NULL__null } |
142 | }; |
143 | |
144 | int UserIndex(int Value, const tDvbParameterMap *Map) |
145 | { |
146 | const tDvbParameterMap *map = Map; |
147 | while (map && map->userValue != -1) { |
148 | if (map->userValue == Value) |
149 | return map - Map; |
150 | map++; |
151 | } |
152 | return -1; |
153 | } |
154 | |
155 | int DriverIndex(int Value, const tDvbParameterMap *Map) |
156 | { |
157 | const tDvbParameterMap *map = Map; |
158 | while (map && map->userValue != -1) { |
159 | if (map->driverValue == Value) |
160 | return map - Map; |
161 | map++; |
162 | } |
163 | return -1; |
164 | } |
165 | |
166 | int MapToUser(int Value, const tDvbParameterMap *Map, const char **String) |
167 | { |
168 | int n = DriverIndex(Value, Map); |
169 | if (n >= 0) { |
170 | if (String) |
171 | *String = tr(Map[n].userString)I18nTranslate(Map[n].userString); |
172 | return Map[n].userValue; |
173 | } |
174 | return -1; |
175 | } |
176 | |
177 | const char *MapToUserString(int Value, const tDvbParameterMap *Map) |
178 | { |
179 | int n = DriverIndex(Value, Map); |
180 | if (n >= 0) |
181 | return Map[n].userString; |
182 | return "???"; |
183 | } |
184 | |
185 | int MapToDriver(int Value, const tDvbParameterMap *Map) |
186 | { |
187 | int n = UserIndex(Value, Map); |
188 | if (n >= 0) |
189 | return Map[n].driverValue; |
190 | return -1; |
191 | } |
192 | |
193 | // --- cDvbTransponderParameters --------------------------------------------- |
194 | |
195 | cDvbTransponderParameters::cDvbTransponderParameters(const char *Parameters) |
196 | { |
197 | polarization = 0; |
198 | inversion = INVERSION_AUTO; |
199 | bandwidth = 8000000; |
200 | coderateH = FEC_AUTO; |
201 | coderateL = FEC_AUTO; |
202 | modulation = QPSK; |
203 | system = DVB_SYSTEM_10; |
204 | transmission = TRANSMISSION_MODE_AUTO; |
205 | guard = GUARD_INTERVAL_AUTO; |
206 | hierarchy = HIERARCHY_AUTO; |
207 | rollOff = ROLLOFF_AUTO; |
208 | streamId = 0; |
209 | Parse(Parameters); |
210 | } |
211 | |
212 | int cDvbTransponderParameters::PrintParameter(char *p, char Name, int Value) const |
213 | { |
214 | return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0; |
215 | } |
216 | |
217 | cString cDvbTransponderParameters::ToString(char Type) const |
218 | { |
219 | #define ST(s)if (strchr(s, type)) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*'))) |
220 | char buffer[64]; |
221 | char *q = buffer; |
222 | *q = 0; |
223 | ST(" S *")if (strchr(" S *", type)) q += sprintf(q, "%c", polarization); |
224 | ST(" T*")if (strchr(" T*", type)) q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues)); |
225 | ST(" CST*")if (strchr(" CST*", type)) q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues)); |
226 | ST(" T*")if (strchr(" T*", type)) q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues)); |
227 | ST(" T*")if (strchr(" T*", type)) q += PrintParameter(q, 'G', MapToUser(guard, GuardValues)); |
228 | ST("ACST*")if (strchr("ACST*", type)) q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues)); |
229 | ST("ACST*")if (strchr("ACST*", type)) q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues)); |
230 | ST(" S 2")if (strchr(" S 2", type)) q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues)); |
231 | ST(" ST2")if (strchr(" ST2", type)) q += PrintParameter(q, 'P', streamId); |
232 | ST(" ST*")if (strchr(" ST*", type)) q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter |
233 | ST(" T*")if (strchr(" T*", type)) q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues)); |
234 | ST(" T*")if (strchr(" T*", type)) q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues)); |
Value stored to 'q' is never read | |
235 | return buffer; |
236 | } |
237 | |
238 | const char *cDvbTransponderParameters::ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map) |
239 | { |
240 | if (*++s) { |
241 | char *p = NULL__null; |
242 | errno(*__errno_location ()) = 0; |
243 | int n = strtol(s, &p, 10); |
244 | if (!errno(*__errno_location ()) && p != s) { |
245 | Value = Map ? MapToDriver(n, Map) : n; |
246 | if (Value >= 0) |
247 | return p; |
248 | } |
249 | } |
250 | esyslog("ERROR: invalid value for parameter '%c'", *(s - 1))void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: invalid value for parameter '%c'" , *(s - 1)) : void() ); |
251 | return NULL__null; |
252 | } |
253 | |
254 | bool cDvbTransponderParameters::Parse(const char *s) |
255 | { |
256 | while (s && *s) { |
257 | switch (toupper(*s)) { |
258 | case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break; |
259 | case 'C': s = ParseParameter(s, coderateH, CoderateValues); break; |
260 | case 'D': s = ParseParameter(s, coderateL, CoderateValues); break; |
261 | case 'G': s = ParseParameter(s, guard, GuardValues); break; |
262 | case 'H': polarization = 'H'; s++; break; |
263 | case 'I': s = ParseParameter(s, inversion, InversionValues); break; |
264 | case 'L': polarization = 'L'; s++; break; |
265 | case 'M': s = ParseParameter(s, modulation, ModulationValues); break; |
266 | case 'O': s = ParseParameter(s, rollOff, RollOffValues); break; |
267 | case 'P': s = ParseParameter(s, streamId); break; |
268 | case 'R': polarization = 'R'; s++; break; |
269 | case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter |
270 | case 'T': s = ParseParameter(s, transmission, TransmissionValues); break; |
271 | case 'V': polarization = 'V'; s++; break; |
272 | case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break; |
273 | default: esyslog("ERROR: unknown parameter key '%c'", *s)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown parameter key '%c'" , *s) : void() ); |
274 | return false; |
275 | } |
276 | } |
277 | return true; |
278 | } |
279 | |
280 | // --- cDvbTuner ------------------------------------------------------------- |
281 | |
282 | #define TUNER_POLL_TIMEOUT10 10 // ms |
283 | |
284 | class cDvbTuner : public cThread { |
285 | private: |
286 | static cMutex bondMutex; |
287 | enum eTunerStatus { tsIdle, tsSet, tsPositioning, tsTuned, tsLocked }; |
288 | int frontendType; |
289 | const cDvbDevice *device; |
290 | int fd_frontend; |
291 | int adapter, frontend; |
292 | uint32_t subsystemId; |
293 | int tuneTimeout; |
294 | int lockTimeout; |
295 | time_t lastTimeoutReport; |
296 | cChannel channel; |
297 | const cDiseqc *lastDiseqc; |
298 | int diseqcOffset; |
299 | int lastSource; |
300 | cPositioner *positioner; |
301 | const cScr *scr; |
302 | bool lnbPowerTurnedOn; |
303 | eTunerStatus tunerStatus; |
304 | cMutex mutex; |
305 | cCondVar locked; |
306 | cCondVar newSet; |
307 | cDvbTuner *bondedTuner; |
308 | bool bondedMaster; |
309 | bool SetFrontendType(const cChannel *Channel); |
310 | cString GetBondingParams(const cChannel *Channel = NULL__null) const; |
311 | cDvbTuner *GetBondedMaster(void); |
312 | bool IsBondedMaster(void) const { return !bondedTuner || bondedMaster; } |
313 | void ClearEventQueue(void) const; |
314 | bool GetFrontendStatus(fe_status_t &Status) const; |
315 | cPositioner *GetPositioner(void); |
316 | void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency); |
317 | void ResetToneAndVoltage(void); |
318 | bool SetFrontend(void); |
319 | virtual void Action(void); |
320 | public: |
321 | cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend); |
322 | virtual ~cDvbTuner(); |
323 | int FrontendType(void) const { return frontendType; } |
324 | bool Bond(cDvbTuner *Tuner); |
325 | void UnBond(void); |
326 | bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const; |
327 | const cChannel *GetTransponder(void) const { return &channel; } |
328 | uint32_t SubsystemId(void) const { return subsystemId; } |
329 | bool IsTunedTo(const cChannel *Channel) const; |
330 | void SetChannel(const cChannel *Channel); |
331 | bool Locked(int TimeoutMs = 0); |
332 | const cPositioner *Positioner(void) const { return positioner; } |
333 | int GetSignalStrength(void) const; |
334 | int GetSignalQuality(void) const; |
335 | }; |
336 | |
337 | cMutex cDvbTuner::bondMutex; |
338 | |
339 | cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend) |
340 | { |
341 | frontendType = SYS_UNDEFINED; |
342 | device = Device; |
343 | fd_frontend = Fd_Frontend; |
344 | adapter = Adapter; |
345 | frontend = Frontend; |
346 | subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); |
347 | tuneTimeout = 0; |
348 | lockTimeout = 0; |
349 | lastTimeoutReport = 0; |
350 | lastDiseqc = NULL__null; |
351 | diseqcOffset = 0; |
352 | lastSource = 0; |
353 | positioner = NULL__null; |
354 | scr = NULL__null; |
355 | lnbPowerTurnedOn = false; |
356 | tunerStatus = tsIdle; |
357 | bondedTuner = NULL__null; |
358 | bondedMaster = false; |
359 | SetDescription("tuner on frontend %d/%d", adapter, frontend); |
360 | Start(); |
361 | } |
362 | |
363 | cDvbTuner::~cDvbTuner() |
364 | { |
365 | tunerStatus = tsIdle; |
366 | newSet.Broadcast(); |
367 | locked.Broadcast(); |
368 | Cancel(3); |
369 | UnBond(); |
370 | /* looks like this irritates the SCR switch, so let's leave it out for now |
371 | if (lastDiseqc && lastDiseqc->IsScr()) { |
372 | unsigned int Frequency = 0; |
373 | ExecuteDiseqc(lastDiseqc, &Frequency); |
374 | } |
375 | */ |
376 | } |
377 | |
378 | bool cDvbTuner::Bond(cDvbTuner *Tuner) |
379 | { |
380 | cMutexLock MutexLock(&bondMutex); |
381 | if (!bondedTuner) { |
382 | ResetToneAndVoltage(); |
383 | bondedMaster = false; // makes sure we don't disturb an existing master |
384 | bondedTuner = Tuner->bondedTuner ? Tuner->bondedTuner : Tuner; |
385 | Tuner->bondedTuner = this; |
386 | dsyslog("tuner %d/%d bonded with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend)void( (SysLogLevel > 2) ? syslog_with_tid(7, "tuner %d/%d bonded with tuner %d/%d" , adapter, frontend, bondedTuner->adapter, bondedTuner-> frontend) : void() ); |
387 | return true; |
388 | } |
389 | else |
390 | esyslog("ERROR: tuner %d/%d already bonded with tuner %d/%d, can't bond with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend, Tuner->adapter, Tuner->frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: tuner %d/%d already bonded with tuner %d/%d, can't bond with tuner %d/%d" , adapter, frontend, bondedTuner->adapter, bondedTuner-> frontend, Tuner->adapter, Tuner->frontend) : void() ); |
391 | return false; |
392 | } |
393 | |
394 | void cDvbTuner::UnBond(void) |
395 | { |
396 | cMutexLock MutexLock(&bondMutex); |
397 | if (cDvbTuner *t = bondedTuner) { |
398 | dsyslog("tuner %d/%d unbonded from tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend)void( (SysLogLevel > 2) ? syslog_with_tid(7, "tuner %d/%d unbonded from tuner %d/%d" , adapter, frontend, bondedTuner->adapter, bondedTuner-> frontend) : void() ); |
399 | while (t->bondedTuner != this) |
400 | t = t->bondedTuner; |
401 | if (t == bondedTuner) |
402 | t->bondedTuner = NULL__null; |
403 | else |
404 | t->bondedTuner = bondedTuner; |
405 | bondedMaster = false; // another one will automatically become master whenever necessary |
406 | bondedTuner = NULL__null; |
407 | } |
408 | } |
409 | |
410 | cString cDvbTuner::GetBondingParams(const cChannel *Channel) const |
411 | { |
412 | if (!Channel) |
413 | Channel = &channel; |
414 | cDvbTransponderParameters dtp(Channel->Parameters()); |
415 | if (Setup.DiSEqC) { |
416 | if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL__null)) |
417 | return diseqc->Commands(); |
418 | } |
419 | else { |
420 | bool ToneOff = Channel->Frequency() < Setup.LnbSLOF; |
421 | bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R'; |
422 | return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V'); |
423 | } |
424 | return ""; |
425 | } |
426 | |
427 | bool cDvbTuner::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const |
428 | { |
429 | cMutexLock MutexLock(&bondMutex); |
430 | if (cDvbTuner *t = bondedTuner) { |
431 | cString BondingParams = GetBondingParams(Channel); |
432 | do { |
433 | if (t->device->Priority() > IDLEPRIORITY((-99) - 1) || ConsiderOccupied && t->device->Occupied()) { |
434 | if (strcmp(BondingParams, t->GetBondedMaster()->GetBondingParams()) != 0) |
435 | return false; |
436 | } |
437 | t = t->bondedTuner; |
438 | } while (t != bondedTuner); |
439 | } |
440 | return true; |
441 | } |
442 | |
443 | cDvbTuner *cDvbTuner::GetBondedMaster(void) |
444 | { |
445 | if (!bondedTuner) |
446 | return this; // an unbonded tuner is always "master" |
447 | cMutexLock MutexLock(&bondMutex); |
448 | if (bondedMaster) |
449 | return this; |
450 | // This tuner is bonded, but it's not the master, so let's see if there is a master at all: |
451 | if (cDvbTuner *t = bondedTuner) { |
452 | while (t != this) { |
453 | if (t->bondedMaster) |
454 | return t; |
455 | t = t->bondedTuner; |
456 | } |
457 | } |
458 | // None of the other bonded tuners is master, so make this one the master: |
459 | bondedMaster = true; |
460 | dsyslog("tuner %d/%d is now bonded master", adapter, frontend)void( (SysLogLevel > 2) ? syslog_with_tid(7, "tuner %d/%d is now bonded master" , adapter, frontend) : void() ); |
461 | return this; |
462 | } |
463 | |
464 | bool cDvbTuner::IsTunedTo(const cChannel *Channel) const |
465 | { |
466 | if (tunerStatus == tsIdle) |
467 | return false; // not tuned to |
468 | if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder()) |
469 | return false; // sufficient mismatch |
470 | // Polarization is already checked as part of the Transponder. |
471 | return strcmp(channel.Parameters(), Channel->Parameters()) == 0; |
472 | } |
473 | |
474 | void cDvbTuner::SetChannel(const cChannel *Channel) |
475 | { |
476 | if (Channel) { |
477 | if (bondedTuner) { |
478 | cMutexLock MutexLock(&bondMutex); |
479 | cDvbTuner *BondedMaster = GetBondedMaster(); |
480 | if (BondedMaster == this) { |
481 | if (strcmp(GetBondingParams(Channel), GetBondingParams()) != 0) { |
482 | // switching to a completely different band, so set all others to idle: |
483 | for (cDvbTuner *t = bondedTuner; t && t != this; t = t->bondedTuner) |
484 | t->SetChannel(NULL__null); |
485 | } |
486 | } |
487 | else if (strcmp(GetBondingParams(Channel), BondedMaster->GetBondingParams()) != 0) |
488 | BondedMaster->SetChannel(Channel); |
489 | } |
490 | cMutexLock MutexLock(&mutex); |
491 | if (!IsTunedTo(Channel)) |
492 | tunerStatus = tsSet; |
493 | diseqcOffset = 0; |
494 | channel = *Channel; |
495 | lastTimeoutReport = 0; |
496 | newSet.Broadcast(); |
497 | } |
498 | else { |
499 | cMutexLock MutexLock(&mutex); |
500 | tunerStatus = tsIdle; |
501 | ResetToneAndVoltage(); |
502 | } |
503 | if (bondedTuner && device->IsPrimaryDevice()) |
504 | cDevice::PrimaryDevice()->DelLivePids(); // 'device' is const, so we must do it this way |
505 | } |
506 | |
507 | bool cDvbTuner::Locked(int TimeoutMs) |
508 | { |
509 | bool isLocked = (tunerStatus >= tsLocked); |
510 | if (isLocked || !TimeoutMs) |
511 | return isLocked; |
512 | |
513 | cMutexLock MutexLock(&mutex); |
514 | if (TimeoutMs && tunerStatus < tsLocked) |
515 | locked.TimedWait(mutex, TimeoutMs); |
516 | return tunerStatus >= tsLocked; |
517 | } |
518 | |
519 | void cDvbTuner::ClearEventQueue(void) const |
520 | { |
521 | cPoller Poller(fd_frontend); |
522 | if (Poller.Poll(TUNER_POLL_TIMEOUT10)) { |
523 | dvb_frontend_event Event; |
524 | while (ioctl(fd_frontend, FE_GET_EVENT(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((78)) << 0) | ((((sizeof(struct dvb_frontend_event)) )) << ((0 +8)+8))), &Event) == 0) |
525 | ; // just to clear the event queue - we'll read the actual status below |
526 | } |
527 | } |
528 | |
529 | bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const |
530 | { |
531 | ClearEventQueue(); |
532 | while (1) { |
533 | if (ioctl(fd_frontend, FE_READ_STATUS(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((69)) << 0) | ((((sizeof(fe_status_t)))) << (( 0 +8)+8))), &Status) != -1) |
534 | return true; |
535 | if (errno(*__errno_location ()) != EINTR4) |
536 | break; |
537 | } |
538 | return false; |
539 | } |
540 | |
541 | //#define DEBUG_SIGNALSTRENGTH |
542 | //#define DEBUG_SIGNALQUALITY |
543 | |
544 | int cDvbTuner::GetSignalStrength(void) const |
545 | { |
546 | ClearEventQueue(); |
547 | uint16_t Signal; |
548 | while (1) { |
549 | if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((71)) << 0) | ((((sizeof(__u16)))) << ((0 +8)+ 8))), &Signal) != -1) |
550 | break; |
551 | if (errno(*__errno_location ()) != EINTR4) |
552 | return -1; |
553 | } |
554 | uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range. |
555 | // Use the subsystemId to identify individual devices in case they need |
556 | // special treatment to map their Signal value into the range 0...0xFFFF. |
557 | switch (subsystemId) { |
558 | case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2) |
559 | case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2) |
560 | MaxSignal = 670; break; |
561 | } |
562 | int s = int(Signal) * 100 / MaxSignal; |
563 | if (s > 100) |
564 | s = 100; |
565 | #ifdef DEBUG_SIGNALSTRENGTH |
566 | fprintf(stderrstderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s); |
567 | #endif |
568 | return s; |
569 | } |
570 | |
571 | #define LOCK_THRESHOLD5 5 // indicates that all 5 FE_HAS_* flags are set |
572 | |
573 | int cDvbTuner::GetSignalQuality(void) const |
574 | { |
575 | fe_status_t Status; |
576 | if (GetFrontendStatus(Status)) { |
577 | // Actually one would expect these checks to be done from FE_HAS_SIGNAL to FE_HAS_LOCK, but some drivers (like the stb0899) are broken, so FE_HAS_LOCK is the only one that (hopefully) is generally reliable... |
578 | if ((Status & FE_HAS_LOCK) == 0) { |
579 | if ((Status & FE_HAS_SIGNAL) == 0) |
580 | return 0; |
581 | if ((Status & FE_HAS_CARRIER) == 0) |
582 | return 1; |
583 | if ((Status & FE_HAS_VITERBI) == 0) |
584 | return 2; |
585 | if ((Status & FE_HAS_SYNC) == 0) |
586 | return 3; |
587 | return 4; |
588 | } |
589 | #ifdef DEBUG_SIGNALQUALITY |
590 | bool HasSnr = true; |
591 | #endif |
592 | uint16_t Snr; |
593 | while (1) { |
594 | if (ioctl(fd_frontend, FE_READ_SNR(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((72)) << 0) | ((((sizeof(__u16)))) << ((0 +8)+ 8))), &Snr) != -1) |
595 | break; |
596 | if (errno(*__errno_location ()) != EINTR4) { |
597 | Snr = 0xFFFF; |
598 | #ifdef DEBUG_SIGNALQUALITY |
599 | HasSnr = false; |
600 | #endif |
601 | break; |
602 | } |
603 | } |
604 | #ifdef DEBUG_SIGNALQUALITY |
605 | bool HasBer = true; |
606 | #endif |
607 | uint32_t Ber; |
608 | while (1) { |
609 | if (ioctl(fd_frontend, FE_READ_BER(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((70)) << 0) | ((((sizeof(__u32)))) << ((0 +8)+ 8))), &Ber) != -1) |
610 | break; |
611 | if (errno(*__errno_location ()) != EINTR4) { |
612 | Ber = 0; |
613 | #ifdef DEBUG_SIGNALQUALITY |
614 | HasBer = false; |
615 | #endif |
616 | break; |
617 | } |
618 | } |
619 | #ifdef DEBUG_SIGNALQUALITY |
620 | bool HasUnc = true; |
621 | #endif |
622 | uint32_t Unc; |
623 | while (1) { |
624 | if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((73)) << 0) | ((((sizeof(__u32)))) << ((0 +8)+ 8))), &Unc) != -1) |
625 | break; |
626 | if (errno(*__errno_location ()) != EINTR4) { |
627 | Unc = 0; |
628 | #ifdef DEBUG_SIGNALQUALITY |
629 | HasUnc = false; |
630 | #endif |
631 | break; |
632 | } |
633 | } |
634 | uint16_t MinSnr = 0x0000; |
635 | uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range. |
636 | // Use the subsystemId to identify individual devices in case they need |
637 | // special treatment to map their Snr value into the range 0...0xFFFF. |
638 | switch (subsystemId) { |
639 | case 0x13C21019: // TT-budget S2-3200 (DVB-S/DVB-S2) |
640 | case 0x1AE40001: // TechniSat SkyStar HD2 (DVB-S/DVB-S2) |
641 | if (frontendType == SYS_DVBS2) { |
642 | MinSnr = 10; |
643 | MaxSnr = 70; |
644 | } |
645 | else |
646 | MaxSnr = 200; |
647 | break; |
648 | case 0x20130245: // PCTV Systems PCTV 73ESE |
649 | case 0x2013024F: // PCTV Systems nanoStick T2 290e |
650 | MaxSnr = 255; break; |
651 | } |
652 | int a = int(constrain(Snr, MinSnr, MaxSnr)) * 100 / (MaxSnr - MinSnr); |
653 | int b = 100 - (Unc * 10 + (Ber / 256) * 5); |
654 | if (b < 0) |
655 | b = 0; |
656 | int q = LOCK_THRESHOLD5 + a * b * (100 - LOCK_THRESHOLD5) / 100 / 100; |
657 | if (q > 100) |
658 | q = 100; |
659 | #ifdef DEBUG_SIGNALQUALITY |
660 | fprintf(stderrstderr, "FE %d/%d: %08X Q = %04X %04X %d %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSnr, Snr, HasSnr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q); |
661 | #endif |
662 | return q; |
663 | } |
664 | return -1; |
665 | } |
666 | |
667 | static unsigned int FrequencyToHz(unsigned int f) |
668 | { |
669 | while (f && f < 1000000) |
670 | f *= 1000; |
671 | return f; |
672 | } |
673 | |
674 | cPositioner *cDvbTuner::GetPositioner(void) |
675 | { |
676 | if (!positioner) { |
677 | positioner = cPositioner::GetPositioner(); |
678 | positioner->SetFrontend(fd_frontend); |
679 | } |
680 | return positioner; |
681 | } |
682 | |
683 | void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency) |
684 | { |
685 | if (!lnbPowerTurnedOn) { |
686 | CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((67)) << 0) | ((0) << ((0 +8)+8))), SEC_VOLTAGE_13)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 686 ) : void() ); }; // must explicitly turn on LNB power |
687 | lnbPowerTurnedOn = true; |
688 | } |
689 | static cMutex Mutex; |
690 | if (Diseqc->IsScr()) |
691 | Mutex.Lock(); |
692 | struct dvb_diseqc_master_cmd cmd; |
693 | const char *CurrentAction = NULL__null; |
694 | cPositioner *Positioner = NULL__null; |
695 | bool Break = false; |
696 | for (int i = 0; !Break; i++) { |
697 | cmd.msg_len = sizeof(cmd.msg); |
698 | cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency); |
699 | if (da == cDiseqc::daNone) { |
700 | diseqcOffset = 0; |
701 | break; |
702 | } |
703 | bool d = i >= diseqcOffset; |
704 | switch (da) { |
705 | case cDiseqc::daToneOff: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((66)) << 0) | ((0) << ((0 +8)+8))), SEC_TONE_OFF)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 705 ) : void() ); }; break; |
706 | case cDiseqc::daToneOn: if (d) CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((66)) << 0) | ((0) << ((0 +8)+8))), SEC_TONE_ON)) < 0) void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 706 ) : void() ); }; break; |
707 | case cDiseqc::daVoltage13: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((67)) << 0) | ((0) << ((0 +8)+8))), SEC_VOLTAGE_13)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 707 ) : void() ); }; break; |
708 | case cDiseqc::daVoltage18: if (d) CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((67)) << 0) | ((0) << ((0 +8)+8))), SEC_VOLTAGE_18)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 708 ) : void() ); }; break; |
709 | case cDiseqc::daMiniA: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((65)) << 0) | ((0) << ((0 +8)+8))), SEC_MINI_A)) < 0) void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 709 ) : void() ); }; break; |
710 | case cDiseqc::daMiniB: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((65)) << 0) | ((0) << ((0 +8)+8))), SEC_MINI_B)) < 0) void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 710 ) : void() ); }; break; |
711 | case cDiseqc::daCodes: if (d) CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)){ if ((ioctl(fd_frontend, (((1U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((63)) << 0) | ((((sizeof(struct dvb_diseqc_master_cmd)))) << ((0 +8)+8))), &cmd)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 711) : void() ); }; break; |
712 | case cDiseqc::daPositionN: if ((Positioner = GetPositioner()) != NULL__null) { |
713 | if (d) { |
714 | Positioner->GotoPosition(Diseqc->Position(), cSource::Position(channel.Source())); |
715 | Break = Positioner->IsMoving(); |
716 | } |
717 | } |
718 | break; |
719 | case cDiseqc::daPositionA: if ((Positioner = GetPositioner()) != NULL__null) { |
720 | if (d) { |
721 | Positioner->GotoAngle(cSource::Position(channel.Source())); |
722 | Break = Positioner->IsMoving(); |
723 | } |
724 | } |
725 | break; |
726 | case cDiseqc::daScr: |
727 | case cDiseqc::daWait: break; |
728 | default: esyslog("ERROR: unknown diseqc command %d", da)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown diseqc command %d" , da) : void() ); |
729 | } |
730 | if (Break) |
731 | diseqcOffset = i + 1; |
732 | } |
733 | positioner = Positioner; |
734 | if (scr && !Break) |
735 | ResetToneAndVoltage(); // makes sure we don't block the bus! |
736 | if (Diseqc->IsScr()) |
737 | Mutex.Unlock(); |
738 | } |
739 | |
740 | void cDvbTuner::ResetToneAndVoltage(void) |
741 | { |
742 | CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((67)) << 0) | ((0) << ((0 +8)+8))), bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13 )) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 742) : void() ); }; |
743 | CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((66)) << 0) | ((0) << ((0 +8)+8))), SEC_TONE_OFF)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m", "dvbdevice.c", 743 ) : void() ); }; |
744 | } |
745 | |
746 | static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp) |
747 | { |
748 | int ds = SYS_UNDEFINED; |
749 | if (Channel->IsAtsc()) |
750 | ds = SYS_ATSC; |
751 | else if (Channel->IsCable()) |
752 | ds = SYS_DVBC_ANNEX_ACSYS_DVBC_ANNEX_A; |
753 | else if (Channel->IsSat()) |
754 | ds = Dtp->System() == DVB_SYSTEM_10 ? SYS_DVBS : SYS_DVBS2; |
755 | else if (Channel->IsTerr()) |
756 | ds = Dtp->System() == DVB_SYSTEM_10 ? SYS_DVBT : SYS_DVBT2; |
757 | else |
758 | esyslog("ERROR: can't determine frontend type for channel %d", Channel->Number())void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't determine frontend type for channel %d" , Channel->Number()) : void() ); |
759 | return ds; |
760 | } |
761 | |
762 | bool cDvbTuner::SetFrontend(void) |
763 | { |
764 | #define MAXFRONTENDCMDS16 16 |
765 | #define SETCMD(c, d){ Frontend[CmdSeq.num].cmd = (c); Frontend[CmdSeq.num].u.data = (d); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } } { Frontend[CmdSeq.num].cmd = (c);\ |
766 | Frontend[CmdSeq.num].u.data = (d);\ |
767 | if (CmdSeq.num++ > MAXFRONTENDCMDS16) {\ |
768 | esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() );\ |
769 | return false;\ |
770 | }\ |
771 | } |
772 | dtv_property Frontend[MAXFRONTENDCMDS16]; |
773 | memset(&Frontend, 0, sizeof(Frontend)); |
774 | dtv_properties CmdSeq; |
775 | memset(&CmdSeq, 0, sizeof(CmdSeq)); |
776 | CmdSeq.props = Frontend; |
777 | SETCMD(DTV_CLEAR, 0){ Frontend[CmdSeq.num].cmd = (2); Frontend[CmdSeq.num].u.data = (0); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
778 | if (ioctl(fd_frontend, FE_SET_PROPERTY(((1U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((82)) << 0) | ((((sizeof(struct dtv_properties)))) << ((0 +8)+8))), &CmdSeq) < 0) { |
779 | esyslog("ERROR: frontend %d/%d: %m", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: frontend %d/%d: %m" , adapter, frontend) : void() ); |
780 | return false; |
781 | } |
782 | CmdSeq.num = 0; |
783 | |
784 | cDvbTransponderParameters dtp(channel.Parameters()); |
785 | |
786 | // Determine the required frontend type: |
787 | frontendType = GetRequiredDeliverySystem(&channel, &dtp); |
788 | if (frontendType == SYS_UNDEFINED) |
789 | return false; |
790 | |
791 | SETCMD(DTV_DELIVERY_SYSTEM, frontendType){ Frontend[CmdSeq.num].cmd = (17); Frontend[CmdSeq.num].u.data = (frontendType); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
792 | if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) { |
793 | unsigned int frequency = channel.Frequency(); |
794 | if (Setup.DiSEqC) { |
795 | if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) { |
796 | frequency -= diseqc->Lof(); |
797 | if (diseqc != lastDiseqc || diseqc->IsScr() || diseqc->Position() >= 0 && channel.Source() != lastSource) { |
798 | if (IsBondedMaster()) { |
799 | ExecuteDiseqc(diseqc, &frequency); |
800 | if (frequency == 0) |
801 | return false; |
802 | } |
803 | else |
804 | ResetToneAndVoltage(); |
805 | lastDiseqc = diseqc; |
806 | lastSource = channel.Source(); |
807 | } |
808 | } |
809 | else { |
810 | esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number())void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: no DiSEqC parameters found for channel %d" , channel.Number()) : void() ); |
811 | return false; |
812 | } |
813 | } |
814 | else { |
815 | int tone = SEC_TONE_OFF; |
816 | if (frequency < (unsigned int)Setup.LnbSLOF) { |
817 | frequency -= Setup.LnbFrequLo; |
818 | tone = SEC_TONE_OFF; |
819 | } |
820 | else { |
821 | frequency -= Setup.LnbFrequHi; |
822 | tone = SEC_TONE_ON; |
823 | } |
824 | int volt = (dtp.Polarization() == 'V' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; |
825 | if (!IsBondedMaster()) { |
826 | tone = SEC_TONE_OFF; |
827 | volt = SEC_VOLTAGE_13; |
828 | } |
829 | CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((67)) << 0) | ((0) << ((0 +8)+8))), volt)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid (3, "ERROR (%s,%d): %m", "dvbdevice.c", 829) : void() ); }; |
830 | CHECK(ioctl(fd_frontend, FE_SET_TONE, tone)){ if ((ioctl(fd_frontend, (((0U) << (((0 +8)+8)+14)) | ( (('o')) << (0 +8)) | (((66)) << 0) | ((0) << ((0 +8)+8))), tone)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid (3, "ERROR (%s,%d): %m", "dvbdevice.c", 830) : void() ); }; |
831 | } |
832 | frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF |
833 | |
834 | // DVB-S/DVB-S2 (common parts) |
835 | SETCMD(DTV_FREQUENCY, frequency * 1000UL){ Frontend[CmdSeq.num].cmd = (3); Frontend[CmdSeq.num].u.data = (frequency * 1000UL); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
836 | SETCMD(DTV_MODULATION, dtp.Modulation()){ Frontend[CmdSeq.num].cmd = (4); Frontend[CmdSeq.num].u.data = (dtp.Modulation()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
837 | SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL){ Frontend[CmdSeq.num].cmd = (8); Frontend[CmdSeq.num].u.data = (channel.Srate() * 1000UL); if (CmdSeq.num++ > 16) { void ( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
838 | SETCMD(DTV_INNER_FEC, dtp.CoderateH()){ Frontend[CmdSeq.num].cmd = (9); Frontend[CmdSeq.num].u.data = (dtp.CoderateH()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
839 | SETCMD(DTV_INVERSION, dtp.Inversion()){ Frontend[CmdSeq.num].cmd = (6); Frontend[CmdSeq.num].u.data = (dtp.Inversion()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
840 | if (frontendType == SYS_DVBS2) { |
841 | // DVB-S2 |
842 | SETCMD(DTV_PILOT, PILOT_AUTO){ Frontend[CmdSeq.num].cmd = (12); Frontend[CmdSeq.num].u.data = (PILOT_AUTO); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
843 | SETCMD(DTV_ROLLOFF, dtp.RollOff()){ Frontend[CmdSeq.num].cmd = (13); Frontend[CmdSeq.num].u.data = (dtp.RollOff()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
844 | if (DvbApiVersion >= 0x0508) |
845 | SETCMD(DTV_STREAM_ID, dtp.StreamId()){ Frontend[CmdSeq.num].cmd = (42); Frontend[CmdSeq.num].u.data = (dtp.StreamId()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
846 | } |
847 | else { |
848 | // DVB-S |
849 | SETCMD(DTV_ROLLOFF, ROLLOFF_35){ Frontend[CmdSeq.num].cmd = (13); Frontend[CmdSeq.num].u.data = (ROLLOFF_35); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; // DVB-S always has a ROLLOFF of 0.35 |
850 | } |
851 | |
852 | tuneTimeout = DVBS_TUNE_TIMEOUT9000; |
853 | lockTimeout = DVBS_LOCK_TIMEOUT2000; |
854 | } |
855 | else if (frontendType == SYS_DVBC_ANNEX_ACSYS_DVBC_ANNEX_A || frontendType == SYS_DVBC_ANNEX_B) { |
856 | // DVB-C |
857 | SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())){ Frontend[CmdSeq.num].cmd = (3); Frontend[CmdSeq.num].u.data = (FrequencyToHz(channel.Frequency())); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
858 | SETCMD(DTV_INVERSION, dtp.Inversion()){ Frontend[CmdSeq.num].cmd = (6); Frontend[CmdSeq.num].u.data = (dtp.Inversion()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
859 | SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL){ Frontend[CmdSeq.num].cmd = (8); Frontend[CmdSeq.num].u.data = (channel.Srate() * 1000UL); if (CmdSeq.num++ > 16) { void ( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
860 | SETCMD(DTV_INNER_FEC, dtp.CoderateH()){ Frontend[CmdSeq.num].cmd = (9); Frontend[CmdSeq.num].u.data = (dtp.CoderateH()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
861 | SETCMD(DTV_MODULATION, dtp.Modulation()){ Frontend[CmdSeq.num].cmd = (4); Frontend[CmdSeq.num].u.data = (dtp.Modulation()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
862 | |
863 | tuneTimeout = DVBC_TUNE_TIMEOUT9000; |
864 | lockTimeout = DVBC_LOCK_TIMEOUT2000; |
865 | } |
866 | else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) { |
867 | // DVB-T/DVB-T2 (common parts) |
868 | SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())){ Frontend[CmdSeq.num].cmd = (3); Frontend[CmdSeq.num].u.data = (FrequencyToHz(channel.Frequency())); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
869 | SETCMD(DTV_INVERSION, dtp.Inversion()){ Frontend[CmdSeq.num].cmd = (6); Frontend[CmdSeq.num].u.data = (dtp.Inversion()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
870 | SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth()){ Frontend[CmdSeq.num].cmd = (5); Frontend[CmdSeq.num].u.data = (dtp.Bandwidth()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
871 | SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH()){ Frontend[CmdSeq.num].cmd = (36); Frontend[CmdSeq.num].u.data = (dtp.CoderateH()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
872 | SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL()){ Frontend[CmdSeq.num].cmd = (37); Frontend[CmdSeq.num].u.data = (dtp.CoderateL()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
873 | SETCMD(DTV_MODULATION, dtp.Modulation()){ Frontend[CmdSeq.num].cmd = (4); Frontend[CmdSeq.num].u.data = (dtp.Modulation()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
874 | SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission()){ Frontend[CmdSeq.num].cmd = (39); Frontend[CmdSeq.num].u.data = (dtp.Transmission()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
875 | SETCMD(DTV_GUARD_INTERVAL, dtp.Guard()){ Frontend[CmdSeq.num].cmd = (38); Frontend[CmdSeq.num].u.data = (dtp.Guard()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
876 | SETCMD(DTV_HIERARCHY, dtp.Hierarchy()){ Frontend[CmdSeq.num].cmd = (40); Frontend[CmdSeq.num].u.data = (dtp.Hierarchy()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
877 | if (frontendType == SYS_DVBT2) { |
878 | // DVB-T2 |
879 | if (DvbApiVersion >= 0x0508) { |
880 | SETCMD(DTV_STREAM_ID, dtp.StreamId()){ Frontend[CmdSeq.num].cmd = (42); Frontend[CmdSeq.num].u.data = (dtp.StreamId()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
881 | } |
882 | else if (DvbApiVersion >= 0x0503) |
883 | SETCMD(DTV_DVBT2_PLP_ID_LEGACY, dtp.StreamId()){ Frontend[CmdSeq.num].cmd = (43); Frontend[CmdSeq.num].u.data = (dtp.StreamId()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
884 | } |
885 | |
886 | tuneTimeout = DVBT_TUNE_TIMEOUT9000; |
887 | lockTimeout = DVBT_LOCK_TIMEOUT2000; |
888 | } |
889 | else if (frontendType == SYS_ATSC) { |
890 | // ATSC |
891 | SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())){ Frontend[CmdSeq.num].cmd = (3); Frontend[CmdSeq.num].u.data = (FrequencyToHz(channel.Frequency())); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
892 | SETCMD(DTV_INVERSION, dtp.Inversion()){ Frontend[CmdSeq.num].cmd = (6); Frontend[CmdSeq.num].u.data = (dtp.Inversion()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
893 | SETCMD(DTV_MODULATION, dtp.Modulation()){ Frontend[CmdSeq.num].cmd = (4); Frontend[CmdSeq.num].u.data = (dtp.Modulation()); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
894 | |
895 | tuneTimeout = ATSC_TUNE_TIMEOUT9000; |
896 | lockTimeout = ATSC_LOCK_TIMEOUT2000; |
897 | } |
898 | else { |
899 | esyslog("ERROR: attempt to set channel with unknown DVB frontend type")void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: attempt to set channel with unknown DVB frontend type" ) : void() ); |
900 | return false; |
901 | } |
902 | SETCMD(DTV_TUNE, 0){ Frontend[CmdSeq.num].cmd = (1); Frontend[CmdSeq.num].u.data = (0); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
903 | if (ioctl(fd_frontend, FE_SET_PROPERTY(((1U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((82)) << 0) | ((((sizeof(struct dtv_properties)))) << ((0 +8)+8))), &CmdSeq) < 0) { |
904 | esyslog("ERROR: frontend %d/%d: %m", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: frontend %d/%d: %m" , adapter, frontend) : void() ); |
905 | return false; |
906 | } |
907 | return true; |
908 | } |
909 | |
910 | void cDvbTuner::Action(void) |
911 | { |
912 | cTimeMs Timer; |
913 | bool LostLock = false; |
914 | fe_status_t Status = (fe_status_t)0; |
915 | while (Running()) { |
916 | fe_status_t NewStatus; |
917 | if (GetFrontendStatus(NewStatus)) |
918 | Status = NewStatus; |
919 | cMutexLock MutexLock(&mutex); |
920 | int WaitTime = 1000; |
921 | switch (tunerStatus) { |
922 | case tsIdle: |
923 | break; // we want the TimedWait() below! |
924 | case tsSet: |
925 | tunerStatus = SetFrontend() ? tsPositioning : tsIdle; |
926 | continue; |
927 | case tsPositioning: |
928 | if (positioner) { |
929 | if (positioner->IsMoving()) |
930 | break; // we want the TimedWait() below! |
931 | else if (diseqcOffset) { |
932 | lastDiseqc = NULL__null; |
933 | tunerStatus = tsSet; // have it process the rest of the DiSEqC sequence |
934 | continue; |
935 | } |
936 | } |
937 | tunerStatus = tsTuned; |
938 | Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT500 : 0)); |
939 | if (positioner) |
940 | continue; |
941 | // otherwise run directly into tsTuned... |
942 | case tsTuned: |
943 | if (Timer.TimedOut()) { |
944 | tunerStatus = tsSet; |
945 | lastDiseqc = NULL__null; |
946 | lastSource = 0; |
947 | if (time(NULL__null) - lastTimeoutReport > 60) { // let's not get too many of these |
948 | isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder())void( (SysLogLevel > 1) ? syslog_with_tid(6, "frontend %d/%d timed out while tuning to channel %d, tp %d" , adapter, frontend, channel.Number(), channel.Transponder()) : void() ); |
949 | lastTimeoutReport = time(NULL__null); |
950 | } |
951 | continue; |
952 | } |
953 | WaitTime = 100; // allows for a quick change from tsTuned to tsLocked |
954 | // run into tsLocked... |
955 | case tsLocked: |
956 | if (Status & FE_REINIT) { |
957 | tunerStatus = tsSet; |
958 | lastDiseqc = NULL__null; |
959 | lastSource = 0; |
960 | isyslog("frontend %d/%d was reinitialized", adapter, frontend)void( (SysLogLevel > 1) ? syslog_with_tid(6, "frontend %d/%d was reinitialized" , adapter, frontend) : void() ); |
961 | lastTimeoutReport = 0; |
962 | continue; |
963 | } |
964 | else if (Status & FE_HAS_LOCK) { |
965 | if (LostLock) { |
966 | isyslog("frontend %d/%d regained lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder())void( (SysLogLevel > 1) ? syslog_with_tid(6, "frontend %d/%d regained lock on channel %d, tp %d" , adapter, frontend, channel.Number(), channel.Transponder()) : void() ); |
967 | LostLock = false; |
968 | } |
969 | tunerStatus = tsLocked; |
970 | locked.Broadcast(); |
971 | lastTimeoutReport = 0; |
972 | } |
973 | else if (tunerStatus == tsLocked) { |
974 | LostLock = true; |
975 | isyslog("frontend %d/%d lost lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder())void( (SysLogLevel > 1) ? syslog_with_tid(6, "frontend %d/%d lost lock on channel %d, tp %d" , adapter, frontend, channel.Number(), channel.Transponder()) : void() ); |
976 | tunerStatus = tsTuned; |
977 | Timer.Set(lockTimeout); |
978 | lastTimeoutReport = 0; |
979 | continue; |
980 | } |
981 | break; |
982 | default: esyslog("ERROR: unknown tuner status %d", tunerStatus)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown tuner status %d" , tunerStatus) : void() ); |
983 | } |
984 | newSet.TimedWait(mutex, WaitTime); |
985 | } |
986 | } |
987 | |
988 | // --- cDvbSourceParam ------------------------------------------------------- |
989 | |
990 | class cDvbSourceParam : public cSourceParam { |
991 | private: |
992 | int param; |
993 | int srate; |
994 | cDvbTransponderParameters dtp; |
995 | public: |
996 | cDvbSourceParam(char Source, const char *Description); |
997 | virtual void SetData(cChannel *Channel); |
998 | virtual void GetData(cChannel *Channel); |
999 | virtual cOsdItem *GetOsdItem(void); |
1000 | }; |
1001 | |
1002 | cDvbSourceParam::cDvbSourceParam(char Source, const char *Description) |
1003 | :cSourceParam(Source, Description) |
1004 | { |
1005 | param = 0; |
1006 | srate = 0; |
1007 | } |
1008 | |
1009 | void cDvbSourceParam::SetData(cChannel *Channel) |
1010 | { |
1011 | srate = Channel->Srate(); |
1012 | dtp.Parse(Channel->Parameters()); |
1013 | param = 0; |
1014 | } |
1015 | |
1016 | void cDvbSourceParam::GetData(cChannel *Channel) |
1017 | { |
1018 | Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), srate, dtp.ToString(Source()), true); |
1019 | } |
1020 | |
1021 | cOsdItem *cDvbSourceParam::GetOsdItem(void) |
1022 | { |
1023 | char type = Source(); |
1024 | const tDvbParameterMap *SystemValues = type == 'S' ? SystemValuesSat : SystemValuesTerr; |
1025 | #undef ST |
1026 | #define ST(s)if (strchr(s, type)) if (strchr(s, type)) |
1027 | switch (param++) { |
1028 | case 0: ST(" S ")if (strchr(" S ", type)) return new cMenuEditChrItem( tr("Polarization")I18nTranslate("Polarization"), &dtp.polarization, "HVLR"); else return GetOsdItem(); |
1029 | case 1: ST(" ST")if (strchr(" ST", type)) return new cMenuEditMapItem( tr("System")I18nTranslate("System"), &dtp.system, SystemValues); else return GetOsdItem(); |
1030 | case 2: ST(" CS ")if (strchr(" CS ", type)) return new cMenuEditIntItem( tr("Srate")I18nTranslate("Srate"), &srate); else return GetOsdItem(); |
1031 | case 3: ST("ACST")if (strchr("ACST", type)) return new cMenuEditMapItem( tr("Inversion")I18nTranslate("Inversion"), &dtp.inversion, InversionValues); else return GetOsdItem(); |
1032 | case 4: ST(" CST")if (strchr(" CST", type)) return new cMenuEditMapItem( tr("CoderateH")I18nTranslate("CoderateH"), &dtp.coderateH, CoderateValues); else return GetOsdItem(); |
1033 | case 5: ST(" T")if (strchr(" T", type)) return new cMenuEditMapItem( tr("CoderateL")I18nTranslate("CoderateL"), &dtp.coderateL, CoderateValues); else return GetOsdItem(); |
1034 | case 6: ST("ACST")if (strchr("ACST", type)) return new cMenuEditMapItem( tr("Modulation")I18nTranslate("Modulation"), &dtp.modulation, ModulationValues); else return GetOsdItem(); |
1035 | case 7: ST(" T")if (strchr(" T", type)) return new cMenuEditMapItem( tr("Bandwidth")I18nTranslate("Bandwidth"), &dtp.bandwidth, BandwidthValues); else return GetOsdItem(); |
1036 | case 8: ST(" T")if (strchr(" T", type)) return new cMenuEditMapItem( tr("Transmission")I18nTranslate("Transmission"), &dtp.transmission, TransmissionValues); else return GetOsdItem(); |
1037 | case 9: ST(" T")if (strchr(" T", type)) return new cMenuEditMapItem( tr("Guard")I18nTranslate("Guard"), &dtp.guard, GuardValues); else return GetOsdItem(); |
1038 | case 10: ST(" T")if (strchr(" T", type)) return new cMenuEditMapItem( tr("Hierarchy")I18nTranslate("Hierarchy"), &dtp.hierarchy, HierarchyValues); else return GetOsdItem(); |
1039 | case 11: ST(" S ")if (strchr(" S ", type)) return new cMenuEditMapItem( tr("Rolloff")I18nTranslate("Rolloff"), &dtp.rollOff, RollOffValues); else return GetOsdItem(); |
1040 | case 12: ST(" ST")if (strchr(" ST", type)) return new cMenuEditIntItem( tr("StreamId")I18nTranslate("StreamId"), &dtp.streamId, 0, 255); else return GetOsdItem(); |
1041 | default: return NULL__null; |
1042 | } |
1043 | return NULL__null; |
1044 | } |
1045 | |
1046 | // --- cDvbDevice ------------------------------------------------------------ |
1047 | |
1048 | int cDvbDevice::setTransferModeForDolbyDigital = 1; |
1049 | cMutex cDvbDevice::bondMutex; |
1050 | |
1051 | const char *DeliverySystemNames[] = { |
1052 | "", |
1053 | "DVB-C", |
1054 | "DVB-C", |
1055 | "DVB-T", |
1056 | "DSS", |
1057 | "DVB-S", |
1058 | "DVB-S2", |
1059 | "DVB-H", |
1060 | "ISDBT", |
1061 | "ISDBS", |
1062 | "ISDBC", |
1063 | "ATSC", |
1064 | "ATSCMH", |
1065 | "DMBTH", |
1066 | "CMMB", |
1067 | "DAB", |
1068 | "DVB-T2", |
1069 | "TURBO", |
1070 | NULL__null |
1071 | }; |
1072 | |
1073 | cDvbDevice::cDvbDevice(int Adapter, int Frontend) |
1074 | { |
1075 | adapter = Adapter; |
1076 | frontend = Frontend; |
1077 | ciAdapter = NULL__null; |
1078 | dvbTuner = NULL__null; |
1079 | numDeliverySystems = 0; |
1080 | numModulations = 0; |
1081 | bondedDevice = NULL__null; |
1082 | needsDetachBondedReceivers = false; |
1083 | tsBuffer = NULL__null; |
1084 | |
1085 | // Devices that are present on all card types: |
1086 | |
1087 | int fd_frontend = DvbOpen(DEV_DVB_FRONTEND"frontend", adapter, frontend, O_RDWR02 | O_NONBLOCK04000); |
1088 | |
1089 | // Common Interface: |
1090 | |
1091 | fd_ca = DvbOpen(DEV_DVB_CA"ca", adapter, frontend, O_RDWR02); |
1092 | if (fd_ca >= 0) |
1093 | ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); |
1094 | |
1095 | // The DVR device (will be opened and closed as needed): |
1096 | |
1097 | fd_dvr = -1; |
1098 | |
1099 | // We only check the devices that must be present - the others will be checked before accessing them://XXX |
1100 | |
1101 | if (fd_frontend >= 0) { |
1102 | if (QueryDeliverySystems(fd_frontend)) |
1103 | dvbTuner = new cDvbTuner(this, fd_frontend, adapter, frontend); |
1104 | } |
1105 | else |
1106 | esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't open DVB device %d/%d" , adapter, frontend) : void() ); |
1107 | |
1108 | StartSectionHandler(); |
1109 | } |
1110 | |
1111 | cDvbDevice::~cDvbDevice() |
1112 | { |
1113 | StopSectionHandler(); |
1114 | delete dvbTuner; |
1115 | delete ciAdapter; |
1116 | UnBond(); |
1117 | // We're not explicitly closing any device files here, since this sometimes |
1118 | // caused segfaults. Besides, the program is about to terminate anyway... |
1119 | } |
1120 | |
1121 | cString cDvbDevice::DvbName(const char *Name, int Adapter, int Frontend) |
1122 | { |
1123 | return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE"/dev/dvb", DEV_DVB_ADAPTER"adapter", Adapter, Name, Frontend); |
1124 | } |
1125 | |
1126 | int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError) |
1127 | { |
1128 | cString FileName = DvbName(Name, Adapter, Frontend); |
1129 | int fd = open(FileName, Mode); |
1130 | if (fd < 0 && ReportError) |
1131 | LOG_ERROR_STR(*FileName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %s: %m" , "dvbdevice.c", 1131, *FileName) : void() ); |
1132 | return fd; |
1133 | } |
1134 | |
1135 | bool cDvbDevice::Exists(int Adapter, int Frontend) |
1136 | { |
1137 | cString FileName = DvbName(DEV_DVB_FRONTEND"frontend", Adapter, Frontend); |
1138 | if (access(FileName, F_OK0) == 0) { |
1139 | int f = open(FileName, O_RDONLY00); |
1140 | if (f >= 0) { |
1141 | close(f); |
1142 | return true; |
1143 | } |
1144 | else if (errno(*__errno_location ()) != ENODEV19 && errno(*__errno_location ()) != EINVAL22) |
1145 | LOG_ERROR_STR(*FileName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %s: %m" , "dvbdevice.c", 1145, *FileName) : void() ); |
1146 | } |
1147 | else if (errno(*__errno_location ()) != ENOENT2) |
1148 | LOG_ERROR_STR(*FileName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %s: %m" , "dvbdevice.c", 1148, *FileName) : void() ); |
1149 | return false; |
1150 | } |
1151 | |
1152 | bool cDvbDevice::Probe(int Adapter, int Frontend) |
1153 | { |
1154 | cString FileName = DvbName(DEV_DVB_FRONTEND"frontend", Adapter, Frontend); |
1155 | dsyslog("probing %s", *FileName)void( (SysLogLevel > 2) ? syslog_with_tid(7, "probing %s", *FileName) : void() ); |
1156 | for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) { |
1157 | if (dp->Probe(Adapter, Frontend)) |
1158 | return true; // a plugin has created the actual device |
1159 | } |
1160 | dsyslog("creating cDvbDevice")void( (SysLogLevel > 2) ? syslog_with_tid(7, "creating cDvbDevice" ) : void() ); |
1161 | new cDvbDevice(Adapter, Frontend); // it's a "budget" device |
1162 | return true; |
1163 | } |
1164 | |
1165 | cString cDvbDevice::DeviceType(void) const |
1166 | { |
1167 | if (dvbTuner) { |
1168 | if (dvbTuner->FrontendType() != SYS_UNDEFINED) |
1169 | return DeliverySystemNames[dvbTuner->FrontendType()]; |
1170 | if (numDeliverySystems) |
1171 | return DeliverySystemNames[deliverySystems[0]]; // to have some reasonable default |
1172 | } |
1173 | return ""; |
1174 | } |
1175 | |
1176 | cString cDvbDevice::DeviceName(void) const |
1177 | { |
1178 | return frontendInfo.name; |
1179 | } |
1180 | |
1181 | bool cDvbDevice::Initialize(void) |
1182 | { |
1183 | new cDvbSourceParam('A', "ATSC"); |
1184 | new cDvbSourceParam('C', "DVB-C"); |
1185 | new cDvbSourceParam('S', "DVB-S"); |
1186 | new cDvbSourceParam('T', "DVB-T"); |
1187 | cStringList Nodes; |
1188 | cReadDir DvbDir(DEV_DVB_BASE"/dev/dvb"); |
1189 | if (DvbDir.Ok()) { |
1190 | struct dirent *a; |
1191 | while ((a = DvbDir.Next()) != NULL__null) { |
1192 | if (strstr(a->d_name, DEV_DVB_ADAPTER"adapter") == a->d_name) { |
1193 | int Adapter = strtol(a->d_name + strlen(DEV_DVB_ADAPTER"adapter"), NULL__null, 10); |
1194 | cReadDir AdapterDir(AddDirectory(DEV_DVB_BASE"/dev/dvb", a->d_name)); |
1195 | if (AdapterDir.Ok()) { |
1196 | struct dirent *f; |
1197 | while ((f = AdapterDir.Next()) != NULL__null) { |
1198 | if (strstr(f->d_name, DEV_DVB_FRONTEND"frontend") == f->d_name) { |
1199 | int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND"frontend"), NULL__null, 10); |
1200 | Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend))); |
1201 | } |
1202 | } |
1203 | } |
1204 | } |
1205 | } |
1206 | } |
1207 | int Found = 0; |
1208 | int Used = 0; |
1209 | if (Nodes.Size() > 0) { |
1210 | Nodes.Sort(); |
1211 | for (int i = 0; i < Nodes.Size(); i++) { |
1212 | int Adapter; |
1213 | int Frontend; |
1214 | if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) { |
1215 | if (Exists(Adapter, Frontend)) { |
1216 | if (Found < MAXDEVICES16) { |
1217 | Found++; |
1218 | if (UseDevice(NextCardIndex())) { |
1219 | if (Probe(Adapter, Frontend)) |
1220 | Used++; |
1221 | } |
1222 | else |
1223 | NextCardIndex(1); // skips this one |
1224 | } |
1225 | } |
1226 | } |
1227 | } |
1228 | } |
1229 | if (Found > 0) { |
1230 | isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : "")void( (SysLogLevel > 1) ? syslog_with_tid(6, "found %d DVB device%s" , Found, Found > 1 ? "s" : "") : void() ); |
1231 | if (Used != Found) |
1232 | isyslog("using only %d DVB device%s", Used, Used > 1 ? "s" : "")void( (SysLogLevel > 1) ? syslog_with_tid(6, "using only %d DVB device%s" , Used, Used > 1 ? "s" : "") : void() ); |
1233 | } |
1234 | else |
1235 | isyslog("no DVB device found")void( (SysLogLevel > 1) ? syslog_with_tid(6, "no DVB device found" ) : void() ); |
1236 | return Found > 0; |
1237 | } |
1238 | |
1239 | bool cDvbDevice::QueryDeliverySystems(int fd_frontend) |
1240 | { |
1241 | numDeliverySystems = 0; |
1242 | if (ioctl(fd_frontend, FE_GET_INFO(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((61)) << 0) | ((((sizeof(struct dvb_frontend_info))) ) << ((0 +8)+8))), &frontendInfo) < 0) { |
1243 | LOG_ERRORvoid( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 1243) : void() ); |
1244 | return false; |
1245 | } |
1246 | dtv_property Frontend[1]; |
1247 | dtv_properties CmdSeq; |
1248 | // Determine the version of the running DVB API: |
1249 | if (!DvbApiVersion) { |
1250 | memset(&Frontend, 0, sizeof(Frontend)); |
1251 | memset(&CmdSeq, 0, sizeof(CmdSeq)); |
1252 | CmdSeq.props = Frontend; |
1253 | SETCMD(DTV_API_VERSION, 0){ Frontend[CmdSeq.num].cmd = (35); Frontend[CmdSeq.num].u.data = (0); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
1254 | if (ioctl(fd_frontend, FE_GET_PROPERTY(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((83)) << 0) | ((((sizeof(struct dtv_properties)))) << ((0 +8)+8))), &CmdSeq) != 0) { |
1255 | LOG_ERRORvoid( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 1255) : void() ); |
1256 | return false; |
1257 | } |
1258 | DvbApiVersion = Frontend[0].u.data; |
1259 | isyslog("DVB API version is 0x%04X (VDR was built with 0x%04X)", DvbApiVersion, DVBAPIVERSION)void( (SysLogLevel > 1) ? syslog_with_tid(6, "DVB API version is 0x%04X (VDR was built with 0x%04X)" , DvbApiVersion, (5 << 8 | 10)) : void() ); |
1260 | } |
1261 | // Determine the types of delivery systems this device provides: |
1262 | bool LegacyMode = true; |
1263 | if (DvbApiVersion >= 0x0505) { |
1264 | memset(&Frontend, 0, sizeof(Frontend)); |
1265 | memset(&CmdSeq, 0, sizeof(CmdSeq)); |
1266 | CmdSeq.props = Frontend; |
1267 | SETCMD(DTV_ENUM_DELSYS, 0){ Frontend[CmdSeq.num].cmd = (44); Frontend[CmdSeq.num].u.data = (0); if (CmdSeq.num++ > 16) { void( (SysLogLevel > 0 ) ? syslog_with_tid(3, "ERROR: too many tuning commands on frontend %d/%d" , adapter, frontend) : void() ); return false; } }; |
1268 | int Result = ioctl(fd_frontend, FE_GET_PROPERTY(((2U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((83)) << 0) | ((((sizeof(struct dtv_properties)))) << ((0 +8)+8))), &CmdSeq); |
1269 | if (Result == 0) { |
1270 | for (uint i = 0; i < Frontend[0].u.buffer.len; i++) { |
1271 | if (numDeliverySystems >= MAXDELIVERYSYSTEMS8) { |
1272 | esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: too many delivery systems on frontend %d/%d" , adapter, frontend) : void() ); |
1273 | break; |
1274 | } |
1275 | deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i]; |
1276 | } |
1277 | LegacyMode = false; |
1278 | } |
1279 | else { |
1280 | esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode" , adapter, frontend) : void() ); |
1281 | } |
1282 | } |
1283 | if (LegacyMode) { |
1284 | // Legacy mode (DVB-API < 5.5): |
1285 | switch (frontendInfo.type) { |
1286 | case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS; |
1287 | if (frontendInfo.caps & FE_CAN_2G_MODULATION) |
1288 | deliverySystems[numDeliverySystems++] = SYS_DVBS2; |
1289 | break; |
1290 | case FE_OFDM: deliverySystems[numDeliverySystems++] = SYS_DVBT; |
1291 | if (frontendInfo.caps & FE_CAN_2G_MODULATION) |
1292 | deliverySystems[numDeliverySystems++] = SYS_DVBT2; |
1293 | break; |
1294 | case FE_QAM: deliverySystems[numDeliverySystems++] = SYS_DVBC_ANNEX_ACSYS_DVBC_ANNEX_A; break; |
1295 | case FE_ATSC: deliverySystems[numDeliverySystems++] = SYS_ATSC; break; |
1296 | default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown frontend type %d on frontend %d/%d" , frontendInfo.type, adapter, frontend) : void() ); |
1297 | } |
1298 | } |
1299 | if (numDeliverySystems > 0) { |
1300 | cString ds(""); |
1301 | for (int i = 0; i < numDeliverySystems; i++) |
1302 | ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", DeliverySystemNames[deliverySystems[i]]); |
1303 | cString ms(""); |
1304 | if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); } |
1305 | if (frontendInfo.caps & FE_CAN_QAM_16) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_16, ModulationValues)); } |
1306 | if (frontendInfo.caps & FE_CAN_QAM_32) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_32, ModulationValues)); } |
1307 | if (frontendInfo.caps & FE_CAN_QAM_64) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_64, ModulationValues)); } |
1308 | if (frontendInfo.caps & FE_CAN_QAM_128) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_128, ModulationValues)); } |
1309 | if (frontendInfo.caps & FE_CAN_QAM_256) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_256, ModulationValues)); } |
1310 | if (frontendInfo.caps & FE_CAN_8VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_8, ModulationValues)); } |
1311 | if (frontendInfo.caps & FE_CAN_16VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_16, ModulationValues)); } |
1312 | if (frontendInfo.caps & FE_CAN_TURBO_FEC) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", "TURBO_FEC"); } |
1313 | if (!**ms) |
1314 | ms = "unknown modulations"; |
1315 | isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, *ds, *ms, frontendInfo.name)void( (SysLogLevel > 1) ? syslog_with_tid(6, "frontend %d/%d provides %s with %s (\"%s\")" , adapter, frontend, *ds, *ms, frontendInfo.name) : void() ); |
1316 | return true; |
1317 | } |
1318 | else |
1319 | esyslog("ERROR: frontend %d/%d doesn't provide any delivery systems", adapter, frontend)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: frontend %d/%d doesn't provide any delivery systems" , adapter, frontend) : void() ); |
1320 | return false; |
1321 | } |
1322 | |
1323 | bool cDvbDevice::BondDevices(const char *Bondings) |
1324 | { |
1325 | UnBondDevices(); |
1326 | if (Bondings) { |
1327 | cSatCableNumbers SatCableNumbers(MAXDEVICES16, Bondings); |
1328 | for (int i = 0; i < cDevice::NumDevices(); i++) { |
1329 | int d = SatCableNumbers.FirstDeviceIndex(i); |
1330 | if (d >= 0) { |
1331 | int ErrorDevice = 0; |
1332 | if (cDevice *Device1 = cDevice::GetDevice(i)) { |
1333 | if (cDevice *Device2 = cDevice::GetDevice(d)) { |
1334 | if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) { |
1335 | if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) { |
1336 | if (!DvbDevice1->Bond(DvbDevice2)) |
1337 | return false; // Bond() has already logged the error |
1338 | } |
1339 | else |
1340 | ErrorDevice = d + 1; |
1341 | } |
1342 | else |
1343 | ErrorDevice = i + 1; |
1344 | if (ErrorDevice) { |
1345 | esyslog("ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: device '%d' in device bondings '%s' is not a cDvbDevice" , ErrorDevice, Bondings) : void() ); |
1346 | return false; |
1347 | } |
1348 | } |
1349 | else |
1350 | ErrorDevice = d + 1; |
1351 | } |
1352 | else |
1353 | ErrorDevice = i + 1; |
1354 | if (ErrorDevice) { |
1355 | esyslog("ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: unknown device '%d' in device bondings '%s'" , ErrorDevice, Bondings) : void() ); |
1356 | return false; |
1357 | } |
1358 | } |
1359 | } |
1360 | } |
1361 | return true; |
1362 | } |
1363 | |
1364 | void cDvbDevice::UnBondDevices(void) |
1365 | { |
1366 | for (int i = 0; i < cDevice::NumDevices(); i++) { |
1367 | if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i))) |
1368 | d->UnBond(); |
1369 | } |
1370 | } |
1371 | |
1372 | bool cDvbDevice::Bond(cDvbDevice *Device) |
1373 | { |
1374 | cMutexLock MutexLock(&bondMutex); |
1375 | if (!bondedDevice) { |
1376 | if (Device != this) { |
1377 | if ((ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) && (Device->ProvidesDeliverySystem(SYS_DVBS) || Device->ProvidesDeliverySystem(SYS_DVBS2))) { |
1378 | if (dvbTuner && Device->dvbTuner && dvbTuner->Bond(Device->dvbTuner)) { |
1379 | bondedDevice = Device->bondedDevice ? Device->bondedDevice : Device; |
1380 | Device->bondedDevice = this; |
1381 | dsyslog("device %d bonded with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1)void( (SysLogLevel > 2) ? syslog_with_tid(7, "device %d bonded with device %d" , CardIndex() + 1, bondedDevice->CardIndex() + 1) : void() ); |
1382 | return true; |
1383 | } |
1384 | } |
1385 | else |
1386 | esyslog("ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)", CardIndex() + 1, Device->CardIndex() + 1)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)" , CardIndex() + 1, Device->CardIndex() + 1) : void() ); |
1387 | } |
1388 | else |
1389 | esyslog("ERROR: can't bond device %d with itself", CardIndex() + 1)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't bond device %d with itself" , CardIndex() + 1) : void() ); |
1390 | } |
1391 | else |
1392 | esyslog("ERROR: device %d already bonded with device %d, can't bond with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1, Device->CardIndex() + 1)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: device %d already bonded with device %d, can't bond with device %d" , CardIndex() + 1, bondedDevice->CardIndex() + 1, Device-> CardIndex() + 1) : void() ); |
1393 | return false; |
1394 | } |
1395 | |
1396 | void cDvbDevice::UnBond(void) |
1397 | { |
1398 | cMutexLock MutexLock(&bondMutex); |
1399 | if (cDvbDevice *d = bondedDevice) { |
1400 | if (dvbTuner) |
1401 | dvbTuner->UnBond(); |
1402 | dsyslog("device %d unbonded from device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1)void( (SysLogLevel > 2) ? syslog_with_tid(7, "device %d unbonded from device %d" , CardIndex() + 1, bondedDevice->CardIndex() + 1) : void() ); |
1403 | while (d->bondedDevice != this) |
1404 | d = d->bondedDevice; |
1405 | if (d == bondedDevice) |
1406 | d->bondedDevice = NULL__null; |
1407 | else |
1408 | d->bondedDevice = bondedDevice; |
1409 | bondedDevice = NULL__null; |
1410 | } |
1411 | } |
1412 | |
1413 | bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const |
1414 | { |
1415 | cMutexLock MutexLock(&bondMutex); |
1416 | if (bondedDevice || Positioner()) |
1417 | return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied); |
1418 | return true; |
1419 | } |
1420 | |
1421 | bool cDvbDevice::HasCi(void) |
1422 | { |
1423 | return ciAdapter; |
1424 | } |
1425 | |
1426 | bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On) |
1427 | { |
1428 | if (Handle->pid) { |
1429 | dmx_pes_filter_params pesFilterParams; |
1430 | memset(&pesFilterParams, 0, sizeof(pesFilterParams)); |
1431 | if (On) { |
1432 | if (Handle->handle < 0) { |
1433 | Handle->handle = DvbOpen(DEV_DVB_DEMUX"demux", adapter, frontend, O_RDWR02 | O_NONBLOCK04000, true); |
1434 | if (Handle->handle < 0) { |
1435 | LOG_ERRORvoid( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 1435) : void() ); |
1436 | return false; |
1437 | } |
1438 | } |
1439 | pesFilterParams.pid = Handle->pid; |
1440 | pesFilterParams.input = DMX_IN_FRONTEND; |
1441 | pesFilterParams.output = DMX_OUT_TS_TAP; |
1442 | pesFilterParams.pes_type= DMX_PES_OTHER; |
1443 | pesFilterParams.flags = DMX_IMMEDIATE_START4; |
1444 | if (ioctl(Handle->handle, DMX_SET_PES_FILTER(((1U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((44)) << 0) | ((((sizeof(struct dmx_pes_filter_params )))) << ((0 +8)+8))), &pesFilterParams) < 0) { |
1445 | LOG_ERRORvoid( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR (%s,%d): %m" , "dvbdevice.c", 1445) : void() ); |
1446 | return false; |
1447 | } |
1448 | } |
1449 | else if (!Handle->used) { |
1450 | CHECK(ioctl(Handle->handle, DMX_STOP)){ if ((ioctl(Handle->handle, (((0U) << (((0 +8)+8)+14 )) | ((('o')) << (0 +8)) | (((42)) << 0) | ((0) << ((0 +8)+8))))) < 0) void( (SysLogLevel > 0) ? syslog_with_tid (3, "ERROR (%s,%d): %m", "dvbdevice.c", 1450) : void() ); }; |
1451 | if (Type <= ptTeletext) { |
1452 | pesFilterParams.pid = 0x1FFF; |
1453 | pesFilterParams.input = DMX_IN_FRONTEND; |
1454 | pesFilterParams.output = DMX_OUT_DECODER; |
1455 | pesFilterParams.pes_type= DMX_PES_OTHER; |
1456 | pesFilterParams.flags = DMX_IMMEDIATE_START4; |
1457 | CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams)){ if ((ioctl(Handle->handle, (((1U) << (((0 +8)+8)+14 )) | ((('o')) << (0 +8)) | (((44)) << 0) | ((((sizeof (struct dmx_pes_filter_params)))) << ((0 +8)+8))), & pesFilterParams)) < 0) void( (SysLogLevel > 0) ? syslog_with_tid (3, "ERROR (%s,%d): %m", "dvbdevice.c", 1457) : void() ); }; |
1458 | } |
1459 | close(Handle->handle); |
1460 | Handle->handle = -1; |
1461 | } |
1462 | } |
1463 | return true; |
1464 | } |
1465 | |
1466 | int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) |
1467 | { |
1468 | cString FileName = DvbName(DEV_DVB_DEMUX"demux", adapter, frontend); |
1469 | int f = open(FileName, O_RDWR02 | O_NONBLOCK04000); |
1470 | if (f >= 0) { |
1471 | dmx_sct_filter_params sctFilterParams; |
1472 | memset(&sctFilterParams, 0, sizeof(sctFilterParams)); |
1473 | sctFilterParams.pid = Pid; |
1474 | sctFilterParams.timeout = 0; |
1475 | sctFilterParams.flags = DMX_IMMEDIATE_START4; |
1476 | sctFilterParams.filter.filter[0] = Tid; |
1477 | sctFilterParams.filter.mask[0] = Mask; |
1478 | if (ioctl(f, DMX_SET_FILTER(((1U) << (((0 +8)+8)+14)) | ((('o')) << (0 +8)) | (((43)) << 0) | ((((sizeof(struct dmx_sct_filter_params )))) << ((0 +8)+8))), &sctFilterParams) >= 0) |
1479 | return f; |
1480 | else { |
1481 | esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m" , Pid, Tid, Mask) : void() ); |
1482 | close(f); |
1483 | } |
1484 | } |
1485 | else |
1486 | esyslog("ERROR: can't open filter handle on '%s'", *FileName)void( (SysLogLevel > 0) ? syslog_with_tid(3, "ERROR: can't open filter handle on '%s'" , *FileName) : void() ); |
1487 | return -1; |
1488 | } |
1489 | |
1490 | void cDvbDevice::CloseFilter(int Handle) |
1491 | { |
1492 | close(Handle); |
1493 | } |
1494 | |
1495 | bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const |
1496 | { |
1497 | for (int i = 0; i < numDeliverySystems; i++) { |
1498 | if (deliverySystems[i] == DeliverySystem) |
1499 | return true; |
1500 | } |
1501 | return false; |
1502 | } |
1503 | |
1504 | bool cDvbDevice::ProvidesSource(int Source) const |
1505 | { |
1506 | int type = Source & cSource::st_Mask; |
1507 | return type == cSource::stNone |
1508 | || type == cSource::stAtsc && ProvidesDeliverySystem(SYS_ATSC) |
1509 | || type == cSource::stCable && (ProvidesDeliverySystem(SYS_DVBC_ANNEX_ACSYS_DVBC_ANNEX_A) || ProvidesDeliverySystem(SYS_DVBC_ANNEX_B)) |
1510 | || type == cSource::stSat && (ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) |
1511 | || type == cSource::stTerr && (ProvidesDeliverySystem(SYS_DVBT) || ProvidesDeliverySystem(SYS_DVBT2)); |
1512 | } |
1513 | |
1514 | bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const |
1515 | { |
1516 | if (!ProvidesSource(Channel->Source())) |
1517 | return false; // doesn't provide source |
1518 | cDvbTransponderParameters dtp(Channel->Parameters()); |
1519 | if (!ProvidesDeliverySystem(GetRequiredDeliverySystem(Channel, &dtp)) || |
1520 | dtp.StreamId() != 0 && !(frontendInfo.caps & FE_CAN_MULTISTREAM) || |
1521 | dtp.Modulation() == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) || |
1522 | dtp.Modulation() == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) || |
1523 | dtp.Modulation() == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) || |
1524 | dtp.Modulation() == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) || |
1525 | dtp.Modulation() == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) || |
1526 | dtp.Modulation() == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) || |
1527 | dtp.Modulation() == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) || |
1528 | dtp.Modulation() == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) || |
1529 | dtp.Modulation() == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) || |
1530 | dtp.Modulation() == PSK_8 && !(frontendInfo.caps & FE_CAN_TURBO_FEC) && dtp.System() == SYS_DVBS) // "turbo fec" is a non standard FEC used by North American broadcasters - this is a best guess to determine this condition |
1531 | return false; // requires modulation system which frontend doesn't provide |
1532 | if (!cSource::IsSat(Channel->Source()) || |
1533 | (!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL__null))) |
1534 | return DeviceHooksProvidesTransponder(Channel); |
1535 | return false; |
1536 | } |
1537 | |
1538 | bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const |
1539 | { |
1540 | bool result = false; |
1541 | bool hasPriority = Priority == IDLEPRIORITY((-99) - 1) || Priority > this->Priority(); |
1542 | bool needsDetachReceivers = false; |
1543 | needsDetachBondedReceivers = false; |
1544 | |
1545 | if (dvbTuner && ProvidesTransponder(Channel)) { |
1546 | result = hasPriority; |
1547 | if (Priority > IDLEPRIORITY((-99) - 1)) { |
1548 | if (Receiving()) { |
1549 | if (dvbTuner->IsTunedTo(Channel)) { |
1550 | if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0)) || Channel->Dpid(0) && !HasPid(Channel->Dpid(0))) { |
1551 | if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN0x0100) { |
1552 | if (CamSlot()->CanDecrypt(Channel)) |
1553 | result = true; |
1554 | else |
1555 | needsDetachReceivers = true; |
1556 | } |
1557 | else |
1558 | result = true; |
1559 | } |
1560 | else |
1561 | result = true; |
1562 | } |
1563 | else |
1564 | needsDetachReceivers = Receiving(); |
1565 | } |
1566 | if (result) { |
1567 | cMutexLock MutexLock(&bondMutex); |
1568 | if (!BondingOk(Channel)) { |
1569 | // This device is bonded, so we need to check the priorities of the others: |
1570 | for (cDvbDevice *d = bondedDevice; d && d != this; d = d->bondedDevice) { |
1571 | if (d->Priority() >= Priority) { |
1572 | result = false; |
1573 | break; |
1574 | } |
1575 | needsDetachReceivers |= d->Receiving(); |
1576 | } |
1577 | needsDetachBondedReceivers = true; |
1578 | needsDetachReceivers |= Receiving(); |
1579 | } |
1580 | } |
1581 | } |
1582 | } |
1583 | if (NeedsDetachReceivers) |
1584 | *NeedsDetachReceivers = needsDetachReceivers; |
1585 | return result; |
1586 | } |
1587 | |
1588 | bool cDvbDevice::ProvidesEIT(void) const |
1589 | { |
1590 | return dvbTuner != NULL__null; |
1591 | } |
1592 | |
1593 | int cDvbDevice::NumProvidedSystems(void) const |
1594 | { |
1595 | return numDeliverySystems + numModulations; |
1596 | } |
1597 | |
1598 | const cPositioner *cDvbDevice::Positioner(void) const |
1599 | { |
1600 | return dvbTuner ? dvbTuner->Positioner() : NULL__null; |
1601 | } |
1602 | |
1603 | int cDvbDevice::SignalStrength(void) const |
1604 | { |
1605 | return dvbTuner ? dvbTuner->GetSignalStrength() : -1; |
1606 | } |
1607 | |
1608 | int cDvbDevice::SignalQuality(void) const |
1609 | { |
1610 | return dvbTuner ? dvbTuner->GetSignalQuality() : -1; |
1611 | } |
1612 | |
1613 | const cChannel *cDvbDevice::GetCurrentlyTunedTransponder(void) const |
1614 | { |
1615 | return dvbTuner ? dvbTuner->GetTransponder() : NULL__null; |
1616 | } |
1617 | |
1618 | bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel) const |
1619 | { |
1620 | return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false; |
1621 | } |
1622 | |
1623 | bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const |
1624 | { |
1625 | return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel); |
1626 | } |
1627 | |
1628 | bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) |
1629 | { |
1630 | if (dvbTuner) |
1631 | dvbTuner->SetChannel(Channel); |
1632 | return true; |
1633 | } |
1634 | |
1635 | bool cDvbDevice::HasLock(int TimeoutMs) const |
1636 | { |
1637 | return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false; |
1638 | } |
1639 | |
1640 | void cDvbDevice::SetTransferModeForDolbyDigital(int Mode) |
1641 | { |
1642 | setTransferModeForDolbyDigital = Mode; |
1643 | } |
1644 | |
1645 | bool cDvbDevice::OpenDvr(void) |
1646 | { |
1647 | CloseDvr(); |
1648 | fd_dvr = DvbOpen(DEV_DVB_DVR"dvr", adapter, frontend, O_RDONLY00 | O_NONBLOCK04000, true); |
1649 | if (fd_dvr >= 0) |
1650 | tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(5)((5) * 1024LL * 1024LL), CardIndex() + 1); |
1651 | return fd_dvr >= 0; |
1652 | } |
1653 | |
1654 | void cDvbDevice::CloseDvr(void) |
1655 | { |
1656 | if (fd_dvr >= 0) { |
1657 | delete tsBuffer; |
1658 | tsBuffer = NULL__null; |
1659 | close(fd_dvr); |
1660 | fd_dvr = -1; |
1661 | } |
1662 | } |
1663 | |
1664 | bool cDvbDevice::GetTSPacket(uchar *&Data) |
1665 | { |
1666 | if (tsBuffer) { |
1667 | if (cCamSlot *cs = CamSlot()) { |
1668 | if (cs->WantsTsData()) { |
1669 | int Available; |
1670 | Data = tsBuffer->Get(&Available); |
1671 | if (Data) { |
1672 | Data = cs->Decrypt(Data, Available); |
1673 | tsBuffer->Skip(Available); |
1674 | } |
1675 | return true; |
1676 | } |
1677 | } |
1678 | Data = tsBuffer->Get(); |
1679 | return true; |
1680 | } |
1681 | return false; |
1682 | } |
1683 | |
1684 | void cDvbDevice::DetachAllReceivers(void) |
1685 | { |
1686 | cMutexLock MutexLock(&bondMutex); |
1687 | cDvbDevice *d = this; |
1688 | do { |
1689 | d->cDevice::DetachAllReceivers(); |
1690 | d = d->bondedDevice; |
1691 | } while (d && d != this && needsDetachBondedReceivers); |
1692 | needsDetachBondedReceivers = false; |
1693 | } |
1694 | |
1695 | // --- cDvbDeviceProbe ------------------------------------------------------- |
1696 | |
1697 | cList<cDvbDeviceProbe> DvbDeviceProbes; |
1698 | |
1699 | cDvbDeviceProbe::cDvbDeviceProbe(void) |
1700 | { |
1701 | DvbDeviceProbes.Add(this); |
1702 | } |
1703 | |
1704 | cDvbDeviceProbe::~cDvbDeviceProbe() |
1705 | { |
1706 | DvbDeviceProbes.Del(this, false); |
1707 | } |
1708 | |
1709 | uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend) |
1710 | { |
1711 | uint32_t SubsystemId = 0; |
1712 | cString FileName = cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend); |
1713 | struct stat st; |
1714 | if (stat(FileName, &st) == 0) { |
1715 | cReadDir d("/sys/class/dvb"); |
1716 | if (d.Ok()) { |
1717 | struct dirent *e; |
1718 | while ((e = d.Next()) != NULL__null) { |
1719 | if (strstr(e->d_name, "frontend")) { |
1720 | FileName = cString::sprintf("/sys/class/dvb/%s/dev", e->d_name); |
1721 | if (FILE *f = fopen(FileName, "r")) { |
1722 | cReadLine ReadLine; |
1723 | char *s = ReadLine.Read(f); |
1724 | fclose(f); |
1725 | unsigned Major; |
1726 | unsigned Minor; |
1727 | if (s && 2 == sscanf(s, "%u:%u", &Major, &Minor)) { |
1728 | if (((Major << 8) | Minor) == st.st_rdev) { |
1729 | FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_vendor", e->d_name); |
1730 | if ((f = fopen(FileName, "r")) != NULL__null) { |
1731 | if (char *s = ReadLine.Read(f)) |
1732 | SubsystemId = strtoul(s, NULL__null, 0) << 16; |
1733 | fclose(f); |
1734 | } |
1735 | FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name); |
1736 | if ((f = fopen(FileName, "r")) != NULL__null) { |
1737 | if (char *s = ReadLine.Read(f)) |
1738 | SubsystemId |= strtoul(s, NULL__null, 0); |
1739 | fclose(f); |
1740 | } |
1741 | break; |
1742 | } |
1743 | } |
1744 | } |
1745 | } |
1746 | } |
1747 | } |
1748 | } |
1749 | return SubsystemId; |
1750 | } |