vdr  2.2.0
diseqc.c
Go to the documentation of this file.
1 /*
2  * diseqc.c: DiSEqC handling
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: diseqc.c 3.4 2015/01/26 12:02:14 kls Exp $
8  */
9 
10 #include "diseqc.h"
11 #include <ctype.h>
12 #include <linux/dvb/frontend.h>
13 #include <sys/ioctl.h>
14 #include "sources.h"
15 #include "thread.h"
16 
17 #define ALL_DEVICES (~0) // all bits set to '1'
18 #define MAX_DEVICES 32 // each bit in a 32-bit integer represents one device
19 
20 static int CurrentDevices = 0;
21 
22 static bool IsDeviceNumbers(const char *s)
23 {
24  return *s && s[strlen(s) - 1] == ':';
25 }
26 
27 static bool ParseDeviceNumbers(const char *s)
28 {
29  if (IsDeviceNumbers(s)) {
30  CurrentDevices = 0;
31  const char *p = s;
32  while (*p && *p != ':') {
33  char *t = NULL;
34  int d = strtol(p, &t, 10);
35  p = t;
36  if (0 < d && d <= MAX_DEVICES)
37  CurrentDevices |= (1 << d - 1);
38  else {
39  esyslog("ERROR: invalid device number %d in '%s'", d, s);
40  return false;
41  }
42  }
43  }
44  return true;
45 }
46 
47 // --- cDiseqcPositioner -----------------------------------------------------
48 
49 // See http://www.eutelsat.com/files/live/sites/eutelsatv2/files/contributed/satellites/pdf/Diseqc/associated%20docs/positioner_appli_notice.pdf
50 
52 {
54  pcCanStep |
55  pcCanHalt |
63  );
64 }
65 
66 void cDiseqcPositioner::SendDiseqc(uint8_t *Codes, int NumCodes)
67 {
68  struct dvb_diseqc_master_cmd cmd;
69  NumCodes = min(NumCodes, int(sizeof(cmd.msg) - 2));
70  cmd.msg_len = 0;
71  cmd.msg[cmd.msg_len++] = 0xE0;
72  cmd.msg[cmd.msg_len++] = 0x31;
73  for (int i = 0; i < NumCodes; i++)
74  cmd.msg[cmd.msg_len++] = Codes[i];
75  CHECK(ioctl(Frontend(), FE_DISEQC_SEND_MASTER_CMD, &cmd));
76 }
77 
79 {
80  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x68 : 0x69), 0x00 };
81  SendDiseqc(Code, 2);
82 }
83 
84 void cDiseqcPositioner::Step(ePositionerDirection Direction, uint Steps)
85 {
86  if (Steps == 0)
87  return;
88  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x68 : 0x69), 0xFF };
89  Code[1] -= min(Steps, uint(0x7F)) - 1;
90  SendDiseqc(Code, 2);
91 }
92 
94 {
95  uint8_t Code[] = { 0x60 };
96  SendDiseqc(Code, 1);
97 }
98 
100 {
101  uint8_t Code[] = { uint8_t(Direction == pdLeft ? 0x66 : 0x67) };
102  SendDiseqc(Code, 1);
103 }
104 
106 {
107  uint8_t Code[] = { 0x63 };
108  SendDiseqc(Code, 1);
109 }
110 
112 {
113  uint8_t Code[] = { 0x6A, 0x00 };
114  SendDiseqc(Code, 2);
115 }
116 
118 {
119  uint8_t Code[] = { 0x6A, uint8_t(Number) };
120  SendDiseqc(Code, 2);
121 }
122 
124 {
125  uint8_t Code[] = { 0x6F, uint8_t(Number), 0x00, 0x00 };
126  SendDiseqc(Code, 4);
127 }
128 
129 void cDiseqcPositioner::GotoPosition(uint Number, int Longitude)
130 {
131  uint8_t Code[] = { 0x6B, uint8_t(Number) };
132  SendDiseqc(Code, 2);
133  cPositioner::GotoPosition(Number, Longitude);
134 }
135 
136 void cDiseqcPositioner::GotoAngle(int Longitude)
137 {
138  uint8_t Code[] = { 0x6E, 0x00, 0x00 };
139  int Angle = CalcHourAngle(Longitude);
140  int a = abs(Angle);
141  Code[1] = a / 10 / 16;
142  Code[2] = a / 10 % 16 * 16 + a % 10 * 16 / 10;
143  Code[1] |= (Angle < 0) ? 0xE0 : 0xD0;
144  SendDiseqc(Code, 3);
145  cPositioner::GotoAngle(Longitude);
146 }
147 
148 // --- cScr ------------------------------------------------------------------
149 
151 {
152  devices = 0;
153  channel = -1;
154  userBand = 0;
155  pin = -1;
156  used = false;
157 }
158 
159 bool cScr::Parse(const char *s)
160 {
161  if (IsDeviceNumbers(s))
162  return ParseDeviceNumbers(s);
163  devices = CurrentDevices;
164  bool result = false;
165  int fields = sscanf(s, "%d %u %d", &channel, &userBand, &pin);
166  if (fields == 2 || fields == 3) {
167  if (channel >= 0 && channel < 32) {
168  result = true;
169  if (fields == 3 && (pin < 0 || pin > 255)) {
170  esyslog("Error: invalid SCR pin '%d'", pin);
171  result = false;
172  }
173  }
174  else
175  esyslog("Error: invalid SCR channel '%d'", channel);
176  }
177  return result;
178 }
179 
180 // --- cScrs -----------------------------------------------------------------
181 
183 
184 bool cScrs::Load(const char *FileName, bool AllowComments, bool MustExist)
185 {
187  return cConfig<cScr>::Load(FileName, AllowComments, MustExist);
188 }
189 
190 cScr *cScrs::GetUnused(int Device)
191 {
192  cMutexLock MutexLock(&mutex);
193  for (cScr *p = First(); p; p = Next(p)) {
194  if (!IsBitSet(p->Devices(), Device - 1))
195  continue;
196  if (!p->Used()) {
197  p->SetUsed(true);
198  return p;
199  }
200  }
201  return NULL;
202 }
203 
204 // --- cDiseqc ---------------------------------------------------------------
205 
207 {
208  devices = 0;
209  source = 0;
210  slof = 0;
211  polarization = 0;
212  lof = 0;
213  position = -1;
214  scrBank = -1;
215  commands = NULL;
216  parsing = false;
217 }
218 
220 {
221  free(commands);
222 }
223 
224 bool cDiseqc::Parse(const char *s)
225 {
226  if (IsDeviceNumbers(s))
227  return ParseDeviceNumbers(s);
228  devices = CurrentDevices;
229  bool result = false;
230  char *sourcebuf = NULL;
231  int fields = sscanf(s, "%m[^ ] %d %c %d %m[^\n]", &sourcebuf, &slof, &polarization, &lof, &commands);
232  if (fields == 4)
233  commands = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %m argument results in an empty string
234  if (4 <= fields && fields <= 5) {
235  source = cSource::FromString(sourcebuf);
236  if (Sources.Get(source)) {
237  polarization = char(toupper(polarization));
238  if (polarization == 'V' || polarization == 'H' || polarization == 'L' || polarization == 'R') {
239  parsing = true;
240  const char *CurrentAction = NULL;
241  while (Execute(&CurrentAction, NULL, NULL, NULL, NULL) != daNone)
242  ;
243  parsing = false;
244  result = !commands || !*CurrentAction;
245  }
246  else
247  esyslog("ERROR: unknown polarization '%c'", polarization);
248  }
249  else
250  esyslog("ERROR: unknown source '%s'", sourcebuf);
251  }
252  free(sourcebuf);
253  return result;
254 }
255 
256 uint cDiseqc::SetScrFrequency(uint SatFrequency, const cScr *Scr, uint8_t *Codes) const
257 {
258  if ((Codes[0] & 0xF0) == 0x70 ) { // EN50607 aka JESS
259  uint t = SatFrequency == 0 ? 0 : (SatFrequency - 100);
260  if (t < 2048 && Scr->Channel() >= 0 && Scr->Channel() < 32) {
261  Codes[1] = t >> 8 | Scr->Channel() << 3;
262  Codes[2] = t;
263  Codes[3] = (t == 0 ? 0 : scrBank);
264  if (t)
265  return Scr->UserBand();
266  }
267  }
268  else { // EN50494 aka Unicable
269  uint t = SatFrequency == 0 ? 0 : (SatFrequency + Scr->UserBand() + 2) / 4 - 350; // '+ 2' together with '/ 4' results in rounding!
270  if (t < 1024 && Scr->Channel() >= 0 && Scr->Channel() < 8) {
271  Codes[3] = t >> 8 | (t == 0 ? 0 : scrBank << 2) | Scr->Channel() << 5;
272  Codes[4] = t;
273  if (t)
274  return (t + 350) * 4 - SatFrequency;
275  }
276  }
277  esyslog("ERROR: invalid SCR channel number %d or frequency %d", Scr->Channel(),SatFrequency);
278  return 0;
279 }
280 
281 int cDiseqc::SetScrPin(const cScr *Scr, uint8_t *Codes) const
282 {
283  if ((Codes[0] & 0xF0) == 0x70 ) { // EN50607 aka JESS
284  if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
285  Codes[0] = 0x71;
286  Codes[4] = Scr->Pin();
287  return 5;
288  }
289  else {
290  Codes[0] = 0x70;
291  return 4;
292  }
293  }
294  else { // EN50494 aka Unicable
295  if (Scr->Pin() >= 0 && Scr->Pin() <= 255) {
296  Codes[2] = 0x5C;
297  Codes[5] = Scr->Pin();
298  return 6;
299  }
300  else {
301  Codes[2] = 0x5A;
302  return 5;
303  }
304  }
305 }
306 
307 const char *cDiseqc::Wait(const char *s) const
308 {
309  char *p = NULL;
310  errno = 0;
311  int n = strtol(s, &p, 10);
312  if (!errno && p != s && n >= 0) {
313  if (!parsing)
315  return p;
316  }
317  esyslog("ERROR: invalid value for wait time in '%s'", s - 1);
318  return NULL;
319 }
320 
321 const char *cDiseqc::GetPosition(const char *s) const
322 {
323  if (!*s || !isdigit(*s)) {
324  position = 0;
325  return s;
326  }
327  char *p = NULL;
328  errno = 0;
329  int n = strtol(s, &p, 10);
330  if (!errno && p != s && n >= 0 && n < 0xFF) {
331  if (parsing) {
332  if (position < 0)
333  position = n;
334  else
335  esyslog("ERROR: more than one position in '%s'", s - 1);
336  }
337  return p;
338  }
339  esyslog("ERROR: invalid satellite position in '%s'", s - 1);
340  return NULL;
341 }
342 
343 const char *cDiseqc::GetScrBank(const char *s) const
344 {
345  char *p = NULL;
346  errno = 0;
347  int n = strtol(s, &p, 10);
348  if (!errno && p != s && n >= 0 && n < 256) {
349  if (parsing) {
350  if (scrBank < 0)
351  scrBank = n;
352  else
353  esyslog("ERROR: more than one scr bank in '%s'", s - 1);
354  }
355  return p;
356  }
357  esyslog("ERROR: invalid value for scr bank in '%s'", s - 1);
358  return NULL;
359 }
360 
361 const char *cDiseqc::GetCodes(const char *s, uchar *Codes, uint8_t *MaxCodes) const
362 {
363  const char *e = strchr(s, ']');
364  if (e) {
365  int NumCodes = 0;
366  const char *t = s;
367  while (t < e) {
368  if (NumCodes < MaxDiseqcCodes) {
369  errno = 0;
370  char *p;
371  int n = strtol(t, &p, 16);
372  if (!errno && p != t && 0 <= n && n <= 255) {
373  if (Codes) {
374  if (NumCodes < *MaxCodes)
375  Codes[NumCodes++] = uchar(n);
376  else {
377  esyslog("ERROR: too many codes in code sequence '%s'", s - 1);
378  return NULL;
379  }
380  }
381  t = skipspace(p);
382  }
383  else {
384  esyslog("ERROR: invalid code at '%s'", t);
385  return NULL;
386  }
387  }
388  else {
389  esyslog("ERROR: too many codes in code sequence '%s'", s - 1);
390  return NULL;
391  }
392  }
393  if (MaxCodes)
394  *MaxCodes = NumCodes;
395  return e + 1;
396  }
397  else
398  esyslog("ERROR: missing closing ']' in code sequence '%s'", s - 1);
399  return NULL;
400 }
401 
402 cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, uint *Frequency) const
403 {
404  if (!*CurrentAction)
405  *CurrentAction = commands;
406  while (*CurrentAction && **CurrentAction) {
407  switch (*(*CurrentAction)++) {
408  case ' ': break;
409  case 't': return daToneOff;
410  case 'T': return daToneOn;
411  case 'v': return daVoltage13;
412  case 'V': return daVoltage18;
413  case 'A': return daMiniA;
414  case 'B': return daMiniB;
415  case 'W': *CurrentAction = Wait(*CurrentAction); return daWait;
416  case 'P': *CurrentAction = GetPosition(*CurrentAction);
417  if (Setup.UsePositioner)
418  return position ? daPositionN : daPositionA;
419  break;
420  case 'S': *CurrentAction = GetScrBank(*CurrentAction); return daScr;
421  case '[': *CurrentAction = GetCodes(*CurrentAction, Codes, MaxCodes);
422  if (*CurrentAction) {
423  if (Scr && Frequency) {
424  *Frequency = SetScrFrequency(*Frequency, Scr, Codes);
425  *MaxCodes = SetScrPin(Scr, Codes);
426  }
427  return daCodes;
428  }
429  break;
430  default: esyslog("ERROR: unknown diseqc code '%c'", *(*CurrentAction - 1));
431  return daNone;
432  }
433  }
434  return daNone;
435 }
436 
437 // --- cDiseqcs --------------------------------------------------------------
438 
440 
441 bool cDiseqcs::Load(const char *FileName, bool AllowComments, bool MustExist)
442 {
444  return cConfig<cDiseqc>::Load(FileName, AllowComments, MustExist);
445 }
446 
447 const cDiseqc *cDiseqcs::Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const
448 {
449  for (const cDiseqc *p = First(); p; p = Next(p)) {
450  if (!IsBitSet(p->Devices(), Device - 1))
451  continue;
452  if (cSource::Matches(p->Source(), Source) && p->Slof() > Frequency && p->Polarization() == toupper(Polarization)) {
453  if (p->IsScr() && Scr && !*Scr) {
454  *Scr = Scrs.GetUnused(Device);
455  if (*Scr)
456  dsyslog("SCR %d assigned to device %d", (*Scr)->Channel(), Device);
457  else
458  esyslog("ERROR: no free SCR entry available for device %d", Device);
459  }
460  return p;
461  }
462  }
463  return NULL;
464 }
virtual void DisableLimits(void)
Disables the soft limits for the dish movement.
Definition: diseqc.c:105
static int CalcHourAngle(int Longitude)
Takes the longitude and latitude of the dish location from the system setup and the given Longitude t...
Definition: positioner.c:51
cDiseqcs Diseqcs
Definition: diseqc.c:439
unsigned char uchar
Definition: tools.h:30
const char * Wait(const char *s) const
Definition: diseqc.c:307
~cDiseqc()
Definition: diseqc.c:219
#define dsyslog(a...)
Definition: tools.h:36
uint UserBand(void) const
Definition: diseqc.h:46
bool Parse(const char *s)
Definition: diseqc.c:159
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition: positioner.c:100
Definition: diseqc.h:52
virtual void EnableLimits(void)
Enables the soft limits for the dish movement.
Definition: diseqc.c:111
#define esyslog(a...)
Definition: tools.h:34
eDiseqcActions Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, uint *Frequency) const
Parses the DiSEqC commands and returns the appropriate action code with every call.
Definition: diseqc.c:402
bool Load(const char *FileName=NULL, bool AllowComments=false, bool MustExist=false)
Definition: config.h:120
virtual void StorePosition(uint Number)
Store the current position as a satellite position with the given Number.
Definition: diseqc.c:117
virtual void Drive(ePositionerDirection Direction)
Continuously move the dish to the given Direction until Halt() is called or it hits the soft or hard ...
Definition: diseqc.c:78
T min(T a, T b)
Definition: tools.h:54
Definition: diseqc.h:62
int Pin(void) const
Definition: diseqc.h:47
virtual void Halt(void)
Stop any ongoing motion of the dish.
Definition: diseqc.c:93
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition: positioner.c:107
cScr(void)
Definition: diseqc.c:150
virtual void SetLimit(ePositionerDirection Direction)
Set the soft limit of the dish movement in the given Direction to the current position.
Definition: diseqc.c:99
#define ALL_DEVICES
Definition: diseqc.c:17
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition: diseqc.c:129
int UsePositioner
Definition: config.h:275
uint SetScrFrequency(uint SatFrequency, const cScr *Scr, uint8_t *Codes) const
Definition: diseqc.c:256
#define CHECK(s)
Definition: tools.h:50
ePositionerDirection
Definition: positioner.h:83
static bool ParseDeviceNumbers(const char *s)
Definition: diseqc.c:27
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition: diseqc.c:136
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition: thread.c:57
cSources Sources
Definition: sources.c:117
#define IsBitSet(v, b)
Definition: tools.h:67
const char * GetScrBank(const char *s) const
Definition: diseqc.c:343
bool Load(const char *FileName, bool AllowComments=false, bool MustExist=false)
Definition: diseqc.c:441
cSetup Setup
Definition: config.c:373
int Frontend(void) const
Returns the file descriptor of the DVB frontend the positioner is connected to.
Definition: positioner.h:49
cMutex mutex
Definition: positioner.h:33
void SetCapabilities(int Capabilities)
A derived class shall call this function in its constructor to set the capability flags it supports...
Definition: positioner.h:46
cDiseqcPositioner(void)
Definition: diseqc.c:51
virtual void Step(ePositionerDirection Direction, uint Steps=1)
Move the dish the given number of Steps in the given Direction.
Definition: diseqc.c:84
const char * GetCodes(const char *s, uchar *Codes=NULL, uint8_t *MaxCodes=NULL) const
Definition: diseqc.c:361
cScr * GetUnused(int Device)
Definition: diseqc.c:190
static int FromString(const char *s)
Definition: sources.c:68
Definition: diseqc.h:34
cScrs Scrs
Definition: diseqc.c:182
#define MAX_DEVICES
Definition: diseqc.c:18
char * skipspace(const char *s)
Definition: tools.h:200
eDiseqcActions
Definition: diseqc.h:64
const cDiseqc * Get(int Device, int Source, int Frequency, char Polarization, const cScr **Scr) const
Selects a DiSEqC entry suitable for the given Device and tuning parameters.
Definition: diseqc.c:447
static bool Matches(int Code1, int Code2)
Returns true if Code2 matches Code1.
Definition: sources.c:40
int SetScrPin(const cScr *Scr, uint8_t *Codes) const
Definition: diseqc.c:281
const char * GetPosition(const char *s) const
Definition: diseqc.c:321
cSource * Get(int Code)
Definition: sources.c:119
bool Load(const char *FileName, bool AllowComments=false, bool MustExist=false)
Definition: diseqc.c:184
int Channel(void) const
Definition: diseqc.h:45
static int CurrentDevices
Definition: diseqc.c:20
static bool IsDeviceNumbers(const char *s)
Definition: diseqc.c:22
void SendDiseqc(uint8_t *Codes, int NumCodes)
Definition: diseqc.c:66
cDiseqc(void)
Definition: diseqc.c:206
bool Parse(const char *s)
Definition: diseqc.c:224
virtual void RecalcPositions(uint Number)
Take the difference between the current actual position of the dish and the position stored with the ...
Definition: diseqc.c:123