vdr
1.7.27
|
00001 /* 00002 * dvbdevice.c: The DVB device tuner interface 00003 * 00004 * See the main source file 'vdr.c' for copyright information and 00005 * how to reach the author. 00006 * 00007 * $Id: dvbdevice.c 2.69 2012/03/25 10:41:45 kls Exp $ 00008 */ 00009 00010 #include "dvbdevice.h" 00011 #include <ctype.h> 00012 #include <errno.h> 00013 #include <limits.h> 00014 #include <linux/dvb/dmx.h> 00015 #include <linux/dvb/frontend.h> 00016 #include <sys/ioctl.h> 00017 #include <sys/mman.h> 00018 #include "channels.h" 00019 #include "diseqc.h" 00020 #include "dvbci.h" 00021 #include "menuitems.h" 00022 #include "sourceparams.h" 00023 00024 #define FE_CAN_TURBO_FEC 0x8000000 // TODO: remove this once it is defined in the driver 00025 00026 #define DVBS_TUNE_TIMEOUT 9000 //ms 00027 #define DVBS_LOCK_TIMEOUT 2000 //ms 00028 #define DVBC_TUNE_TIMEOUT 9000 //ms 00029 #define DVBC_LOCK_TIMEOUT 2000 //ms 00030 #define DVBT_TUNE_TIMEOUT 9000 //ms 00031 #define DVBT_LOCK_TIMEOUT 2000 //ms 00032 #define ATSC_TUNE_TIMEOUT 9000 //ms 00033 #define ATSC_LOCK_TIMEOUT 2000 //ms 00034 00035 #define SCR_RANDOM_TIMEOUT 500 // ms (add random value up to this when tuning SCR device to avoid lockups) 00036 00037 // --- DVB Parameter Maps ---------------------------------------------------- 00038 00039 const tDvbParameterMap InversionValues[] = { 00040 { 0, INVERSION_OFF, trNOOP("off") }, 00041 { 1, INVERSION_ON, trNOOP("on") }, 00042 { 999, INVERSION_AUTO, trNOOP("auto") }, 00043 { -1, 0, NULL } 00044 }; 00045 00046 const tDvbParameterMap BandwidthValues[] = { 00047 { 5, 5000000, "5 MHz" }, 00048 { 6, 6000000, "6 MHz" }, 00049 { 7, 7000000, "7 MHz" }, 00050 { 8, 8000000, "8 MHz" }, 00051 { 10, 10000000, "10 MHz" }, 00052 { 1712, 1712000, "1.712 MHz" }, 00053 { -1, 0, NULL } 00054 }; 00055 00056 const tDvbParameterMap CoderateValues[] = { 00057 { 0, FEC_NONE, trNOOP("none") }, 00058 { 12, FEC_1_2, "1/2" }, 00059 { 23, FEC_2_3, "2/3" }, 00060 { 34, FEC_3_4, "3/4" }, 00061 { 35, FEC_3_5, "3/5" }, 00062 { 45, FEC_4_5, "4/5" }, 00063 { 56, FEC_5_6, "5/6" }, 00064 { 67, FEC_6_7, "6/7" }, 00065 { 78, FEC_7_8, "7/8" }, 00066 { 89, FEC_8_9, "8/9" }, 00067 { 910, FEC_9_10, "9/10" }, 00068 { 999, FEC_AUTO, trNOOP("auto") }, 00069 { -1, 0, NULL } 00070 }; 00071 00072 const tDvbParameterMap ModulationValues[] = { 00073 { 16, QAM_16, "QAM16" }, 00074 { 32, QAM_32, "QAM32" }, 00075 { 64, QAM_64, "QAM64" }, 00076 { 128, QAM_128, "QAM128" }, 00077 { 256, QAM_256, "QAM256" }, 00078 { 2, QPSK, "QPSK" }, 00079 { 5, PSK_8, "8PSK" }, 00080 { 6, APSK_16, "16APSK" }, 00081 { 7, APSK_32, "32APSK" }, 00082 { 10, VSB_8, "VSB8" }, 00083 { 11, VSB_16, "VSB16" }, 00084 { 12, DQPSK, "DQPSK" }, 00085 { 999, QAM_AUTO, trNOOP("auto") }, 00086 { -1, 0, NULL } 00087 }; 00088 00089 #define DVB_SYSTEM_1 0 // see also nit.c 00090 #define DVB_SYSTEM_2 1 00091 00092 const tDvbParameterMap SystemValuesSat[] = { 00093 { 0, DVB_SYSTEM_1, "DVB-S" }, 00094 { 1, DVB_SYSTEM_2, "DVB-S2" }, 00095 { -1, 0, NULL } 00096 }; 00097 00098 const tDvbParameterMap SystemValuesTerr[] = { 00099 { 0, DVB_SYSTEM_1, "DVB-T" }, 00100 { 1, DVB_SYSTEM_2, "DVB-T2" }, 00101 { -1, 0, NULL } 00102 }; 00103 00104 const tDvbParameterMap TransmissionValues[] = { 00105 { 1, TRANSMISSION_MODE_1K, "1K" }, 00106 { 2, TRANSMISSION_MODE_2K, "2K" }, 00107 { 4, TRANSMISSION_MODE_4K, "4K" }, 00108 { 8, TRANSMISSION_MODE_8K, "8K" }, 00109 { 16, TRANSMISSION_MODE_16K, "16K" }, 00110 { 32, TRANSMISSION_MODE_32K, "32K" }, 00111 { 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") }, 00112 { -1, 0, NULL } 00113 }; 00114 00115 const tDvbParameterMap GuardValues[] = { 00116 { 4, GUARD_INTERVAL_1_4, "1/4" }, 00117 { 8, GUARD_INTERVAL_1_8, "1/8" }, 00118 { 16, GUARD_INTERVAL_1_16, "1/16" }, 00119 { 32, GUARD_INTERVAL_1_32, "1/32" }, 00120 { 128, GUARD_INTERVAL_1_128, "1/128" }, 00121 { 19128, GUARD_INTERVAL_19_128, "19/128" }, 00122 { 19256, GUARD_INTERVAL_19_256, "19/256" }, 00123 { 999, GUARD_INTERVAL_AUTO, trNOOP("auto") }, 00124 { -1, 0, NULL } 00125 }; 00126 00127 const tDvbParameterMap HierarchyValues[] = { 00128 { 0, HIERARCHY_NONE, trNOOP("none") }, 00129 { 1, HIERARCHY_1, "1" }, 00130 { 2, HIERARCHY_2, "2" }, 00131 { 4, HIERARCHY_4, "4" }, 00132 { 999, HIERARCHY_AUTO, trNOOP("auto") }, 00133 { -1, 0, NULL } 00134 }; 00135 00136 const tDvbParameterMap RollOffValues[] = { 00137 { 0, ROLLOFF_AUTO, trNOOP("auto") }, 00138 { 20, ROLLOFF_20, "0.20" }, 00139 { 25, ROLLOFF_25, "0.25" }, 00140 { 35, ROLLOFF_35, "0.35" }, 00141 { -1, 0, NULL } 00142 }; 00143 00144 int UserIndex(int Value, const tDvbParameterMap *Map) 00145 { 00146 const tDvbParameterMap *map = Map; 00147 while (map && map->userValue != -1) { 00148 if (map->userValue == Value) 00149 return map - Map; 00150 map++; 00151 } 00152 return -1; 00153 } 00154 00155 int DriverIndex(int Value, const tDvbParameterMap *Map) 00156 { 00157 const tDvbParameterMap *map = Map; 00158 while (map && map->userValue != -1) { 00159 if (map->driverValue == Value) 00160 return map - Map; 00161 map++; 00162 } 00163 return -1; 00164 } 00165 00166 int MapToUser(int Value, const tDvbParameterMap *Map, const char **String) 00167 { 00168 int n = DriverIndex(Value, Map); 00169 if (n >= 0) { 00170 if (String) 00171 *String = tr(Map[n].userString); 00172 return Map[n].userValue; 00173 } 00174 return -1; 00175 } 00176 00177 const char *MapToUserString(int Value, const tDvbParameterMap *Map) 00178 { 00179 int n = DriverIndex(Value, Map); 00180 if (n >= 0) 00181 return Map[n].userString; 00182 return "???"; 00183 } 00184 00185 int MapToDriver(int Value, const tDvbParameterMap *Map) 00186 { 00187 int n = UserIndex(Value, Map); 00188 if (n >= 0) 00189 return Map[n].driverValue; 00190 return -1; 00191 } 00192 00193 // --- cDvbTransponderParameters --------------------------------------------- 00194 00195 cDvbTransponderParameters::cDvbTransponderParameters(const char *Parameters) 00196 { 00197 polarization = 0; 00198 inversion = INVERSION_AUTO; 00199 bandwidth = 8000000; 00200 coderateH = FEC_AUTO; 00201 coderateL = FEC_AUTO; 00202 modulation = QPSK; 00203 system = DVB_SYSTEM_1; 00204 transmission = TRANSMISSION_MODE_AUTO; 00205 guard = GUARD_INTERVAL_AUTO; 00206 hierarchy = HIERARCHY_AUTO; 00207 rollOff = ROLLOFF_AUTO; 00208 plpId = 0; 00209 Parse(Parameters); 00210 } 00211 00212 int cDvbTransponderParameters::PrintParameter(char *p, char Name, int Value) const 00213 { 00214 return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0; 00215 } 00216 00217 cString cDvbTransponderParameters::ToString(char Type) const 00218 { 00219 #define ST(s) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*'))) 00220 char buffer[64]; 00221 char *q = buffer; 00222 *q = 0; 00223 ST(" S *") q += sprintf(q, "%c", polarization); 00224 ST(" T*") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues)); 00225 ST(" CST*") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues)); 00226 ST(" T*") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues)); 00227 ST(" T*") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues)); 00228 ST("ACST*") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues)); 00229 ST("ACST*") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues)); 00230 ST(" S 2") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues)); 00231 ST(" T2") q += PrintParameter(q, 'P', plpId); 00232 ST(" ST*") q += PrintParameter(q, 'S', MapToUser(system, SystemValuesSat)); // we only need the numerical value, so Sat or Terr doesn't matter 00233 ST(" T*") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues)); 00234 ST(" T*") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues)); 00235 return buffer; 00236 } 00237 00238 const char *cDvbTransponderParameters::ParseParameter(const char *s, int &Value, const tDvbParameterMap *Map) 00239 { 00240 if (*++s) { 00241 char *p = NULL; 00242 errno = 0; 00243 int n = strtol(s, &p, 10); 00244 if (!errno && p != s) { 00245 Value = Map ? MapToDriver(n, Map) : n; 00246 if (Value >= 0) 00247 return p; 00248 } 00249 } 00250 esyslog("ERROR: invalid value for parameter '%c'", *(s - 1)); 00251 return NULL; 00252 } 00253 00254 bool cDvbTransponderParameters::Parse(const char *s) 00255 { 00256 while (s && *s) { 00257 switch (toupper(*s)) { 00258 case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break; 00259 case 'C': s = ParseParameter(s, coderateH, CoderateValues); break; 00260 case 'D': s = ParseParameter(s, coderateL, CoderateValues); break; 00261 case 'G': s = ParseParameter(s, guard, GuardValues); break; 00262 case 'H': polarization = *s++; break; 00263 case 'I': s = ParseParameter(s, inversion, InversionValues); break; 00264 case 'L': polarization = *s++; break; 00265 case 'M': s = ParseParameter(s, modulation, ModulationValues); break; 00266 case 'O': s = ParseParameter(s, rollOff, RollOffValues); break; 00267 case 'P': s = ParseParameter(s, plpId); break; 00268 case 'R': polarization = *s++; break; 00269 case 'S': s = ParseParameter(s, system, SystemValuesSat); break; // we only need the numerical value, so Sat or Terr doesn't matter 00270 case 'T': s = ParseParameter(s, transmission, TransmissionValues); break; 00271 case 'V': polarization = *s++; break; 00272 case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break; 00273 default: esyslog("ERROR: unknown parameter key '%c'", *s); 00274 return false; 00275 } 00276 } 00277 return true; 00278 } 00279 00280 // --- cDvbTuner ------------------------------------------------------------- 00281 00282 #define TUNER_POLL_TIMEOUT 10 // ms 00283 00284 class cDvbTuner : public cThread { 00285 private: 00286 static cMutex bondMutex; 00287 enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; 00288 const cDvbDevice *device; 00289 int fd_frontend; 00290 int adapter, frontend; 00291 uint32_t subsystemId; 00292 int tuneTimeout; 00293 int lockTimeout; 00294 time_t lastTimeoutReport; 00295 cChannel channel; 00296 const cDiseqc *lastDiseqc; 00297 const cScr *scr; 00298 bool lnbPowerTurnedOn; 00299 eTunerStatus tunerStatus; 00300 cMutex mutex; 00301 cCondVar locked; 00302 cCondVar newSet; 00303 cDvbTuner *bondedTuner; 00304 bool bondedMaster; 00305 bool bondedMasterFailed; 00306 bool SetFrontendType(const cChannel *Channel); 00307 cString GetBondingParams(const cChannel *Channel = NULL) const; 00308 void ClearEventQueue(void) const; 00309 bool GetFrontendStatus(fe_status_t &Status) const; 00310 void ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency); 00311 void ResetToneAndVoltage(void); 00312 bool SetFrontend(void); 00313 virtual void Action(void); 00314 public: 00315 cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend); 00316 virtual ~cDvbTuner(); 00317 bool Bond(cDvbTuner *Tuner); 00318 void UnBond(void); 00319 bool BondingOk(const cChannel *Channel, bool ConsiderOccupied = false) const; 00320 cDvbTuner *GetBondedMaster(void); 00321 const cChannel *GetTransponder(void) const { return &channel; } 00322 uint32_t SubsystemId(void) const { return subsystemId; } 00323 bool IsTunedTo(const cChannel *Channel) const; 00324 void SetChannel(const cChannel *Channel); 00325 bool Locked(int TimeoutMs = 0); 00326 int GetSignalStrength(void) const; 00327 int GetSignalQuality(void) const; 00328 }; 00329 00330 cMutex cDvbTuner::bondMutex; 00331 00332 cDvbTuner::cDvbTuner(const cDvbDevice *Device, int Fd_Frontend, int Adapter, int Frontend) 00333 { 00334 device = Device; 00335 fd_frontend = Fd_Frontend; 00336 adapter = Adapter; 00337 frontend = Frontend; 00338 subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend); 00339 tuneTimeout = 0; 00340 lockTimeout = 0; 00341 lastTimeoutReport = 0; 00342 lastDiseqc = NULL; 00343 scr = NULL; 00344 lnbPowerTurnedOn = false; 00345 tunerStatus = tsIdle; 00346 bondedTuner = NULL; 00347 bondedMaster = false; 00348 bondedMasterFailed = false; 00349 SetDescription("tuner on frontend %d/%d", adapter, frontend); 00350 Start(); 00351 } 00352 00353 cDvbTuner::~cDvbTuner() 00354 { 00355 tunerStatus = tsIdle; 00356 newSet.Broadcast(); 00357 locked.Broadcast(); 00358 Cancel(3); 00359 UnBond(); 00360 /* looks like this irritates the SCR switch, so let's leave it out for now 00361 if (lastDiseqc && lastDiseqc->IsScr()) { 00362 unsigned int Frequency = 0; 00363 ExecuteDiseqc(lastDiseqc, &Frequency); 00364 } 00365 */ 00366 } 00367 00368 bool cDvbTuner::Bond(cDvbTuner *Tuner) 00369 { 00370 cMutexLock MutexLock(&bondMutex); 00371 if (!bondedTuner) { 00372 ResetToneAndVoltage(); 00373 bondedMaster = false; // makes sure we don't disturb an existing master 00374 bondedTuner = Tuner->bondedTuner ? Tuner->bondedTuner : Tuner; 00375 Tuner->bondedTuner = this; 00376 dsyslog("tuner %d/%d bonded with tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend); 00377 return true; 00378 } 00379 else 00380 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); 00381 return false; 00382 } 00383 00384 void cDvbTuner::UnBond(void) 00385 { 00386 cMutexLock MutexLock(&bondMutex); 00387 if (cDvbTuner *t = bondedTuner) { 00388 dsyslog("tuner %d/%d unbonded from tuner %d/%d", adapter, frontend, bondedTuner->adapter, bondedTuner->frontend); 00389 while (t->bondedTuner != this) 00390 t = t->bondedTuner; 00391 if (t == bondedTuner) 00392 t->bondedTuner = NULL; 00393 else 00394 t->bondedTuner = bondedTuner; 00395 bondedMaster = false; // another one will automatically become master whenever necessary 00396 bondedTuner = NULL; 00397 } 00398 } 00399 00400 cString cDvbTuner::GetBondingParams(const cChannel *Channel) const 00401 { 00402 if (!Channel) 00403 Channel = &channel; 00404 cDvbTransponderParameters dtp(Channel->Parameters()); 00405 if (Setup.DiSEqC) { 00406 if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL)) 00407 return diseqc->Commands(); 00408 } 00409 else { 00410 bool ToneOff = Channel->Frequency() < (unsigned int)Setup.LnbSLOF; 00411 bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R'; 00412 return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V'); 00413 } 00414 return ""; 00415 } 00416 00417 bool cDvbTuner::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const 00418 { 00419 cMutexLock MutexLock(&bondMutex); 00420 if (cDvbTuner *t = bondedTuner) { 00421 cString BondingParams = GetBondingParams(Channel); 00422 do { 00423 if (t->device->Priority() > IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) { 00424 if (strcmp(BondingParams, t->GetBondingParams()) != 0) 00425 return false; 00426 } 00427 t = t->bondedTuner; 00428 } while (t != bondedTuner); 00429 } 00430 return true; 00431 } 00432 00433 cDvbTuner *cDvbTuner::GetBondedMaster(void) 00434 { 00435 if (!bondedTuner) 00436 return this; // an unbonded tuner is always "master" 00437 cMutexLock MutexLock(&bondMutex); 00438 if (bondedMaster) { 00439 if (!bondedMasterFailed) 00440 return this; 00441 else 00442 bondedMaster = false; 00443 } 00444 // This tuner is bonded, but it's not the master, so let's see if there is a master at all: 00445 if (cDvbTuner *t = bondedTuner) { 00446 while (t != this) { 00447 if (t->bondedMaster) 00448 return t; 00449 t = t->bondedTuner; 00450 } 00451 } 00452 // None of the other bonded tuners is master, so make this one the master: 00453 cDvbTuner *t = this; 00454 if (bondedMasterFailed) { 00455 // This one has failed, so switch to the next one: 00456 t = bondedTuner; 00457 t->bondedMasterFailed = false; 00458 cMutexLock MutexLock(&t->mutex); 00459 t->channel = channel; 00460 t->tunerStatus = tsSet; 00461 } 00462 t->bondedMaster = true; 00463 dsyslog("tuner %d/%d is now bonded master", t->adapter, t->frontend); 00464 return t; 00465 } 00466 00467 bool cDvbTuner::IsTunedTo(const cChannel *Channel) const 00468 { 00469 if (tunerStatus == tsIdle) 00470 return false; // not tuned to 00471 if (channel.Source() != Channel->Source() || channel.Transponder() != Channel->Transponder()) 00472 return false; // sufficient mismatch 00473 // Polarization is already checked as part of the Transponder. 00474 return strcmp(channel.Parameters(), Channel->Parameters()) == 0; 00475 } 00476 00477 void cDvbTuner::SetChannel(const cChannel *Channel) 00478 { 00479 if (Channel) { 00480 if (bondedTuner) { 00481 cMutexLock MutexLock(&bondMutex); 00482 cDvbTuner *BondedMaster = GetBondedMaster(); 00483 if (BondedMaster == this) { 00484 if (strcmp(GetBondingParams(Channel), GetBondingParams()) != 0) { 00485 // switching to a completely different band, so set all others to idle: 00486 for (cDvbTuner *t = bondedTuner; t && t != this; t = t->bondedTuner) 00487 t->SetChannel(NULL); 00488 } 00489 } 00490 else if (strcmp(GetBondingParams(Channel), BondedMaster->GetBondingParams()) != 0) 00491 BondedMaster->SetChannel(Channel); 00492 } 00493 cMutexLock MutexLock(&mutex); 00494 if (!IsTunedTo(Channel)) 00495 tunerStatus = tsSet; 00496 channel = *Channel; 00497 lastTimeoutReport = 0; 00498 newSet.Broadcast(); 00499 } 00500 else { 00501 cMutexLock MutexLock(&mutex); 00502 tunerStatus = tsIdle; 00503 ResetToneAndVoltage(); 00504 } 00505 if (bondedTuner && device->IsPrimaryDevice()) 00506 cDevice::PrimaryDevice()->DelLivePids(); // 'device' is const, so we must do it this way 00507 } 00508 00509 bool cDvbTuner::Locked(int TimeoutMs) 00510 { 00511 bool isLocked = (tunerStatus >= tsLocked); 00512 if (isLocked || !TimeoutMs) 00513 return isLocked; 00514 00515 cMutexLock MutexLock(&mutex); 00516 if (TimeoutMs && tunerStatus < tsLocked) 00517 locked.TimedWait(mutex, TimeoutMs); 00518 return tunerStatus >= tsLocked; 00519 } 00520 00521 void cDvbTuner::ClearEventQueue(void) const 00522 { 00523 cPoller Poller(fd_frontend); 00524 if (Poller.Poll(TUNER_POLL_TIMEOUT)) { 00525 dvb_frontend_event Event; 00526 while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0) 00527 ; // just to clear the event queue - we'll read the actual status below 00528 } 00529 } 00530 00531 bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const 00532 { 00533 ClearEventQueue(); 00534 while (1) { 00535 if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1) 00536 return true; 00537 if (errno != EINTR) 00538 break; 00539 } 00540 return false; 00541 } 00542 00543 //#define DEBUG_SIGNALSTRENGTH 00544 //#define DEBUG_SIGNALQUALITY 00545 00546 int cDvbTuner::GetSignalStrength(void) const 00547 { 00548 ClearEventQueue(); 00549 uint16_t Signal; 00550 while (1) { 00551 if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1) 00552 break; 00553 if (errno != EINTR) 00554 return -1; 00555 } 00556 uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range. 00557 // Use the subsystemId to identify individual devices in case they need 00558 // special treatment to map their Signal value into the range 0...0xFFFF. 00559 switch (subsystemId) { 00560 case 0x13C21019: MaxSignal = 670; break; // TT-budget S2-3200 (DVB-S/DVB-S2) 00561 } 00562 int s = int(Signal) * 100 / MaxSignal; 00563 if (s > 100) 00564 s = 100; 00565 #ifdef DEBUG_SIGNALSTRENGTH 00566 fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s); 00567 #endif 00568 return s; 00569 } 00570 00571 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set 00572 00573 int cDvbTuner::GetSignalQuality(void) const 00574 { 00575 fe_status_t Status; 00576 if (GetFrontendStatus(Status)) { 00577 // 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... 00578 if ((Status & FE_HAS_LOCK) == 0) { 00579 if ((Status & FE_HAS_SIGNAL) == 0) 00580 return 0; 00581 if ((Status & FE_HAS_CARRIER) == 0) 00582 return 1; 00583 if ((Status & FE_HAS_VITERBI) == 0) 00584 return 2; 00585 if ((Status & FE_HAS_SYNC) == 0) 00586 return 3; 00587 return 4; 00588 } 00589 bool HasSnr = true; 00590 uint16_t Snr; 00591 while (1) { 00592 if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1) 00593 break; 00594 if (errno == EOPNOTSUPP) { 00595 Snr = 0xFFFF; 00596 HasSnr = false; 00597 break; 00598 } 00599 if (errno != EINTR) 00600 return -1; 00601 } 00602 bool HasBer = true; 00603 uint32_t Ber; 00604 while (1) { 00605 if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1) 00606 break; 00607 if (errno == EOPNOTSUPP) { 00608 Ber = 0; 00609 HasBer = false; 00610 break; 00611 } 00612 if (errno != EINTR) 00613 return -1; 00614 } 00615 bool HasUnc = true; 00616 uint32_t Unc; 00617 while (1) { 00618 if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1) 00619 break; 00620 if (errno == EOPNOTSUPP) { 00621 Unc = 0; 00622 HasUnc = false; 00623 break; 00624 } 00625 if (errno != EINTR) 00626 return -1; 00627 } 00628 uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range. 00629 // Use the subsystemId to identify individual devices in case they need 00630 // special treatment to map their Snr value into the range 0...0xFFFF. 00631 switch (subsystemId) { 00632 case 0x13C21019: MaxSnr = 200; break; // TT-budget S2-3200 (DVB-S/DVB-S2) 00633 } 00634 int a = int(Snr) * 100 / MaxSnr; 00635 int b = 100 - (Unc * 10 + (Ber / 256) * 5); 00636 if (b < 0) 00637 b = 0; 00638 int q = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100; 00639 if (q > 100) 00640 q = 100; 00641 #ifdef DEBUG_SIGNALQUALITY 00642 fprintf(stderr, "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); 00643 #endif 00644 return q; 00645 } 00646 return -1; 00647 } 00648 00649 static unsigned int FrequencyToHz(unsigned int f) 00650 { 00651 while (f && f < 1000000) 00652 f *= 1000; 00653 return f; 00654 } 00655 00656 void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, unsigned int *Frequency) 00657 { 00658 if (!lnbPowerTurnedOn) { 00659 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); // must explicitly turn on LNB power 00660 lnbPowerTurnedOn = true; 00661 } 00662 static cMutex Mutex; 00663 if (Diseqc->IsScr()) 00664 Mutex.Lock(); 00665 struct dvb_diseqc_master_cmd cmd; 00666 const char *CurrentAction = NULL; 00667 for (;;) { 00668 cmd.msg_len = sizeof(cmd.msg); 00669 cDiseqc::eDiseqcActions da = Diseqc->Execute(&CurrentAction, cmd.msg, &cmd.msg_len, scr, Frequency); 00670 if (da == cDiseqc::daNone) 00671 break; 00672 switch (da) { 00673 case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break; 00674 case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break; 00675 case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break; 00676 case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break; 00677 case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break; 00678 case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break; 00679 case cDiseqc::daCodes: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); break; 00680 default: esyslog("ERROR: unknown diseqc command %d", da); 00681 } 00682 } 00683 if (scr) 00684 ResetToneAndVoltage(); // makes sure we don't block the bus! 00685 if (Diseqc->IsScr()) 00686 Mutex.Unlock(); 00687 } 00688 00689 void cDvbTuner::ResetToneAndVoltage(void) 00690 { 00691 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); 00692 CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); 00693 } 00694 00695 static int GetRequiredDeliverySystem(const cChannel *Channel, const cDvbTransponderParameters *Dtp) 00696 { 00697 int ds = SYS_UNDEFINED; 00698 if (Channel->IsAtsc()) 00699 ds = SYS_ATSC; 00700 else if (Channel->IsCable()) 00701 ds = SYS_DVBC_ANNEX_AC; 00702 else if (Channel->IsSat()) 00703 ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBS : SYS_DVBS2; 00704 else if (Channel->IsTerr()) 00705 ds = Dtp->System() == DVB_SYSTEM_1 ? SYS_DVBT : SYS_DVBT2; 00706 else 00707 esyslog("ERROR: can't determine frontend type for channel %d", Channel->Number()); 00708 return ds; 00709 } 00710 00711 bool cDvbTuner::SetFrontend(void) 00712 { 00713 #define MAXFRONTENDCMDS 16 00714 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\ 00715 Frontend[CmdSeq.num].u.data = (d);\ 00716 if (CmdSeq.num++ > MAXFRONTENDCMDS) {\ 00717 esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\ 00718 return false;\ 00719 }\ 00720 } 00721 dtv_property Frontend[MAXFRONTENDCMDS]; 00722 memset(&Frontend, 0, sizeof(Frontend)); 00723 dtv_properties CmdSeq; 00724 memset(&CmdSeq, 0, sizeof(CmdSeq)); 00725 CmdSeq.props = Frontend; 00726 SETCMD(DTV_CLEAR, 0); 00727 if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) { 00728 esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); 00729 return false; 00730 } 00731 CmdSeq.num = 0; 00732 00733 cDvbTransponderParameters dtp(channel.Parameters()); 00734 00735 // Determine the required frontend type: 00736 int frontendType = GetRequiredDeliverySystem(&channel, &dtp); 00737 if (frontendType == SYS_UNDEFINED) 00738 return false; 00739 00740 SETCMD(DTV_DELIVERY_SYSTEM, frontendType); 00741 if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) { 00742 unsigned int frequency = channel.Frequency(); 00743 if (Setup.DiSEqC) { 00744 if (const cDiseqc *diseqc = Diseqcs.Get(device->CardIndex() + 1, channel.Source(), frequency, dtp.Polarization(), &scr)) { 00745 frequency -= diseqc->Lof(); 00746 if (diseqc != lastDiseqc || diseqc->IsScr()) { 00747 if (GetBondedMaster() == this) { 00748 ExecuteDiseqc(diseqc, &frequency); 00749 if (frequency == 0) 00750 return false; 00751 } 00752 else 00753 ResetToneAndVoltage(); 00754 lastDiseqc = diseqc; 00755 } 00756 } 00757 else { 00758 esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number()); 00759 return false; 00760 } 00761 } 00762 else { 00763 int tone = SEC_TONE_OFF; 00764 if (frequency < (unsigned int)Setup.LnbSLOF) { 00765 frequency -= Setup.LnbFrequLo; 00766 tone = SEC_TONE_OFF; 00767 } 00768 else { 00769 frequency -= Setup.LnbFrequHi; 00770 tone = SEC_TONE_ON; 00771 } 00772 int volt = (dtp.Polarization() == 'V' || dtp.Polarization() == 'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; 00773 if (GetBondedMaster() != this) { 00774 tone = SEC_TONE_OFF; 00775 volt = SEC_VOLTAGE_13; 00776 } 00777 CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt)); 00778 CHECK(ioctl(fd_frontend, FE_SET_TONE, tone)); 00779 } 00780 frequency = abs(frequency); // Allow for C-band, where the frequency is less than the LOF 00781 00782 // DVB-S/DVB-S2 (common parts) 00783 SETCMD(DTV_FREQUENCY, frequency * 1000UL); 00784 SETCMD(DTV_MODULATION, dtp.Modulation()); 00785 SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL); 00786 SETCMD(DTV_INNER_FEC, dtp.CoderateH()); 00787 SETCMD(DTV_INVERSION, dtp.Inversion()); 00788 if (frontendType == SYS_DVBS2) { 00789 // DVB-S2 00790 SETCMD(DTV_PILOT, PILOT_AUTO); 00791 SETCMD(DTV_ROLLOFF, dtp.RollOff()); 00792 } 00793 else { 00794 // DVB-S 00795 SETCMD(DTV_ROLLOFF, ROLLOFF_35); // DVB-S always has a ROLLOFF of 0.35 00796 } 00797 00798 tuneTimeout = DVBS_TUNE_TIMEOUT; 00799 lockTimeout = DVBS_LOCK_TIMEOUT; 00800 } 00801 else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) { 00802 // DVB-C 00803 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())); 00804 SETCMD(DTV_INVERSION, dtp.Inversion()); 00805 SETCMD(DTV_SYMBOL_RATE, channel.Srate() * 1000UL); 00806 SETCMD(DTV_INNER_FEC, dtp.CoderateH()); 00807 SETCMD(DTV_MODULATION, dtp.Modulation()); 00808 00809 tuneTimeout = DVBC_TUNE_TIMEOUT; 00810 lockTimeout = DVBC_LOCK_TIMEOUT; 00811 } 00812 else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) { 00813 // DVB-T/DVB-T2 (common parts) 00814 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())); 00815 SETCMD(DTV_INVERSION, dtp.Inversion()); 00816 SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth()); 00817 SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH()); 00818 SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL()); 00819 SETCMD(DTV_MODULATION, dtp.Modulation()); 00820 SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission()); 00821 SETCMD(DTV_GUARD_INTERVAL, dtp.Guard()); 00822 SETCMD(DTV_HIERARCHY, dtp.Hierarchy()); 00823 if (frontendType == SYS_DVBT2) { 00824 // DVB-T2 00825 SETCMD(DTV_DVBT2_PLP_ID, dtp.PlpId()); 00826 } 00827 00828 tuneTimeout = DVBT_TUNE_TIMEOUT; 00829 lockTimeout = DVBT_LOCK_TIMEOUT; 00830 } 00831 else if (frontendType == SYS_ATSC) { 00832 // ATSC 00833 SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency())); 00834 SETCMD(DTV_INVERSION, dtp.Inversion()); 00835 SETCMD(DTV_MODULATION, dtp.Modulation()); 00836 00837 tuneTimeout = ATSC_TUNE_TIMEOUT; 00838 lockTimeout = ATSC_LOCK_TIMEOUT; 00839 } 00840 else { 00841 esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); 00842 return false; 00843 } 00844 SETCMD(DTV_TUNE, 0); 00845 if (ioctl(fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) { 00846 esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); 00847 return false; 00848 } 00849 return true; 00850 } 00851 00852 void cDvbTuner::Action(void) 00853 { 00854 cTimeMs Timer; 00855 bool LostLock = false; 00856 fe_status_t Status = (fe_status_t)0; 00857 while (Running()) { 00858 fe_status_t NewStatus; 00859 if (GetFrontendStatus(NewStatus)) 00860 Status = NewStatus; 00861 cMutexLock MutexLock(&mutex); 00862 int WaitTime = 1000; 00863 switch (tunerStatus) { 00864 case tsIdle: 00865 break; 00866 case tsSet: 00867 tunerStatus = SetFrontend() ? tsTuned : tsIdle; 00868 Timer.Set(tuneTimeout + (scr ? rand() % SCR_RANDOM_TIMEOUT : 0)); 00869 continue; 00870 case tsTuned: 00871 if (Timer.TimedOut()) { 00872 tunerStatus = tsSet; 00873 lastDiseqc = NULL; 00874 if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these 00875 isyslog("frontend %d/%d timed out while tuning to channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder()); 00876 lastTimeoutReport = time(NULL); 00877 } 00878 cMutexLock MutexLock(&bondMutex); 00879 if (bondedTuner && bondedMaster) 00880 bondedMasterFailed = true; // give an other tuner a chance in case the sat cable was disconnected 00881 continue; 00882 } 00883 WaitTime = 100; // allows for a quick change from tsTuned to tsLocked 00884 case tsLocked: 00885 if (Status & FE_REINIT) { 00886 tunerStatus = tsSet; 00887 lastDiseqc = NULL; 00888 isyslog("frontend %d/%d was reinitialized", adapter, frontend); 00889 lastTimeoutReport = 0; 00890 continue; 00891 } 00892 else if (Status & FE_HAS_LOCK) { 00893 if (LostLock) { 00894 isyslog("frontend %d/%d regained lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder()); 00895 LostLock = false; 00896 } 00897 tunerStatus = tsLocked; 00898 locked.Broadcast(); 00899 lastTimeoutReport = 0; 00900 } 00901 else if (tunerStatus == tsLocked) { 00902 LostLock = true; 00903 isyslog("frontend %d/%d lost lock on channel %d, tp %d", adapter, frontend, channel.Number(), channel.Transponder()); 00904 tunerStatus = tsTuned; 00905 Timer.Set(lockTimeout); 00906 lastTimeoutReport = 0; 00907 continue; 00908 } 00909 break; 00910 default: esyslog("ERROR: unknown tuner status %d", tunerStatus); 00911 } 00912 newSet.TimedWait(mutex, WaitTime); 00913 } 00914 } 00915 00916 // --- cDvbSourceParam ------------------------------------------------------- 00917 00918 class cDvbSourceParam : public cSourceParam { 00919 private: 00920 int param; 00921 int srate; 00922 cDvbTransponderParameters dtp; 00923 public: 00924 cDvbSourceParam(char Source, const char *Description); 00925 virtual void SetData(cChannel *Channel); 00926 virtual void GetData(cChannel *Channel); 00927 virtual cOsdItem *GetOsdItem(void); 00928 }; 00929 00930 cDvbSourceParam::cDvbSourceParam(char Source, const char *Description) 00931 :cSourceParam(Source, Description) 00932 { 00933 param = 0; 00934 srate = 0; 00935 } 00936 00937 void cDvbSourceParam::SetData(cChannel *Channel) 00938 { 00939 srate = Channel->Srate(); 00940 dtp.Parse(Channel->Parameters()); 00941 param = 0; 00942 } 00943 00944 void cDvbSourceParam::GetData(cChannel *Channel) 00945 { 00946 Channel->SetTransponderData(Channel->Source(), Channel->Frequency(), srate, dtp.ToString(Source()), true); 00947 } 00948 00949 cOsdItem *cDvbSourceParam::GetOsdItem(void) 00950 { 00951 char type = Source(); 00952 const tDvbParameterMap *SystemValues = type == 'S' ? SystemValuesSat : SystemValuesTerr; 00953 #undef ST 00954 #define ST(s) if (strchr(s, type)) 00955 switch (param++) { 00956 case 0: ST(" S ") return new cMenuEditChrItem( tr("Polarization"), &dtp.polarization, "HVLR"); else return GetOsdItem(); 00957 case 1: ST(" ST") return new cMenuEditMapItem( tr("System"), &dtp.system, SystemValues); else return GetOsdItem(); 00958 case 2: ST(" CS ") return new cMenuEditIntItem( tr("Srate"), &srate); else return GetOsdItem(); 00959 case 3: ST("ACST") return new cMenuEditMapItem( tr("Inversion"), &dtp.inversion, InversionValues); else return GetOsdItem(); 00960 case 4: ST(" CST") return new cMenuEditMapItem( tr("CoderateH"), &dtp.coderateH, CoderateValues); else return GetOsdItem(); 00961 case 5: ST(" T") return new cMenuEditMapItem( tr("CoderateL"), &dtp.coderateL, CoderateValues); else return GetOsdItem(); 00962 case 6: ST("ACST") return new cMenuEditMapItem( tr("Modulation"), &dtp.modulation, ModulationValues); else return GetOsdItem(); 00963 case 7: ST(" T") return new cMenuEditMapItem( tr("Bandwidth"), &dtp.bandwidth, BandwidthValues); else return GetOsdItem(); 00964 case 8: ST(" T") return new cMenuEditMapItem( tr("Transmission"), &dtp.transmission, TransmissionValues); else return GetOsdItem(); 00965 case 9: ST(" T") return new cMenuEditMapItem( tr("Guard"), &dtp.guard, GuardValues); else return GetOsdItem(); 00966 case 10: ST(" T") return new cMenuEditMapItem( tr("Hierarchy"), &dtp.hierarchy, HierarchyValues); else return GetOsdItem(); 00967 case 11: ST(" S ") return new cMenuEditMapItem( tr("Rolloff"), &dtp.rollOff, RollOffValues); else return GetOsdItem(); 00968 case 12: ST(" T") return new cMenuEditIntItem( tr("PlpId"), &dtp.plpId, 0, 255); else return GetOsdItem(); 00969 default: return NULL; 00970 } 00971 return NULL; 00972 } 00973 00974 // --- cDvbDevice ------------------------------------------------------------ 00975 00976 int cDvbDevice::setTransferModeForDolbyDigital = 1; 00977 cMutex cDvbDevice::bondMutex; 00978 00979 const char *DeliverySystemNames[] = { 00980 "UNDEFINED", 00981 "DVB-C", 00982 "DVB-C", 00983 "DVB-T", 00984 "DSS", 00985 "DVB-S", 00986 "DVB-S2", 00987 "DVB-H", 00988 "ISDBT", 00989 "ISDBS", 00990 "ISDBC", 00991 "ATSC", 00992 "ATSCMH", 00993 "DMBTH", 00994 "CMMB", 00995 "DAB", 00996 "DVB-T2", 00997 "TURBO", 00998 NULL 00999 }; 01000 01001 cDvbDevice::cDvbDevice(int Adapter, int Frontend) 01002 { 01003 adapter = Adapter; 01004 frontend = Frontend; 01005 ciAdapter = NULL; 01006 dvbTuner = NULL; 01007 numDeliverySystems = 0; 01008 numModulations = 0; 01009 bondedDevice = NULL; 01010 needsDetachBondedReceivers = false; 01011 tsBuffer = NULL; 01012 01013 // Devices that are present on all card types: 01014 01015 int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, adapter, frontend, O_RDWR | O_NONBLOCK); 01016 01017 // Common Interface: 01018 01019 fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); 01020 if (fd_ca >= 0) 01021 ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); 01022 01023 // The DVR device (will be opened and closed as needed): 01024 01025 fd_dvr = -1; 01026 01027 // We only check the devices that must be present - the others will be checked before accessing them://XXX 01028 01029 if (fd_frontend >= 0) { 01030 if (QueryDeliverySystems(fd_frontend)) 01031 dvbTuner = new cDvbTuner(this, fd_frontend, adapter, frontend); 01032 } 01033 else 01034 esyslog("ERROR: can't open DVB device %d/%d", adapter, frontend); 01035 01036 StartSectionHandler(); 01037 } 01038 01039 cDvbDevice::~cDvbDevice() 01040 { 01041 StopSectionHandler(); 01042 delete dvbTuner; 01043 delete ciAdapter; 01044 UnBond(); 01045 // We're not explicitly closing any device files here, since this sometimes 01046 // caused segfaults. Besides, the program is about to terminate anyway... 01047 } 01048 01049 cString cDvbDevice::DvbName(const char *Name, int Adapter, int Frontend) 01050 { 01051 return cString::sprintf("%s/%s%d/%s%d", DEV_DVB_BASE, DEV_DVB_ADAPTER, Adapter, Name, Frontend); 01052 } 01053 01054 int cDvbDevice::DvbOpen(const char *Name, int Adapter, int Frontend, int Mode, bool ReportError) 01055 { 01056 cString FileName = DvbName(Name, Adapter, Frontend); 01057 int fd = open(FileName, Mode); 01058 if (fd < 0 && ReportError) 01059 LOG_ERROR_STR(*FileName); 01060 return fd; 01061 } 01062 01063 bool cDvbDevice::Exists(int Adapter, int Frontend) 01064 { 01065 cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend); 01066 if (access(FileName, F_OK) == 0) { 01067 int f = open(FileName, O_RDONLY); 01068 if (f >= 0) { 01069 close(f); 01070 return true; 01071 } 01072 else if (errno != ENODEV && errno != EINVAL) 01073 LOG_ERROR_STR(*FileName); 01074 } 01075 else if (errno != ENOENT) 01076 LOG_ERROR_STR(*FileName); 01077 return false; 01078 } 01079 01080 bool cDvbDevice::Probe(int Adapter, int Frontend) 01081 { 01082 cString FileName = DvbName(DEV_DVB_FRONTEND, Adapter, Frontend); 01083 dsyslog("probing %s", *FileName); 01084 for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) { 01085 if (dp->Probe(Adapter, Frontend)) 01086 return true; // a plugin has created the actual device 01087 } 01088 dsyslog("creating cDvbDevice"); 01089 new cDvbDevice(Adapter, Frontend); // it's a "budget" device 01090 return true; 01091 } 01092 01093 cString cDvbDevice::DeviceName(void) const 01094 { 01095 return frontendInfo.name; 01096 } 01097 01098 bool cDvbDevice::Initialize(void) 01099 { 01100 new cDvbSourceParam('A', "ATSC"); 01101 new cDvbSourceParam('C', "DVB-C"); 01102 new cDvbSourceParam('S', "DVB-S"); 01103 new cDvbSourceParam('T', "DVB-T"); 01104 cStringList Nodes; 01105 cReadDir DvbDir(DEV_DVB_BASE); 01106 if (DvbDir.Ok()) { 01107 struct dirent *a; 01108 while ((a = DvbDir.Next()) != NULL) { 01109 if (strstr(a->d_name, DEV_DVB_ADAPTER) == a->d_name) { 01110 int Adapter = strtol(a->d_name + strlen(DEV_DVB_ADAPTER), NULL, 10); 01111 cReadDir AdapterDir(AddDirectory(DEV_DVB_BASE, a->d_name)); 01112 if (AdapterDir.Ok()) { 01113 struct dirent *f; 01114 while ((f = AdapterDir.Next()) != NULL) { 01115 if (strstr(f->d_name, DEV_DVB_FRONTEND) == f->d_name) { 01116 int Frontend = strtol(f->d_name + strlen(DEV_DVB_FRONTEND), NULL, 10); 01117 Nodes.Append(strdup(cString::sprintf("%2d %2d", Adapter, Frontend))); 01118 } 01119 } 01120 } 01121 } 01122 } 01123 } 01124 int Checked = 0; 01125 int Found = 0; 01126 if (Nodes.Size() > 0) { 01127 Nodes.Sort(); 01128 for (int i = 0; i < Nodes.Size(); i++) { 01129 int Adapter; 01130 int Frontend; 01131 if (2 == sscanf(Nodes[i], "%d %d", &Adapter, &Frontend)) { 01132 if (Exists(Adapter, Frontend)) { 01133 if (Checked++ < MAXDVBDEVICES) { 01134 if (UseDevice(NextCardIndex())) { 01135 if (Probe(Adapter, Frontend)) 01136 Found++; 01137 } 01138 else 01139 NextCardIndex(1); // skips this one 01140 } 01141 } 01142 } 01143 } 01144 } 01145 NextCardIndex(MAXDVBDEVICES - Checked); // skips the rest 01146 if (Found > 0) 01147 isyslog("found %d DVB device%s", Found, Found > 1 ? "s" : ""); 01148 else 01149 isyslog("no DVB device found"); 01150 return Found > 0; 01151 } 01152 01153 bool cDvbDevice::QueryDeliverySystems(int fd_frontend) 01154 { 01155 numDeliverySystems = 0; 01156 if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) < 0) { 01157 LOG_ERROR; 01158 return false; 01159 } 01160 #if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) >= 0x0505 01161 dtv_property Frontend[1]; 01162 memset(&Frontend, 0, sizeof(Frontend)); 01163 dtv_properties CmdSeq; 01164 memset(&CmdSeq, 0, sizeof(CmdSeq)); 01165 CmdSeq.props = Frontend; 01166 SETCMD(DTV_ENUM_DELSYS, 0); 01167 int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq); 01168 if (Result == 0) { 01169 for (uint i = 0; i < Frontend[0].u.buffer.len; i++) { 01170 if (numDeliverySystems >= MAXDELIVERYSYSTEMS) { 01171 esyslog("ERROR: too many delivery systems on frontend %d/%d", adapter, frontend); 01172 break; 01173 } 01174 deliverySystems[numDeliverySystems++] = Frontend[0].u.buffer.data[i]; 01175 } 01176 } 01177 else { 01178 esyslog("ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode", adapter, frontend); 01179 #else 01180 { 01181 #endif 01182 // Legacy mode (DVB-API < 5.5): 01183 switch (frontendInfo.type) { 01184 case FE_QPSK: deliverySystems[numDeliverySystems++] = SYS_DVBS; 01185 if (frontendInfo.caps & FE_CAN_2G_MODULATION) 01186 deliverySystems[numDeliverySystems++] = SYS_DVBS2; 01187 break; 01188 case FE_OFDM: deliverySystems[numDeliverySystems++] = SYS_DVBT; 01189 if (frontendInfo.caps & FE_CAN_2G_MODULATION) 01190 deliverySystems[numDeliverySystems++] = SYS_DVBT2; 01191 break; 01192 case FE_QAM: deliverySystems[numDeliverySystems++] = SYS_DVBC_ANNEX_AC; break; 01193 case FE_ATSC: deliverySystems[numDeliverySystems++] = SYS_ATSC; break; 01194 default: esyslog("ERROR: unknown frontend type %d on frontend %d/%d", frontendInfo.type, adapter, frontend); 01195 } 01196 } 01197 if (numDeliverySystems > 0) { 01198 cString ds(""); 01199 for (int i = 0; i < numDeliverySystems; i++) 01200 ds = cString::sprintf("%s%s%s", *ds, i ? "," : "", DeliverySystemNames[deliverySystems[i]]); 01201 cString ms(""); 01202 if (frontendInfo.caps & FE_CAN_QPSK) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QPSK, ModulationValues)); } 01203 if (frontendInfo.caps & FE_CAN_QAM_16) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_16, ModulationValues)); } 01204 if (frontendInfo.caps & FE_CAN_QAM_32) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_32, ModulationValues)); } 01205 if (frontendInfo.caps & FE_CAN_QAM_64) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_64, ModulationValues)); } 01206 if (frontendInfo.caps & FE_CAN_QAM_128) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_128, ModulationValues)); } 01207 if (frontendInfo.caps & FE_CAN_QAM_256) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(QAM_256, ModulationValues)); } 01208 if (frontendInfo.caps & FE_CAN_8VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_8, ModulationValues)); } 01209 if (frontendInfo.caps & FE_CAN_16VSB) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", MapToUserString(VSB_16, ModulationValues)); } 01210 if (frontendInfo.caps & FE_CAN_TURBO_FEC) { numModulations++; ms = cString::sprintf("%s%s%s", *ms, **ms ? "," : "", "TURBO_FEC"); } 01211 if (!**ms) 01212 ms = "unknown modulations"; 01213 isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, *ds, *ms, frontendInfo.name); 01214 return true; 01215 } 01216 else 01217 esyslog("ERROR: frontend %d/%d doesn't provide any delivery systems", adapter, frontend); 01218 return false; 01219 } 01220 01221 bool cDvbDevice::Ready(void) 01222 { 01223 if (ciAdapter) 01224 return ciAdapter->Ready(); 01225 return true; 01226 } 01227 01228 bool cDvbDevice::BondDevices(const char *Bondings) 01229 { 01230 UnBondDevices(); 01231 if (Bondings) { 01232 cSatCableNumbers SatCableNumbers(MAXDEVICES, Bondings); 01233 for (int i = 0; i < cDevice::NumDevices(); i++) { 01234 int d = SatCableNumbers.FirstDeviceIndex(i); 01235 if (d >= 0) { 01236 int ErrorDevice = 0; 01237 if (cDevice *Device1 = cDevice::GetDevice(i)) { 01238 if (cDevice *Device2 = cDevice::GetDevice(d)) { 01239 if (cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) { 01240 if (cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) { 01241 if (!DvbDevice1->Bond(DvbDevice2)) 01242 return false; // Bond() has already logged the error 01243 } 01244 else 01245 ErrorDevice = d + 1; 01246 } 01247 else 01248 ErrorDevice = i + 1; 01249 if (ErrorDevice) { 01250 esyslog("ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings); 01251 return false; 01252 } 01253 } 01254 else 01255 ErrorDevice = d + 1; 01256 } 01257 else 01258 ErrorDevice = i + 1; 01259 if (ErrorDevice) { 01260 esyslog("ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings); 01261 return false; 01262 } 01263 } 01264 } 01265 } 01266 return true; 01267 } 01268 01269 void cDvbDevice::UnBondDevices(void) 01270 { 01271 for (int i = 0; i < cDevice::NumDevices(); i++) { 01272 if (cDvbDevice *d = dynamic_cast<cDvbDevice *>(cDevice::GetDevice(i))) 01273 d->UnBond(); 01274 } 01275 } 01276 01277 bool cDvbDevice::Bond(cDvbDevice *Device) 01278 { 01279 cMutexLock MutexLock(&bondMutex); 01280 if (!bondedDevice) { 01281 if (Device != this) { 01282 if ((ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) && (Device->ProvidesDeliverySystem(SYS_DVBS) || Device->ProvidesDeliverySystem(SYS_DVBS2))) { 01283 if (dvbTuner && Device->dvbTuner && dvbTuner->Bond(Device->dvbTuner)) { 01284 bondedDevice = Device->bondedDevice ? Device->bondedDevice : Device; 01285 Device->bondedDevice = this; 01286 dsyslog("device %d bonded with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1); 01287 return true; 01288 } 01289 } 01290 else 01291 esyslog("ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)", CardIndex() + 1, Device->CardIndex() + 1); 01292 } 01293 else 01294 esyslog("ERROR: can't bond device %d with itself", CardIndex() + 1); 01295 } 01296 else 01297 esyslog("ERROR: device %d already bonded with device %d, can't bond with device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1, Device->CardIndex() + 1); 01298 return false; 01299 } 01300 01301 void cDvbDevice::UnBond(void) 01302 { 01303 cMutexLock MutexLock(&bondMutex); 01304 if (cDvbDevice *d = bondedDevice) { 01305 if (dvbTuner) 01306 dvbTuner->UnBond(); 01307 dsyslog("device %d unbonded from device %d", CardIndex() + 1, bondedDevice->CardIndex() + 1); 01308 while (d->bondedDevice != this) 01309 d = d->bondedDevice; 01310 if (d == bondedDevice) 01311 d->bondedDevice = NULL; 01312 else 01313 d->bondedDevice = bondedDevice; 01314 bondedDevice = NULL; 01315 } 01316 } 01317 01318 bool cDvbDevice::BondingOk(const cChannel *Channel, bool ConsiderOccupied) const 01319 { 01320 cMutexLock MutexLock(&bondMutex); 01321 if (bondedDevice) 01322 return dvbTuner && dvbTuner->BondingOk(Channel, ConsiderOccupied); 01323 return true; 01324 } 01325 01326 bool cDvbDevice::HasCi(void) 01327 { 01328 return ciAdapter; 01329 } 01330 01331 bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On) 01332 { 01333 if (Handle->pid) { 01334 dmx_pes_filter_params pesFilterParams; 01335 memset(&pesFilterParams, 0, sizeof(pesFilterParams)); 01336 if (On) { 01337 if (Handle->handle < 0) { 01338 Handle->handle = DvbOpen(DEV_DVB_DEMUX, adapter, frontend, O_RDWR | O_NONBLOCK, true); 01339 if (Handle->handle < 0) { 01340 LOG_ERROR; 01341 return false; 01342 } 01343 } 01344 pesFilterParams.pid = Handle->pid; 01345 pesFilterParams.input = DMX_IN_FRONTEND; 01346 pesFilterParams.output = DMX_OUT_TS_TAP; 01347 pesFilterParams.pes_type= DMX_PES_OTHER; 01348 pesFilterParams.flags = DMX_IMMEDIATE_START; 01349 if (ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) { 01350 LOG_ERROR; 01351 return false; 01352 } 01353 } 01354 else if (!Handle->used) { 01355 CHECK(ioctl(Handle->handle, DMX_STOP)); 01356 if (Type <= ptTeletext) { 01357 pesFilterParams.pid = 0x1FFF; 01358 pesFilterParams.input = DMX_IN_FRONTEND; 01359 pesFilterParams.output = DMX_OUT_DECODER; 01360 pesFilterParams.pes_type= DMX_PES_OTHER; 01361 pesFilterParams.flags = DMX_IMMEDIATE_START; 01362 CHECK(ioctl(Handle->handle, DMX_SET_PES_FILTER, &pesFilterParams)); 01363 } 01364 close(Handle->handle); 01365 Handle->handle = -1; 01366 } 01367 } 01368 return true; 01369 } 01370 01371 int cDvbDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) 01372 { 01373 cString FileName = DvbName(DEV_DVB_DEMUX, adapter, frontend); 01374 int f = open(FileName, O_RDWR | O_NONBLOCK); 01375 if (f >= 0) { 01376 dmx_sct_filter_params sctFilterParams; 01377 memset(&sctFilterParams, 0, sizeof(sctFilterParams)); 01378 sctFilterParams.pid = Pid; 01379 sctFilterParams.timeout = 0; 01380 sctFilterParams.flags = DMX_IMMEDIATE_START; 01381 sctFilterParams.filter.filter[0] = Tid; 01382 sctFilterParams.filter.mask[0] = Mask; 01383 if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0) 01384 return f; 01385 else { 01386 esyslog("ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask); 01387 close(f); 01388 } 01389 } 01390 else 01391 esyslog("ERROR: can't open filter handle on '%s'", *FileName); 01392 return -1; 01393 } 01394 01395 void cDvbDevice::CloseFilter(int Handle) 01396 { 01397 close(Handle); 01398 } 01399 01400 bool cDvbDevice::ProvidesDeliverySystem(int DeliverySystem) const 01401 { 01402 for (int i = 0; i < numDeliverySystems; i++) { 01403 if (deliverySystems[i] == DeliverySystem) 01404 return true; 01405 } 01406 return false; 01407 } 01408 01409 bool cDvbDevice::ProvidesSource(int Source) const 01410 { 01411 int type = Source & cSource::st_Mask; 01412 return type == cSource::stNone 01413 || type == cSource::stAtsc && ProvidesDeliverySystem(SYS_ATSC) 01414 || type == cSource::stCable && (ProvidesDeliverySystem(SYS_DVBC_ANNEX_AC) || ProvidesDeliverySystem(SYS_DVBC_ANNEX_B)) 01415 || type == cSource::stSat && (ProvidesDeliverySystem(SYS_DVBS) || ProvidesDeliverySystem(SYS_DVBS2)) 01416 || type == cSource::stTerr && (ProvidesDeliverySystem(SYS_DVBT) || ProvidesDeliverySystem(SYS_DVBT2)); 01417 } 01418 01419 bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const 01420 { 01421 if (!ProvidesSource(Channel->Source())) 01422 return false; // doesn't provide source 01423 cDvbTransponderParameters dtp(Channel->Parameters()); 01424 if (!ProvidesDeliverySystem(GetRequiredDeliverySystem(Channel, &dtp)) || 01425 dtp.Modulation() == QPSK && !(frontendInfo.caps & FE_CAN_QPSK) || 01426 dtp.Modulation() == QAM_16 && !(frontendInfo.caps & FE_CAN_QAM_16) || 01427 dtp.Modulation() == QAM_32 && !(frontendInfo.caps & FE_CAN_QAM_32) || 01428 dtp.Modulation() == QAM_64 && !(frontendInfo.caps & FE_CAN_QAM_64) || 01429 dtp.Modulation() == QAM_128 && !(frontendInfo.caps & FE_CAN_QAM_128) || 01430 dtp.Modulation() == QAM_256 && !(frontendInfo.caps & FE_CAN_QAM_256) || 01431 dtp.Modulation() == QAM_AUTO && !(frontendInfo.caps & FE_CAN_QAM_AUTO) || 01432 dtp.Modulation() == VSB_8 && !(frontendInfo.caps & FE_CAN_8VSB) || 01433 dtp.Modulation() == VSB_16 && !(frontendInfo.caps & FE_CAN_16VSB) || 01434 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 01435 return false; // requires modulation system which frontend doesn't provide 01436 if (!cSource::IsSat(Channel->Source()) || 01437 (!Setup.DiSEqC || Diseqcs.Get(CardIndex() + 1, Channel->Source(), Channel->Frequency(), dtp.Polarization(), NULL))) 01438 return DeviceHooksProvidesTransponder(Channel); 01439 return false; 01440 } 01441 01442 bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const 01443 { 01444 bool result = false; 01445 bool hasPriority = Priority == IDLEPRIORITY || Priority > this->Priority(); 01446 bool needsDetachReceivers = false; 01447 needsDetachBondedReceivers = false; 01448 01449 if (dvbTuner && ProvidesTransponder(Channel)) { 01450 result = hasPriority; 01451 if (Priority > IDLEPRIORITY) { 01452 if (Receiving()) { 01453 if (dvbTuner->IsTunedTo(Channel)) { 01454 if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0)) || Channel->Dpid(0) && !HasPid(Channel->Dpid(0))) { 01455 if (CamSlot() && Channel->Ca() >= CA_ENCRYPTED_MIN) { 01456 if (CamSlot()->CanDecrypt(Channel)) 01457 result = true; 01458 else 01459 needsDetachReceivers = true; 01460 } 01461 else 01462 result = true; 01463 } 01464 else 01465 result = true; 01466 } 01467 else 01468 needsDetachReceivers = Receiving(); 01469 } 01470 if (result) { 01471 if (!BondingOk(Channel)) { 01472 // This device is bonded, so we need to check the priorities of the others: 01473 for (cDvbDevice *d = bondedDevice; d && d != this; d = d->bondedDevice) { 01474 if (d->Priority() >= Priority) { 01475 result = false; 01476 break; 01477 } 01478 } 01479 needsDetachBondedReceivers = true; 01480 needsDetachReceivers = Receiving(); 01481 } 01482 } 01483 } 01484 } 01485 if (NeedsDetachReceivers) 01486 *NeedsDetachReceivers = needsDetachReceivers; 01487 return result; 01488 } 01489 01490 bool cDvbDevice::ProvidesEIT(void) const 01491 { 01492 return dvbTuner != NULL; 01493 } 01494 01495 int cDvbDevice::NumProvidedSystems(void) const 01496 { 01497 return numDeliverySystems + numModulations; 01498 } 01499 01500 int cDvbDevice::SignalStrength(void) const 01501 { 01502 return dvbTuner ? dvbTuner->GetSignalStrength() : -1; 01503 } 01504 01505 int cDvbDevice::SignalQuality(void) const 01506 { 01507 return dvbTuner ? dvbTuner->GetSignalQuality() : -1; 01508 } 01509 01510 const cChannel *cDvbDevice::GetCurrentlyTunedTransponder(void) const 01511 { 01512 return dvbTuner ? dvbTuner->GetTransponder() : NULL; 01513 } 01514 01515 bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel) const 01516 { 01517 return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false; 01518 } 01519 01520 bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const 01521 { 01522 return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel); 01523 } 01524 01525 bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) 01526 { 01527 if (dvbTuner) 01528 dvbTuner->SetChannel(Channel); 01529 return true; 01530 } 01531 01532 bool cDvbDevice::HasLock(int TimeoutMs) 01533 { 01534 return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false; 01535 } 01536 01537 void cDvbDevice::SetTransferModeForDolbyDigital(int Mode) 01538 { 01539 setTransferModeForDolbyDigital = Mode; 01540 } 01541 01542 bool cDvbDevice::OpenDvr(void) 01543 { 01544 CloseDvr(); 01545 fd_dvr = DvbOpen(DEV_DVB_DVR, adapter, frontend, O_RDONLY | O_NONBLOCK, true); 01546 if (fd_dvr >= 0) 01547 tsBuffer = new cTSBuffer(fd_dvr, MEGABYTE(2), CardIndex() + 1); 01548 return fd_dvr >= 0; 01549 } 01550 01551 void cDvbDevice::CloseDvr(void) 01552 { 01553 if (fd_dvr >= 0) { 01554 delete tsBuffer; 01555 tsBuffer = NULL; 01556 close(fd_dvr); 01557 fd_dvr = -1; 01558 } 01559 } 01560 01561 bool cDvbDevice::GetTSPacket(uchar *&Data) 01562 { 01563 if (tsBuffer) { 01564 Data = tsBuffer->Get(); 01565 return true; 01566 } 01567 return false; 01568 } 01569 01570 void cDvbDevice::DetachAllReceivers(void) 01571 { 01572 cMutexLock MutexLock(&bondMutex); 01573 cDvbDevice *d = this; 01574 do { 01575 d->cDevice::DetachAllReceivers(); 01576 d = d->bondedDevice; 01577 } while (d && d != this && needsDetachBondedReceivers); 01578 needsDetachBondedReceivers = false; 01579 } 01580 01581 // --- cDvbDeviceProbe ------------------------------------------------------- 01582 01583 cList<cDvbDeviceProbe> DvbDeviceProbes; 01584 01585 cDvbDeviceProbe::cDvbDeviceProbe(void) 01586 { 01587 DvbDeviceProbes.Add(this); 01588 } 01589 01590 cDvbDeviceProbe::~cDvbDeviceProbe() 01591 { 01592 DvbDeviceProbes.Del(this, false); 01593 } 01594 01595 uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend) 01596 { 01597 uint32_t SubsystemId = 0; 01598 cString FileName = cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend); 01599 struct stat st; 01600 if (stat(FileName, &st) == 0) { 01601 cReadDir d("/sys/class/dvb"); 01602 if (d.Ok()) { 01603 struct dirent *e; 01604 while ((e = d.Next()) != NULL) { 01605 if (strstr(e->d_name, "frontend")) { 01606 FileName = cString::sprintf("/sys/class/dvb/%s/dev", e->d_name); 01607 if (FILE *f = fopen(FileName, "r")) { 01608 cReadLine ReadLine; 01609 char *s = ReadLine.Read(f); 01610 fclose(f); 01611 unsigned Major; 01612 unsigned Minor; 01613 if (s && 2 == sscanf(s, "%u:%u", &Major, &Minor)) { 01614 if (((Major << 8) | Minor) == st.st_rdev) { 01615 FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_vendor", e->d_name); 01616 if ((f = fopen(FileName, "r")) != NULL) { 01617 if (char *s = ReadLine.Read(f)) 01618 SubsystemId = strtoul(s, NULL, 0) << 16; 01619 fclose(f); 01620 } 01621 FileName = cString::sprintf("/sys/class/dvb/%s/device/subsystem_device", e->d_name); 01622 if ((f = fopen(FileName, "r")) != NULL) { 01623 if (char *s = ReadLine.Read(f)) 01624 SubsystemId |= strtoul(s, NULL, 0); 01625 fclose(f); 01626 } 01627 break; 01628 } 01629 } 01630 } 01631 } 01632 } 01633 } 01634 } 01635 return SubsystemId; 01636 }