diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 310c1a25..4251460c 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -441,6 +441,7 @@ Oliver Endriss Reinhard Walter Buchner for adding some satellites to 'sources.conf' + for his help in testing tuning with "Motor-DiSEqC" Lauri Tischler for helping to test and debug the new channel source and DiSEqC handling diff --git a/HISTORY b/HISTORY index ab4c5c15..c5f7d95e 100644 --- a/HISTORY +++ b/HISTORY @@ -1845,3 +1845,9 @@ Video Disk Recorder Revision History - Improved handling of repeated remote keys. - The RCU now only sets the channel number display when there are no incoming remote control keys, which improves reaction on repeated keys. +- The actual tuning is now done in a separate thread, which makes zapping through the + channels a lot faster and no longer gets stuck on channels that don't broadcast. + This also makes "Motor-DiSEqC" work (thanks to Reinhard Walter Buchner for his help + in testing this). Since switching channels now no longer explicitly waits for a + channel lock in the foreground thread, the "panic level" mechanism is no longer + used (maybe we don't need it nay more, anyway). diff --git a/diseqc.c b/diseqc.c index 92b92cbb..0766db8a 100644 --- a/diseqc.c +++ b/diseqc.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.c 1.1 2002/10/05 13:54:32 kls Exp $ + * $Id: diseqc.c 1.2 2002/12/07 13:44:56 kls Exp $ */ #include "diseqc.h" @@ -16,7 +16,6 @@ cDiseqc::cDiseqc(void) { commands = NULL; - currentAction = NULL;; parsing = false; numCodes = 0; } @@ -39,11 +38,11 @@ bool cDiseqc::Parse(const char *s) polarization = toupper(polarization); if (polarization == 'V' || polarization == 'H') { parsing = true; - bool Start = true; - while (Execute(Start) != daNone) - Start = false; + char *CurrentAction = NULL; + while (Execute(&CurrentAction) != daNone) + ; parsing = false; - result = !commands || currentAction && !*currentAction; + result = !commands || !*CurrentAction; } else esyslog("ERROR: unknown polarization '%c'", polarization); @@ -101,12 +100,12 @@ char *cDiseqc::Codes(char *s) return NULL; } -cDiseqc::eDiseqcActions cDiseqc::Execute(bool Start) +cDiseqc::eDiseqcActions cDiseqc::Execute(char **CurrentAction) { - if (Start) - currentAction = commands; - while (currentAction && *currentAction) { - switch (*currentAction++) { + if (!*CurrentAction) + *CurrentAction = commands; + while (*CurrentAction && **CurrentAction) { + switch (*(*CurrentAction)++) { case ' ': break; case 't': return daToneOff; case 'T': return daToneOn; @@ -114,8 +113,8 @@ cDiseqc::eDiseqcActions cDiseqc::Execute(bool Start) case 'V': return daVoltage18; case 'A': return daMiniA; case 'B': return daMiniB; - case 'W': currentAction = Wait(currentAction); break; - case '[': currentAction = Codes(currentAction); return currentAction ? daCodes : daNone; + case 'W': *CurrentAction = Wait(*CurrentAction); break; + case '[': *CurrentAction = Codes(*CurrentAction); return *CurrentAction ? daCodes : daNone; default: return daNone; } } diff --git a/diseqc.h b/diseqc.h index f2b0e99c..602bdecb 100644 --- a/diseqc.h +++ b/diseqc.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: diseqc.h 1.1 2002/10/05 13:02:52 kls Exp $ + * $Id: diseqc.h 1.2 2002/12/07 13:54:02 kls Exp $ */ #ifndef __DISEQC_H @@ -31,7 +31,6 @@ private: char polarization; int lof; char *commands; - char *currentAction; bool parsing; uchar codes[MaxDiseqcCodes]; int numCodes; @@ -41,7 +40,14 @@ public: cDiseqc(void); ~cDiseqc(); bool Parse(const char *s); - eDiseqcActions Execute(bool Start = false); + eDiseqcActions Execute(char **CurrentAction); + // Parses the DiSEqC commands and returns the appropriate action code + // with every call. CurrentAction must be the address of a character pointer, + // which is initialized to NULL. This pointer is used internally while parsing + // the commands and shall not be modified once Execute() has been called with + // it. Call Execute() repeatedly (always providing the same CurrentAction pointer) + // until it returns daNone. After a successful execution of all commands + // *CurrentAction points to the value 0x00. int Source(void) const { return source; } int Slof(void) const { return slof; } char Polarization(void) const { return polarization; } diff --git a/dvbdevice.c b/dvbdevice.c index e4b989d5..a2b4ce97 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 1.37 2002/11/16 12:36:50 kls Exp $ + * $Id: dvbdevice.c 1.38 2002/12/07 14:50:46 kls Exp $ */ #include "dvbdevice.h" @@ -60,8 +60,207 @@ static int DvbOpen(const char *Name, int n, int Mode, bool ReportError = false) return fd; } +// --- cDvbTuner ------------------------------------------------------------- + +class cDvbTuner : public cThread { +private: + enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; + int fd_frontend; + int cardIndex; + fe_type_t frontendType; + cChannel channel; + const char *diseqcCommands; + bool active; + eTunerStatus tunerStatus; + cMutex mutex; + cCondVar newSet; + bool SetFrontend(void); + virtual void Action(void); +public: + cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType); + virtual ~cDvbTuner(); + bool IsTunedTo(const cChannel *Channel) const; + void Set(const cChannel *Channel); + bool Locked(void) { return tunerStatus == tsLocked; } + }; + +cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType) +{ + fd_frontend = Fd_Frontend; + cardIndex = CardIndex; + frontendType = FrontendType; + diseqcCommands = NULL; + active = false; + tunerStatus = tsIdle; + Start(); +} + +cDvbTuner::~cDvbTuner() +{ + active = false; + tunerStatus = tsIdle; + newSet.Broadcast(); + Cancel(3); +} + +bool cDvbTuner::IsTunedTo(const cChannel *Channel) const +{ + return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Frequency() == Channel->Frequency(); +} + +void cDvbTuner::Set(const cChannel *Channel) +{ + cMutexLock MutexLock(&mutex); + channel = *Channel; + tunerStatus = tsSet; + newSet.Broadcast(); +} + +static unsigned int FrequencyToHz(unsigned int f) +{ + while (f && f < 1000000) + f *= 1000; + return f; +} + +bool cDvbTuner::SetFrontend(void) +{ + dvb_frontend_parameters Frontend; + + memset(&Frontend, 0, sizeof(Frontend)); + + switch (frontendType) { + case FE_QPSK: { // DVB-S + + unsigned int frequency = channel.Frequency(); + + if (Setup.DiSEqC) { + cDiseqc *diseqc = Diseqcs.Get(channel.Source(), channel.Frequency(), channel.Polarization()); + if (diseqc) { + if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) { + cDiseqc::eDiseqcActions da; + for (char *CurrentAction = NULL; (da = diseqc->Execute(&CurrentAction)) != cDiseqc::daNone; ) { + switch (da) { + case cDiseqc::daNone: break; + case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break; + case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break; + case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break; + case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break; + case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break; + case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break; + case cDiseqc::daCodes: { + int n = 0; + uchar *codes = diseqc->Codes(n); + if (codes) { + struct dvb_diseqc_master_cmd cmd; + memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg)))); + cmd.msg_len = n; + CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); + } + } + break; + } + } + diseqcCommands = diseqc->Commands(); + } + frequency -= diseqc->Lof(); + } + else { + esyslog("ERROR: no DiSEqC parameters found for channel %d", channel.Number()); + return false; + } + } + else { + int tone = SEC_TONE_OFF; + + if (frequency < (unsigned int)Setup.LnbSLOF) { + frequency -= Setup.LnbFrequLo; + tone = SEC_TONE_OFF; + } + else { + frequency -= Setup.LnbFrequHi; + tone = SEC_TONE_ON; + } + int volt = (channel.Polarization() == 'v' || channel.Polarization() == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; + CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt)); + CHECK(ioctl(fd_frontend, FE_SET_TONE, tone)); + } + + Frontend.frequency = frequency * 1000UL; + Frontend.inversion = fe_spectral_inversion_t(channel.Inversion()); + Frontend.u.qpsk.symbol_rate = channel.Srate() * 1000UL; + Frontend.u.qpsk.fec_inner = fe_code_rate_t(channel.CoderateH()); + } + break; + case FE_QAM: { // DVB-C + + // Frequency and symbol rate: + + Frontend.frequency = FrequencyToHz(channel.Frequency()); + Frontend.inversion = fe_spectral_inversion_t(channel.Inversion()); + Frontend.u.qam.symbol_rate = channel.Srate() * 1000UL; + Frontend.u.qam.fec_inner = fe_code_rate_t(channel.CoderateH()); + Frontend.u.qam.modulation = fe_modulation_t(channel.Modulation()); + } + break; + case FE_OFDM: { // DVB-T + + // Frequency and OFDM paramaters: + + Frontend.frequency = FrequencyToHz(channel.Frequency()); + Frontend.inversion = fe_spectral_inversion_t(channel.Inversion()); + Frontend.u.ofdm.bandwidth = fe_bandwidth_t(channel.Bandwidth()); + Frontend.u.ofdm.code_rate_HP = fe_code_rate_t(channel.CoderateH()); + Frontend.u.ofdm.code_rate_LP = fe_code_rate_t(channel.CoderateL()); + Frontend.u.ofdm.constellation = fe_modulation_t(channel.Modulation()); + Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(channel.Transmission()); + Frontend.u.ofdm.guard_interval = fe_guard_interval_t(channel.Guard()); + Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(channel.Hierarchy()); + } + break; + default: + esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); + return false; + } + if (ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend) < 0) { + esyslog("ERROR: frontend %d: %m", cardIndex); + return false; + } + return true; +} + +void cDvbTuner::Action(void) +{ + dsyslog("tuner thread started on device %d (pid=%d)", cardIndex + 1, getpid()); + active = true; + while (active) { + cMutexLock MutexLock(&mutex); + if (tunerStatus == tsSet) + tunerStatus = SetFrontend() ? tsTuned : tsIdle; + if (tunerStatus == tsTuned) { + fe_status_t status = fe_status_t(0); + CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status)); + if (status & FE_HAS_LOCK) + tunerStatus = tsLocked; + } + dvb_frontend_event event; + if (ioctl(fd_frontend, FE_GET_EVENT, &event) == 0) { + if (tunerStatus != tsIdle && event.status & FE_REINIT) { + tunerStatus = tsSet; + esyslog("ERROR: frontend %d was reinitialized - re-tuning", cardIndex); + continue; + } + } + newSet.TimedWait(mutex, 1000); + } + dsyslog("tuner thread ended on device %d (pid=%d)", cardIndex + 1, getpid()); +} + +// --- cDvbDevice ------------------------------------------------------------ + cDvbDevice::cDvbDevice(int n) { + dvbTuner = NULL; frontendType = fe_type_t(-1); // don't know how else to initialize this - there is no FE_UNKNOWN siProcessor = NULL; spuDecoder = NULL; @@ -69,7 +268,7 @@ cDvbDevice::cDvbDevice(int n) // Devices that are present on all card types: - fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK); + int fd_frontend = DvbOpen(DEV_DVB_FRONTEND, n, O_RDWR | O_NONBLOCK); // Devices that are only present on cards with decoders: @@ -90,8 +289,10 @@ cDvbDevice::cDvbDevice(int n) if (fd_frontend >= 0) { dvb_frontend_info feinfo; siProcessor = new cSIProcessor(DvbName(DEV_DVB_DEMUX, n)); - if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) + if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) { frontendType = feinfo.type; + dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType); + } else LOG_ERROR; } @@ -99,16 +300,13 @@ cDvbDevice::cDvbDevice(int n) esyslog("ERROR: can't open DVB device %d", n); aPid1 = aPid2 = 0; - - source = -1; - frequency = -1; - diseqcCommands = NULL; } cDvbDevice::~cDvbDevice() { delete spuDecoder; delete siProcessor; + delete dvbTuner; // We're not explicitly closing any device files here, since this sometimes // caused segfaults. Besides, the program is about to terminate anyway... } @@ -322,11 +520,6 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On) return true; } -bool cDvbDevice::IsTunedTo(const cChannel *Channel) const -{ - return source == Channel->Source() && frequency == Channel->Frequency(); -} - bool cDvbDevice::ProvidesSource(int Source) const { int type = Source & cSource::st_Mask; @@ -346,7 +539,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) { #ifdef DO_MULTIPLE_RECORDINGS if (Receiving()) { - if (IsTunedTo(Channel)) { + if (dvbTuner->IsTunedTo(Channel)) { needsDetachReceivers = false; if (!HasPid(Channel->Vpid())) { if (Channel->Ca() > CACONFBASE) { @@ -375,18 +568,11 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne return result; } -static unsigned int FrequencyToHz(unsigned int f) -{ - while (f && f < 1000000) - f *= 1000; - return f; -} - bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) { bool IsEncrypted = Channel->Ca() > CACONFBASE; - bool DoTune = !IsTunedTo(Channel); + bool DoTune = !dvbTuner->IsTunedTo(Channel); bool TurnOffLivePIDs = HasDecoder() && (DoTune @@ -436,136 +622,15 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) } if (DoTune) { - - dvb_frontend_parameters Frontend; - - memset(&Frontend, 0, sizeof(Frontend)); - - switch (frontendType) { - case FE_QPSK: { // DVB-S - - unsigned int frequency = Channel->Frequency(); - - if (Setup.DiSEqC) { - cDiseqc *diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization()); - if (diseqc) { - if (diseqc->Commands() && (!diseqcCommands || strcmp(diseqcCommands, diseqc->Commands()) != 0)) { - cDiseqc::eDiseqcActions da; - for (bool Start = true; (da = diseqc->Execute(Start)) != cDiseqc::daNone; Start = false) { - switch (da) { - case cDiseqc::daNone: break; - case cDiseqc::daToneOff: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF)); break; - case cDiseqc::daToneOn: CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_ON)); break; - case cDiseqc::daVoltage13: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_13)); break; - case cDiseqc::daVoltage18: CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, SEC_VOLTAGE_18)); break; - case cDiseqc::daMiniA: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_A)); break; - case cDiseqc::daMiniB: CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_BURST, SEC_MINI_B)); break; - case cDiseqc::daCodes: { - int n = 0; - uchar *codes = diseqc->Codes(n); - if (codes) { - struct dvb_diseqc_master_cmd cmd; - memcpy(cmd.msg, codes, min(n, int(sizeof(cmd.msg)))); - cmd.msg_len = n; - CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &cmd)); - } - } - break; - } - } - diseqcCommands = diseqc->Commands(); - } - frequency -= diseqc->Lof(); - } - else { - esyslog("ERROR: no DiSEqC parameters found for channel %d", Channel->Number()); - return false; - } - } - else { - int tone = SEC_TONE_OFF; - - if (frequency < (unsigned int)Setup.LnbSLOF) { - frequency -= Setup.LnbFrequLo; - tone = SEC_TONE_OFF; - } - else { - frequency -= Setup.LnbFrequHi; - tone = SEC_TONE_ON; - } - int volt = (Channel->Polarization() == 'v' || Channel->Polarization() == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; - CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, volt)); - CHECK(ioctl(fd_frontend, FE_SET_TONE, tone)); - } - - Frontend.frequency = frequency * 1000UL; - Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion()); - Frontend.u.qpsk.symbol_rate = Channel->Srate() * 1000UL; - Frontend.u.qpsk.fec_inner = fe_code_rate_t(Channel->CoderateH()); - } - break; - case FE_QAM: { // DVB-C - - // Frequency and symbol rate: - - Frontend.frequency = FrequencyToHz(Channel->Frequency()); - Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion()); - Frontend.u.qam.symbol_rate = Channel->Srate() * 1000UL; - Frontend.u.qam.fec_inner = fe_code_rate_t(Channel->CoderateH()); - Frontend.u.qam.modulation = fe_modulation_t(Channel->Modulation()); - } - break; - case FE_OFDM: { // DVB-T - - // Frequency and OFDM paramaters: - - Frontend.frequency = FrequencyToHz(Channel->Frequency()); - Frontend.inversion = fe_spectral_inversion_t(Channel->Inversion()); - Frontend.u.ofdm.bandwidth = fe_bandwidth_t(Channel->Bandwidth()); - Frontend.u.ofdm.code_rate_HP = fe_code_rate_t(Channel->CoderateH()); - Frontend.u.ofdm.code_rate_LP = fe_code_rate_t(Channel->CoderateL()); - Frontend.u.ofdm.constellation = fe_modulation_t(Channel->Modulation()); - Frontend.u.ofdm.transmission_mode = fe_transmit_mode_t(Channel->Transmission()); - Frontend.u.ofdm.guard_interval = fe_guard_interval_t(Channel->Guard()); - Frontend.u.ofdm.hierarchy_information = fe_hierarchy_t(Channel->Hierarchy()); - } - break; - default: - esyslog("ERROR: attempt to set channel with unknown DVB frontend type"); - return false; - } - - // Discard stale events: - - for (;;) { - dvb_frontend_event event; - if (ioctl(fd_frontend, FE_GET_EVENT, &event) < 0) - break; - } - - // Tuning: - - CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); - - // Wait for channel lock: - - fe_status_t status = fe_status_t(0); - for (int i = 0; i < 100; i++) { - CHECK(ioctl(fd_frontend, FE_READ_STATUS, &status)); - if (status & FE_HAS_LOCK) - break; - usleep(10 * 1000); - } + dvbTuner->Set(Channel); + /*XXX do we still need this??? if (!(status & FE_HAS_LOCK)) { esyslog("ERROR: channel %d not locked on DVB card %d!", Channel->Number(), CardIndex() + 1); if (LiveView && IsPrimaryDevice()) cThread::RaisePanic(); return false; } - - source = Channel->Source(); - frequency = Channel->Frequency(); - + XXX*/ } // PID settings: diff --git a/dvbdevice.h b/dvbdevice.h index 709790cd..d135a298 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.h 1.18 2002/11/03 12:31:15 kls Exp $ + * $Id: dvbdevice.h 1.19 2002/12/07 14:44:29 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -22,6 +22,8 @@ #define MAXDVBDEVICES 4 +class cDvbTuner; + class cDvbDevice : public cDevice { friend class cDvbOsd; private: @@ -33,7 +35,7 @@ public: // Must be called before accessing any DVB functions. private: fe_type_t frontendType; - int fd_osd, fd_frontend, fd_audio, fd_video, fd_dvr; + int fd_osd, fd_audio, fd_video, fd_dvr; int OsdDeviceHandle(void) const { return fd_osd; } protected: virtual void MakePrimaryDevice(bool On); @@ -53,10 +55,7 @@ public: // Channel facilities private: - int source; - int frequency; - const char *diseqcCommands; - bool IsTunedTo(const cChannel *Channel) const; + cDvbTuner *dvbTuner; public: virtual bool ProvidesSource(int Source) const; virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;