vdr 2.6.8
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 4.1 2017/01/09 15:10:40 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
20static int CurrentDevices = 0;
21
22static bool IsDeviceNumbers(const char *s)
23{
24 return *s && s[strlen(s) - 1] == ':';
25}
26
27static bool ParseDeviceNumbers(const char *s)
28{
29 if (IsDeviceNumbers(s)) {
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
65
66void 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
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
129void 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
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
159bool cScr::Parse(const char *s)
160{
161 if (IsDeviceNumbers(s))
162 return ParseDeviceNumbers(s);
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
184bool cScrs::Load(const char *FileName, bool AllowComments, bool MustExist)
185{
187 return cConfig<cScr>::Load(FileName, AllowComments, MustExist);
188}
189
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
224bool cDiseqc::Parse(const char *s)
225{
226 if (IsDeviceNumbers(s))
227 return ParseDeviceNumbers(s);
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
256int cDiseqc::SetScrFrequency(int SatFrequency, const cScr *Scr, uint8_t *Codes) const
257{
258 if ((Codes[0] & 0xF0) == 0x70 ) { // EN50607 aka JESS
259 int 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 int 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
281int 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
307const 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
321const 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
343const 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
361const 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
402cDiseqc::eDiseqcActions cDiseqc::Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, int *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);
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
441bool cDiseqcs::Load(const char *FileName, bool AllowComments, bool MustExist)
442{
444 return cConfig<cDiseqc>::Load(FileName, AllowComments, MustExist);
445}
446
447const 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}
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:72
const char * FileName(void)
Definition config.h:126
bool Load(const char *FileName=NULL, bool AllowComments=false, bool MustExist=false)
Definition config.h:127
virtual void DisableLimits(void)
Disables the soft limits for the dish movement.
Definition diseqc.c:105
void SendDiseqc(uint8_t *Codes, int NumCodes)
Definition diseqc.c:66
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition diseqc.c:129
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition diseqc.c:136
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
virtual void Step(ePositionerDirection Direction, uint Steps=1)
Move the dish the given number of Steps in the given Direction.
Definition diseqc.c:84
virtual void Halt(void)
Stop any ongoing motion of the dish.
Definition diseqc.c:93
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
virtual void EnableLimits(void)
Enables the soft limits for the dish movement.
Definition diseqc.c:111
virtual void StorePosition(uint Number)
Store the current position as a satellite position with the given Number.
Definition diseqc.c:117
cDiseqcPositioner(void)
Definition diseqc.c:51
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
@ MaxDiseqcCodes
Definition diseqc.h:78
eDiseqcActions Execute(const char **CurrentAction, uchar *Codes, uint8_t *MaxCodes, const cScr *Scr, int *Frequency) const
Parses the DiSEqC commands and returns the appropriate action code with every call.
Definition diseqc.c:402
char polarization
Definition diseqc.h:83
bool Parse(const char *s)
Definition diseqc.c:224
int devices
Definition diseqc.h:80
int position
Definition diseqc.h:85
int slof
Definition diseqc.h:82
const char * GetPosition(const char *s) const
Definition diseqc.c:321
const char * Wait(const char *s) const
Definition diseqc.c:307
int SetScrFrequency(int SatFrequency, const cScr *Scr, uint8_t *Codes) const
Definition diseqc.c:256
~cDiseqc()
Definition diseqc.c:219
const char * GetCodes(const char *s, uchar *Codes=NULL, uint8_t *MaxCodes=NULL) const
Definition diseqc.c:361
int scrBank
Definition diseqc.h:86
bool parsing
Definition diseqc.h:88
char * commands
Definition diseqc.h:87
int source
Definition diseqc.h:81
int lof
Definition diseqc.h:84
int SetScrPin(const cScr *Scr, uint8_t *Codes) const
Definition diseqc.c:281
const char * GetScrBank(const char *s) const
Definition diseqc.c:343
eDiseqcActions
Definition diseqc.h:64
@ daMiniB
Definition diseqc.h:71
@ daNone
Definition diseqc.h:65
@ daPositionN
Definition diseqc.h:72
@ daMiniA
Definition diseqc.h:70
@ daCodes
Definition diseqc.h:75
@ daVoltage13
Definition diseqc.h:68
@ daWait
Definition diseqc.h:76
@ daToneOff
Definition diseqc.h:66
@ daToneOn
Definition diseqc.h:67
@ daVoltage18
Definition diseqc.h:69
@ daPositionA
Definition diseqc.h:73
@ daScr
Definition diseqc.h:74
cDiseqc(void)
Definition diseqc.c:206
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
bool Load(const char *FileName, bool AllowComments=false, bool MustExist=false)
Definition diseqc.c:441
const cScr * First(void) const
Definition tools.h:656
const cScr * Next(const cScr *Object) const
Definition tools.h:663
@ pcCanEnableLimits
Definition positioner.h:77
@ pcCanRecalcPositions
Definition positioner.h:79
@ pcCanDisableLimits
Definition positioner.h:76
@ pcCanStorePosition
Definition positioner.h:78
@ pcCanGotoPosition
Definition positioner.h:80
int Frontend(void) const
Returns the file descriptor of the DVB frontend the positioner is connected to.
Definition positioner.h:49
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
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
virtual void GotoPosition(uint Number, int Longitude)
Move the dish to the satellite position stored under the given Number.
Definition positioner.c:100
virtual void GotoAngle(int Longitude)
Move the dish to the given angular position.
Definition positioner.c:107
Definition diseqc.h:34
uint UserBand(void) const
Definition diseqc.h:46
int Channel(void) const
Definition diseqc.h:45
int devices
Definition diseqc.h:36
cScr(void)
Definition diseqc.c:150
bool used
Definition diseqc.h:40
uint userBand
Definition diseqc.h:38
bool Parse(const char *s)
Definition diseqc.c:159
int Pin(void) const
Definition diseqc.h:47
int channel
Definition diseqc.h:37
int pin
Definition diseqc.h:39
Definition diseqc.h:52
bool Load(const char *FileName, bool AllowComments=false, bool MustExist=false)
Definition diseqc.c:184
cMutex mutex
Definition diseqc.h:54
cScr * GetUnused(int Device)
Definition diseqc.c:190
int UsePositioner
Definition config.h:281
static int FromString(const char *s)
Definition sources.c:68
static bool Matches(int Code1, int Code2)
Returns true if Code2 matches Code1.
Definition sources.c:40
cSource * Get(int Code)
Definition sources.c:119
cSetup Setup
Definition config.c:372
cDiseqcs Diseqcs
Definition diseqc.c:439
static bool IsDeviceNumbers(const char *s)
Definition diseqc.c:22
static bool ParseDeviceNumbers(const char *s)
Definition diseqc.c:27
#define MAX_DEVICES
Definition diseqc.c:18
cScrs Scrs
Definition diseqc.c:182
static int CurrentDevices
Definition diseqc.c:20
#define ALL_DEVICES
Definition diseqc.c:17
cScrs Scrs
Definition diseqc.c:182
cSources Sources
Definition sources.c:117
#define IsBitSet(v, b)
Definition tools.h:77
unsigned char uchar
Definition tools.h:31
#define CHECK(s)
Definition tools.h:51
#define dsyslog(a...)
Definition tools.h:37
char * skipspace(const char *s)
Definition tools.h:244
T min(T a, T b)
Definition tools.h:63
#define esyslog(a...)
Definition tools.h:35