vdr  1.7.27
diseqc.c
Go to the documentation of this file.
00001 /*
00002  * diseqc.c: DiSEqC handling
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: diseqc.c 2.9 2011/09/17 14:13:31 kls Exp $
00008  */
00009 
00010 #include "diseqc.h"
00011 #include <ctype.h>
00012 #include "sources.h"
00013 #include "thread.h"
00014 
00015 static bool ParseDeviceNumbers(const char *s, int &Devices)
00016 {
00017   if (*s && s[strlen(s) - 1] == ':') {
00018      const char *p = s;
00019      while (*p && *p != ':') {
00020            char *t = NULL;
00021            int d = strtol(p, &t, 10);
00022            p = t;
00023            if (0 < d && d < 31)
00024               Devices |= (1 << d - 1);
00025            else {
00026               esyslog("ERROR: invalid device number %d in '%s'", d, s);
00027               return false;
00028               }
00029            }
00030      }
00031   return true;
00032 }
00033 
00034 // --- cScr ------------------------------------------------------------------
00035 
00036 cScr::cScr(void)
00037 {
00038   devices = 0;
00039   channel = -1;
00040   userBand = 0;
00041   pin = -1;
00042   used = false;
00043 }
00044 
00045 bool cScr::Parse(const char *s)
00046 {
00047   if (!ParseDeviceNumbers(s, devices))
00048      return false;
00049   if (devices)
00050      return true;
00051   bool result = false;
00052   int fields = sscanf(s, "%d %u %d", &channel, &userBand, &pin);
00053   if (fields == 2 || fields == 3) {
00054      if (channel >= 0 && channel < 8) {
00055         result = true;
00056         if (fields == 3 && (pin < 0 || pin > 255)) {
00057            esyslog("Error: invalid SCR pin '%d'", pin);
00058            result = false;
00059            }
00060         }
00061      else
00062         esyslog("Error: invalid SCR channel '%d'", channel);
00063      }
00064   return result;
00065 }
00066 
00067 // --- cScrs -----------------------------------------------------------------
00068 
00069 cScrs Scrs;
00070 
00071 cScr *cScrs::GetUnused(int Device)
00072 {
00073   cMutexLock MutexLock(&mutex);
00074   int Devices = 0;
00075   for (cScr *p = First(); p; p = Next(p)) {
00076       if (p->Devices()) {
00077          Devices = p->Devices();
00078          continue;
00079          }
00080       if (Devices && !(Devices & (1 << Device - 1)))
00081          continue;
00082       if (!p->Used()) {
00083         p->SetUsed(true);
00084         return p;
00085         }
00086       }
00087   return NULL;
00088 }
00089 
00090 // --- cDiseqc ---------------------------------------------------------------
00091 
00092 cDiseqc::cDiseqc(void)
00093 {
00094   devices = 0;
00095   source = 0;
00096   slof = 0;
00097   polarization = 0;
00098   lof = 0;
00099   scrBank = -1;
00100   commands = NULL;
00101   parsing = false;
00102 }
00103 
00104 cDiseqc::~cDiseqc()
00105 {
00106   free(commands);
00107 }
00108 
00109 bool cDiseqc::Parse(const char *s)
00110 {
00111   if (!ParseDeviceNumbers(s, devices))
00112      return false;
00113   if (devices)
00114      return true;
00115   bool result = false;
00116   char *sourcebuf = NULL;
00117   int fields = sscanf(s, "%a[^ ] %d %c %d %a[^\n]", &sourcebuf, &slof, &polarization, &lof, &commands);
00118   if (fields == 4)
00119      commands = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %a argument results in an empty string
00120   if (4 <= fields && fields <= 5) {
00121      source = cSource::FromString(sourcebuf);
00122      if (Sources.Get(source)) {
00123         polarization = char(toupper(polarization));
00124         if (polarization == 'V' || polarization == 'H' || polarization == 'L' || polarization == 'R') {
00125            parsing = true;
00126            const char *CurrentAction = NULL;
00127            while (Execute(&CurrentAction, NULL, NULL, NULL, NULL) != daNone)
00128                  ;
00129            parsing = false;
00130            result = !commands || !*CurrentAction;
00131            }
00132         else
00133            esyslog("ERROR: unknown polarization '%c'", polarization);
00134         }
00135      else
00136         esyslog("ERROR: unknown source '%s'", sourcebuf);
00137      }
00138   free(sourcebuf);
00139   return result;
00140 }
00141 
00142 uint cDiseqc::SetScrFrequency(uint SatFrequency, const cScr *Scr, uint8_t *Codes) const
00143 {
00144   uint t = SatFrequency == 0 ? 0 : (SatFrequency + Scr->UserBand() + 2) / 4 - 350; // '+ 2' together with '/ 4' results in rounding!
00145   if (t < 1024 && Scr->Channel() >= 0 && Scr->Channel() < 8) {
00146      Codes[3] = t >> 8 | (t == 0 ? 0 : scrBank << 2) | Scr->Channel() << 5;
00147      Codes[4] = t;
00148      if (t)
00149         return (t + 350) * 4 - SatFrequency;
00150      }
00151   return 0;
00152 }
00153 
00154 int cDiseqc::SetScrPin(const cScr *Scr, uint8_t *Codes) const
00155 {
00156   if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
00157      Codes[2] = 0x5C;
00158      Codes[5] = Scr->Pin();
00159      return 6;
00160      }
00161   else {
00162      Codes[2] = 0x5A;
00163      return 5;
00164      }
00165 }
00166 
00167 const char *cDiseqc::Wait(const char *s) const
00168 {
00169   char *p = NULL;
00170   errno = 0;
00171   int n = strtol(s, &p, 10);
00172   if (!errno && p != s && n >= 0) {
00173      if (!parsing)
00174         cCondWait::SleepMs(n);
00175      return p;
00176      }
00177   esyslog("ERROR: invalid value for wait time in '%s'", s - 1);
00178   return NULL;
00179 }
00180 
00181 const char *cDiseqc::GetScrBank(const char *s) const
00182 {
00183   char *p = NULL;
00184   errno = 0;
00185   int n = strtol(s, &p, 10);
00186   if (!errno && p != s && n >= 0 && n < 8) {
00187      if (parsing) {
00188         if (scrBank < 0)
00189            scrBank = n;
00190         else
00191            esyslog("ERROR: more than one scr bank in '%s'", s - 1);
00192         }
00193      return p;
00194      }
00195   esyslog("ERROR: more than one scr bank in '%s'", s - 1);
00196   return NULL;
00197 }
00198 
00199 const char *cDiseqc::GetCodes(const char *s, uchar *Codes, uint8_t *MaxCodes) const
00200 {
00201   const char *e = strchr(s, ']');
00202   if (e) {
00203      int NumCodes = 0;
00204      const char *t = s;
00205      while (t < e) {
00206            if (NumCodes < MaxDiseqcCodes) {
00207               errno = 0;
00208               char *p;
00209               int n = strtol(t, &p, 16);
00210               if (!errno && p != t && 0 <= n && n <= 255) {
00211                  if (Codes) {
00212                     if (NumCodes < *MaxCodes)
00213                        Codes[NumCodes++] = uchar(n);
00214                     else {
00215                        esyslog("ERROR: too many codes in code sequence '%s'", s - 1);
00216                        return NULL;
00217                        }
00218                     }
00219                  t = skipspace(p);
00220                  }
00221               else {
00222                  esyslog("ERROR: invalid code at '%s'", t);
00223                  return NULL;
00224                  }
00225               }
00226            else {
00227               esyslog("ERROR: too many codes in code sequence '%s'", s - 1);
00228               return NULL;
00229               }
00230            }
00231      if (MaxCodes)
00232         *MaxCodes = NumCodes;
00233      return e + 1;
00234      }
00235   else
00236      esyslog("ERROR: missing closing ']' in code sequence '%s'", s - 1);
00237   return NULL;
00238 }
00239 
00240 cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, uint *Frequency) const
00241 {
00242   if (!*CurrentAction)
00243      *CurrentAction = commands;
00244   while (*CurrentAction && **CurrentAction) {
00245         switch (*(*CurrentAction)++) {
00246           case ' ': break;
00247           case 't': return daToneOff;
00248           case 'T': return daToneOn;
00249           case 'v': return daVoltage13;
00250           case 'V': return daVoltage18;
00251           case 'A': return daMiniA;
00252           case 'B': return daMiniB;
00253           case 'W': *CurrentAction = Wait(*CurrentAction); break;
00254           case 'S': *CurrentAction = GetScrBank(*CurrentAction); break;
00255           case '[': *CurrentAction = GetCodes(*CurrentAction, Codes, MaxCodes);
00256                     if (*CurrentAction) {
00257                        if (Scr && Frequency) {
00258                           *Frequency = SetScrFrequency(*Frequency, Scr, Codes);
00259                           *MaxCodes = SetScrPin(Scr, Codes);
00260                           }
00261                        return daCodes;
00262                        }
00263                     break;
00264           default: return daNone;
00265           }
00266         }
00267   return daNone;
00268 }
00269 
00270 // --- cDiseqcs --------------------------------------------------------------
00271 
00272 cDiseqcs Diseqcs;
00273 
00274 const cDiseqc *cDiseqcs::Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const
00275 {
00276   int Devices = 0;
00277   for (const cDiseqc *p = First(); p; p = Next(p)) {
00278       if (p->Devices()) {
00279          Devices = p->Devices();
00280          continue;
00281          }
00282       if (Devices && !(Devices & (1 << Device - 1)))
00283          continue;
00284       if (p->Source() == Source && p->Slof() > Frequency && p->Polarization() == toupper(Polarization)) {
00285          if (p->IsScr() && Scr && !*Scr) {
00286             *Scr = Scrs.GetUnused(Device);
00287             if (*Scr)
00288                dsyslog("SCR %d assigned to device %d", (*Scr)->Channel(), Device);
00289             else
00290                esyslog("ERROR: no free SCR entry available for device %d", Device);
00291             }
00292          return p;
00293          }
00294       }
00295   return NULL;
00296 }