vdr 2.6.1
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
52{
54 pcCanStep |
55 pcCanHalt |
63 );
64}
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
Definition: diseqc.h:62
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
char * commands
Definition: diseqc.h:87
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
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
@ MaxDiseqcCodes
Definition: diseqc.h:78
eDiseqcActions
Definition: diseqc.h:64
@ daVoltage18
Definition: diseqc.h:69
@ daToneOff
Definition: diseqc.h:66
@ daNone
Definition: diseqc.h:65
@ daScr
Definition: diseqc.h:74
@ daCodes
Definition: diseqc.h:75
@ daToneOn
Definition: diseqc.h:67
@ daMiniA
Definition: diseqc.h:70
@ daWait
Definition: diseqc.h:76
@ daPositionA
Definition: diseqc.h:73
@ daMiniB
Definition: diseqc.h:71
@ daPositionN
Definition: diseqc.h:72
@ daVoltage13
Definition: diseqc.h:68
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
Returns the first element in this list, or NULL if the list is empty.
const cScr * Next(const cScr *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
int Frontend(void) const
Returns the file descriptor of the DVB frontend the positioner is connected to.
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.
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
cSources Sources
Definition: sources.c:117
#define IsBitSet(v, b)
unsigned char uchar
#define CHECK(s)
#define dsyslog(a...)
char * skipspace(const char *s)
T min(T a, T b)
#define esyslog(a...)